diff --git a/.editorconfig b/.editorconfig
index c7a381b730..25e0135725 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -137,7 +137,7 @@ space_within_single_line_array_initializer_braces = true
csharp_wrap_before_ternary_opsigns = false
# Xaml files
-[*.xaml]
+[*.{xaml,axaml}]
indent_size = 2
# Xml project files
diff --git a/.gitignore b/.gitignore
index 9b15011929..44fe5e4ba4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -192,13 +192,12 @@ dirs.sln
##################
-# XCode
+# Xcode
##################
Index/
Logs/
ModuleCache.noindex/
Build/Intermediates.noindex/
-info.plist
build-intermediate
obj-Direct2D1/
obj-Skia/
diff --git a/.ncrunch/Avalonia.IntegrationTests.Appium.v3.ncrunchproject b/.ncrunch/Avalonia.IntegrationTests.Appium.v3.ncrunchproject
new file mode 100644
index 0000000000..319cd523ce
--- /dev/null
+++ b/.ncrunch/Avalonia.IntegrationTests.Appium.v3.ncrunchproject
@@ -0,0 +1,5 @@
+
+
+ True
+
+
\ No newline at end of file
diff --git a/.ncrunch/Avalonia.IntegrationTests.Win32.v3.ncrunchproject b/.ncrunch/Avalonia.IntegrationTests.Win32.v3.ncrunchproject
new file mode 100644
index 0000000000..319cd523ce
--- /dev/null
+++ b/.ncrunch/Avalonia.IntegrationTests.Win32.v3.ncrunchproject
@@ -0,0 +1,5 @@
+
+
+ True
+
+
\ No newline at end of file
diff --git a/.ncrunch/IntegrationTestApp.v3.ncrunchproject b/.ncrunch/IntegrationTestApp.v3.ncrunchproject
new file mode 100644
index 0000000000..319cd523ce
--- /dev/null
+++ b/.ncrunch/IntegrationTestApp.v3.ncrunchproject
@@ -0,0 +1,5 @@
+
+
+ True
+
+
\ No newline at end of file
diff --git a/Avalonia.sln b/Avalonia.sln
index f1bdfdbbc3..c8e513f94c 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}"
@@ -95,8 +72,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog", "samples\C
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.Desktop", "samples\ControlCatalog.Desktop\ControlCatalog.Desktop.csproj", "{2B888490-D14A-4BCA-AB4B-48676FA93C9B}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.iOS", "samples\ControlCatalog.iOS\ControlCatalog.iOS.csproj", "{57E0455D-D565-44BB-B069-EE1AA20F8337}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.DesignerSupport.Tests", "tests\Avalonia.DesignerSupport.Tests\Avalonia.DesignerSupport.Tests.csproj", "{52F55355-D120-42AC-8116-8410A7D602FA}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.DesignerSupport.TestApp", "tests\Avalonia.DesignerSupport.TestApp\Avalonia.DesignerSupport.TestApp.csproj", "{F1381F98-4D24-409A-A6C5-1C5B1E08BB08}"
@@ -119,12 +94,11 @@ 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\iOSWorkarounds.props = build\iOSWorkarounds.props
build\JetBrains.Annotations.props = build\JetBrains.Annotations.props
build\JetBrains.dotMemoryUnit.props = build\JetBrains.dotMemoryUnit.props
- build\Magick.NET-Q16-AnyCPU.props = build\Magick.NET-Q16-AnyCPU.props
build\Microsoft.CSharp.props = build\Microsoft.CSharp.props
build\Microsoft.Reactive.Testing.props = build\Microsoft.Reactive.Testing.props
build\Moq.props = build\Moq.props
@@ -137,11 +111,13 @@ 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
build\UnitTests.NetFX.props = build\UnitTests.NetFX.props
build\XUnit.props = build\XUnit.props
+ build\ImageSharp.props = build\ImageSharp.props
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Targets", "Targets", "{4D6FAF79-58B4-482F-9122-0668C346364C}"
@@ -177,8 +153,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.OpenGL", "src\Aval
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Native", "src\Avalonia.Native\Avalonia.Native.csproj", "{12A91A62-C064-42CA-9A8C-A1272F354388}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.DesktopRuntime", "src\Avalonia.DesktopRuntime\Avalonia.DesktopRuntime.csproj", "{878FEFE0-CD14-41CB-90B0-DBCB163E8F15}"
-EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Packages", "Packages", "{E870DCD7-F46A-498D-83FC-D0FD13E0A11C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia", "packages\Avalonia\Avalonia.csproj", "{D49233F8-F29C-47DD-9975-C4C9E4502720}"
@@ -189,14 +163,14 @@ 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}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.ReactiveUI.UnitTests", "tests\Avalonia.ReactiveUI.UnitTests\Avalonia.ReactiveUI.UnitTests.csproj", "{AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls.ColorPicker", "src\Avalonia.Controls.ColorPicker\Avalonia.Controls.ColorPicker.csproj", "{1ECC012A-8837-4AE2-9BDA-3E2857898727}"
+EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls.DataGrid", "src\Avalonia.Controls.DataGrid\Avalonia.Controls.DataGrid.csproj", "{3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Dialogs", "src\Avalonia.Dialogs\Avalonia.Dialogs.csproj", "{4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}"
@@ -221,6 +195,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.MicroCom", "src\Av
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MiniMvvm", "samples\MiniMvvm\MiniMvvm.csproj", "{BC594FD5-4AF2-409E-A1E6-04123F54D7C5}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IntegrationTestApp", "samples\IntegrationTestApp\IntegrationTestApp.csproj", "{676D6BFD-029D-4E43-BFC7-3892265CE251}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.IntegrationTests.Appium", "tests\Avalonia.IntegrationTests.Appium\Avalonia.IntegrationTests.Appium.csproj", "{F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}"
+EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Web", "Web", "{86A3F706-DC3C-43C6-BE1B-B98F5BAAA268}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Web.Blazor", "src\Web\Avalonia.Web.Blazor\Avalonia.Web.Blazor.csproj", "{25831348-EB2A-483E-9576-E8F6528674A5}"
@@ -233,6 +211,14 @@ 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("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.iOS", "samples\ControlCatalog.iOS\ControlCatalog.iOS.csproj", "{70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.PlatformSupport.UnitTests", "tests\Avalonia.PlatformSupport.UnitTests\Avalonia.PlatformSupport.UnitTests.csproj", "{CE910927-CE5A-456F-BC92-E4C757354A5C}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.SourceGenerator", "src\Avalonia.SourceGenerator\Avalonia.SourceGenerator.csproj", "{CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevAnalyzers", "src\tools\DevAnalyzers\DevAnalyzers.csproj", "{2B390431-288C-435C-BB6B-A374033BD8D1}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Ad-Hoc|Any CPU = Ad-Hoc|Any CPU
@@ -273,54 +259,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
@@ -369,54 +307,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
@@ -441,30 +331,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
@@ -513,54 +379,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
@@ -585,30 +403,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
@@ -633,54 +427,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
@@ -705,30 +451,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
@@ -911,22 +633,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
@@ -1095,26 +801,6 @@ Global
{2B888490-D14A-4BCA-AB4B-48676FA93C9B}.Release|iPhone.Build.0 = Release|Any CPU
{2B888490-D14A-4BCA-AB4B-48676FA93C9B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{2B888490-D14A-4BCA-AB4B-48676FA93C9B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
- {57E0455D-D565-44BB-B069-EE1AA20F8337}.Ad-Hoc|Any CPU.ActiveCfg = Ad-Hoc|iPhone
- {57E0455D-D565-44BB-B069-EE1AA20F8337}.Ad-Hoc|iPhone.ActiveCfg = Ad-Hoc|iPhone
- {57E0455D-D565-44BB-B069-EE1AA20F8337}.Ad-Hoc|iPhone.Build.0 = Ad-Hoc|iPhone
- {57E0455D-D565-44BB-B069-EE1AA20F8337}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Ad-Hoc|iPhoneSimulator
- {57E0455D-D565-44BB-B069-EE1AA20F8337}.Ad-Hoc|iPhoneSimulator.Build.0 = Ad-Hoc|iPhoneSimulator
- {57E0455D-D565-44BB-B069-EE1AA20F8337}.AppStore|Any CPU.ActiveCfg = AppStore|iPhone
- {57E0455D-D565-44BB-B069-EE1AA20F8337}.AppStore|iPhone.ActiveCfg = AppStore|iPhone
- {57E0455D-D565-44BB-B069-EE1AA20F8337}.AppStore|iPhone.Build.0 = AppStore|iPhone
- {57E0455D-D565-44BB-B069-EE1AA20F8337}.AppStore|iPhoneSimulator.ActiveCfg = AppStore|iPhoneSimulator
- {57E0455D-D565-44BB-B069-EE1AA20F8337}.AppStore|iPhoneSimulator.Build.0 = AppStore|iPhoneSimulator
- {57E0455D-D565-44BB-B069-EE1AA20F8337}.Debug|Any CPU.ActiveCfg = Debug|iPhone
- {57E0455D-D565-44BB-B069-EE1AA20F8337}.Debug|iPhone.ActiveCfg = Debug|iPhone
- {57E0455D-D565-44BB-B069-EE1AA20F8337}.Debug|iPhone.Build.0 = Debug|iPhone
- {57E0455D-D565-44BB-B069-EE1AA20F8337}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
- {57E0455D-D565-44BB-B069-EE1AA20F8337}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
- {57E0455D-D565-44BB-B069-EE1AA20F8337}.Release|Any CPU.ActiveCfg = Release|iPhone
- {57E0455D-D565-44BB-B069-EE1AA20F8337}.Release|iPhone.ActiveCfg = Release|iPhone
- {57E0455D-D565-44BB-B069-EE1AA20F8337}.Release|iPhone.Build.0 = Release|iPhone
- {57E0455D-D565-44BB-B069-EE1AA20F8337}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
- {57E0455D-D565-44BB-B069-EE1AA20F8337}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
{52F55355-D120-42AC-8116-8410A7D602FA}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
{52F55355-D120-42AC-8116-8410A7D602FA}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
{52F55355-D120-42AC-8116-8410A7D602FA}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
@@ -1559,30 +1245,6 @@ Global
{12A91A62-C064-42CA-9A8C-A1272F354388}.Release|iPhone.Build.0 = Release|Any CPU
{12A91A62-C064-42CA-9A8C-A1272F354388}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{12A91A62-C064-42CA-9A8C-A1272F354388}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.AppStore|Any CPU.Build.0 = Debug|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.AppStore|iPhone.Build.0 = Debug|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Debug|iPhone.ActiveCfg = Debug|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Debug|iPhone.Build.0 = Debug|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Release|Any CPU.Build.0 = Release|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Release|iPhone.ActiveCfg = Release|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Release|iPhone.Build.0 = Release|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{D49233F8-F29C-47DD-9975-C4C9E4502720}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{D49233F8-F29C-47DD-9975-C4C9E4502720}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{D49233F8-F29C-47DD-9975-C4C9E4502720}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
@@ -1679,30 +1341,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
@@ -2063,6 +1701,54 @@ Global
{BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Release|iPhone.Build.0 = Release|Any CPU
{BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.AppStore|Any CPU.Build.0 = Debug|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.AppStore|iPhone.Build.0 = Debug|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.Debug|iPhone.Build.0 = Debug|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.Release|Any CPU.Build.0 = Release|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.Release|iPhone.ActiveCfg = Release|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.Release|iPhone.Build.0 = Release|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.AppStore|Any CPU.Build.0 = Debug|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.AppStore|iPhone.Build.0 = Debug|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Debug|iPhone.Build.0 = Debug|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Release|iPhone.ActiveCfg = Release|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Release|iPhone.Build.0 = Release|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{25831348-EB2A-483E-9576-E8F6528674A5}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{25831348-EB2A-483E-9576-E8F6528674A5}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{25831348-EB2A-483E-9576-E8F6528674A5}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
@@ -2183,6 +1869,126 @@ Global
{E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Release|iPhone.Build.0 = Release|Any CPU
{E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.AppStore|Any CPU.Build.0 = Debug|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.AppStore|iPhone.Build.0 = Debug|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Debug|iPhone.Build.0 = Debug|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Release|iPhone.ActiveCfg = Release|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Release|iPhone.Build.0 = Release|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+ {CE910927-CE5A-456F-BC92-E4C757354A5C}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
+ {CE910927-CE5A-456F-BC92-E4C757354A5C}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
+ {CE910927-CE5A-456F-BC92-E4C757354A5C}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
+ {CE910927-CE5A-456F-BC92-E4C757354A5C}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
+ {CE910927-CE5A-456F-BC92-E4C757354A5C}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {CE910927-CE5A-456F-BC92-E4C757354A5C}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {CE910927-CE5A-456F-BC92-E4C757354A5C}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
+ {CE910927-CE5A-456F-BC92-E4C757354A5C}.AppStore|Any CPU.Build.0 = Debug|Any CPU
+ {CE910927-CE5A-456F-BC92-E4C757354A5C}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
+ {CE910927-CE5A-456F-BC92-E4C757354A5C}.AppStore|iPhone.Build.0 = Debug|Any CPU
+ {CE910927-CE5A-456F-BC92-E4C757354A5C}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {CE910927-CE5A-456F-BC92-E4C757354A5C}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {CE910927-CE5A-456F-BC92-E4C757354A5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CE910927-CE5A-456F-BC92-E4C757354A5C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CE910927-CE5A-456F-BC92-E4C757354A5C}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {CE910927-CE5A-456F-BC92-E4C757354A5C}.Debug|iPhone.Build.0 = Debug|Any CPU
+ {CE910927-CE5A-456F-BC92-E4C757354A5C}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {CE910927-CE5A-456F-BC92-E4C757354A5C}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {CE910927-CE5A-456F-BC92-E4C757354A5C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CE910927-CE5A-456F-BC92-E4C757354A5C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CE910927-CE5A-456F-BC92-E4C757354A5C}.Release|iPhone.ActiveCfg = Release|Any CPU
+ {CE910927-CE5A-456F-BC92-E4C757354A5C}.Release|iPhone.Build.0 = Release|Any CPU
+ {CE910927-CE5A-456F-BC92-E4C757354A5C}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {CE910927-CE5A-456F-BC92-E4C757354A5C}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+ {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
+ {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
+ {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
+ {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
+ {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
+ {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.AppStore|Any CPU.Build.0 = Debug|Any CPU
+ {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
+ {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.AppStore|iPhone.Build.0 = Debug|Any CPU
+ {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Debug|iPhone.Build.0 = Debug|Any CPU
+ {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Release|iPhone.ActiveCfg = Release|Any CPU
+ {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Release|iPhone.Build.0 = Release|Any CPU
+ {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.AppStore|Any CPU.Build.0 = Debug|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.AppStore|iPhone.Build.0 = Debug|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.Debug|iPhone.Build.0 = Debug|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.Release|iPhone.ActiveCfg = Release|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.Release|iPhone.Build.0 = Release|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|Any CPU.Build.0 = Debug|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|iPhone.Build.0 = Debug|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|iPhone.Build.0 = Debug|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|iPhone.ActiveCfg = Release|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|iPhone.Build.0 = Release|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -2190,14 +1996,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}
@@ -2205,14 +2006,12 @@ 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}
{410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{D0A739B9-3C68-4BA6-A328-41606954B6BD} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{2B888490-D14A-4BCA-AB4B-48676FA93C9B} = {9B9E3891-2366-4253-A952-D08BCEB71098}
- {57E0455D-D565-44BB-B069-EE1AA20F8337} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{52F55355-D120-42AC-8116-8410A7D602FA} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{F1381F98-4D24-409A-A6C5-1C5B1E08BB08} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{FBCAF3D0-2808-4934-8E96-3F607594517B} = {9B9E3891-2366-4253-A952-D08BCEB71098}
@@ -2221,6 +2020,7 @@ Global
{29132311-1848-4FD6-AE0C-4FF841151BD3} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{7D2D3083-71DD-4CC9-8907-39A0D86FB322} = {3743B0F2-CC41-4F14-A8C8-267F579BF91E}
{39D7B147-1A5B-47C2-9D01-21FB7C47C4B3} = {9B9E3891-2366-4253-A952-D08BCEB71098}
+ {86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B} = {A689DEF5-D50F-4975-8B72-124C9EB54066}
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9} = {86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B}
{638580B0-7910-40EF-B674-DCB34DA308CD} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9}
{CBC4FF2F-92D4-420B-BE21-9FE0B930B04E} = {B39A8919-9F95-48FE-AD7B-76E08B509888}
@@ -2231,7 +2031,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}
@@ -2240,10 +2039,15 @@ Global
{909A8CBD-7D0E-42FD-B841-022AD8925820} = {8B6A8209-894F-4BA1-B880-965FD453982C}
{11BE52AF-E2DD-4CF0-B19A-05285ACAF571} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{BC594FD5-4AF2-409E-A1E6-04123F54D7C5} = {9B9E3891-2366-4253-A952-D08BCEB71098}
+ {676D6BFD-029D-4E43-BFC7-3892265CE251} = {9B9E3891-2366-4253-A952-D08BCEB71098}
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{25831348-EB2A-483E-9576-E8F6528674A5} = {86A3F706-DC3C-43C6-BE1B-B98F5BAAA268}
{C08E9894-AA92-426E-BF56-033E262CAD3E} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{26A98DA1-D89D-4A95-8152-349F404DA2E2} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9}
{A0D0A6A4-5C72-4ADA-9B27-621C7D94F270} = {9B9E3891-2366-4253-A952-D08BCEB71098}
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B} = {9B9E3891-2366-4253-A952-D08BCEB71098}
+ {CE910927-CE5A-456F-BC92-E4C757354A5C} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
+ {2B390431-288C-435C-BB6B-A374033BD8D1} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A}
diff --git a/Directory.Build.props b/Directory.Build.props
index 835decc672..42daa2df7f 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -1,9 +1,11 @@
+
$(MSBuildThisFileDirectory)build-intermediate/nuget
$(MSBuildThisFileDirectory)\src\tools\Avalonia.Designer.HostApp\bin\$(Configuration)\netcoreapp2.0\Avalonia.Designer.HostApp.dll
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 ede5aec0e8..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.100
+ 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.100
+ 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.100
+ version: 6.0.202
- task: CmdLine@2
displayName: 'Install Workloads'
inputs:
script: |
- dotnet workload install android
+ dotnet workload install android ios
- task: CmdLine@2
displayName: 'Install Nuke'
diff --git a/build/ApiCompatAttributeExcludeList.txt b/build/ApiCompatAttributeExcludeList.txt
new file mode 100644
index 0000000000..1df5a30ec3
--- /dev/null
+++ b/build/ApiCompatAttributeExcludeList.txt
@@ -0,0 +1,2 @@
+T:Avalonia.Metadata.NotClientImplementableAttribute
+T:Avalonia.Metadata.UnstableAttribute
diff --git a/build/AvaloniaPublicKey.props b/build/AvaloniaPublicKey.props
new file mode 100644
index 0000000000..89215635c0
--- /dev/null
+++ b/build/AvaloniaPublicKey.props
@@ -0,0 +1,5 @@
+
+
+ 0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87
+
+
diff --git a/build/CoreLibraries.props b/build/CoreLibraries.props
index 3fccad2641..9448a31d73 100644
--- a/build/CoreLibraries.props
+++ b/build/CoreLibraries.props
@@ -1,22 +1,13 @@
-
-
-
-
-
-
-
-
-
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 1d84d5289a..85e7a1f34d 100644
--- a/build/HarfBuzzSharp.props
+++ b/build/HarfBuzzSharp.props
@@ -1,7 +1,7 @@
-
-
-
+
+
+
diff --git a/build/ImageSharp.props b/build/ImageSharp.props
new file mode 100644
index 0000000000..178c274ac9
--- /dev/null
+++ b/build/ImageSharp.props
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/build/JetBrains.dotMemoryUnit.props b/build/JetBrains.dotMemoryUnit.props
index eb4e2b6f15..5d74d474cf 100644
--- a/build/JetBrains.dotMemoryUnit.props
+++ b/build/JetBrains.dotMemoryUnit.props
@@ -1,5 +1,5 @@
-
+
diff --git a/build/Magick.NET-Q16-AnyCPU.props b/build/Magick.NET-Q16-AnyCPU.props
deleted file mode 100644
index 21d9cdcb1f..0000000000
--- a/build/Magick.NET-Q16-AnyCPU.props
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/build/NetFX.props b/build/NetFX.props
index 8ffc9ec561..14adb54035 100644
--- a/build/NetFX.props
+++ b/build/NetFX.props
@@ -1,7 +1,6 @@
-
diff --git a/build/SharedVersion.props b/build/SharedVersion.props
index 7f24ef35bc..3d9548ab9d 100644
--- a/build/SharedVersion.props
+++ b/build/SharedVersion.props
@@ -11,7 +11,7 @@
latest
MIT
Icon.png
- Avalonia is a WPF/UWP-inspired cross-platform XAML-based UI framework providing a flexible styling system and supporting a wide range of Operating Systems such as Windows (.NET Framework, .NET Core), Linux (via Xorg), MacOS and with experimental support for Android and iOS.
+ Avalonia is a cross-platform UI framework for .NET providing a flexible styling system and supporting a wide range of Operating Systems such as Windows, Linux, macOS and with experimental support for Android, iOS and WebAssembly.
avalonia;avaloniaui;mvvm;rx;reactive extensions;android;ios;mac;forms;wpf;net;netstandard;net461;uwp;xamarin
https://github.com/AvaloniaUI/Avalonia/releases
git
diff --git a/build/SkiaSharp.props b/build/SkiaSharp.props
index bb370256f9..1ee4aa56a2 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/build/SourceLink.props b/build/SourceLink.props
index 9f05848881..dd7ecc8d2a 100644
--- a/build/SourceLink.props
+++ b/build/SourceLink.props
@@ -19,7 +19,7 @@
-
+
diff --git a/build/XUnit.props b/build/XUnit.props
index a75e1bac86..17ead91aa3 100644
--- a/build/XUnit.props
+++ b/build/XUnit.props
@@ -1,14 +1,14 @@
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/dirs.proj b/dirs.proj
index 74d7b482cc..396e0c915c 100644
--- a/dirs.proj
+++ b/dirs.proj
@@ -10,21 +10,20 @@
+
+
-
-
-
-
-
+
+
diff --git a/global.json b/global.json
index 1f93ed27c3..a6792b05c7 100644
--- a/global.json
+++ b/global.json
@@ -1,10 +1,10 @@
{
"sdk": {
- "version": "6.0.100"
+ "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/inc/rendertarget.h b/native/Avalonia.Native/inc/rendertarget.h
index 2b0338d099..a59915777f 100644
--- a/native/Avalonia.Native/inc/rendertarget.h
+++ b/native/Avalonia.Native/inc/rendertarget.h
@@ -1,3 +1,8 @@
+#pragma once
+
+#include "com.h"
+#include "comimpl.h"
+#include "avalonia-native.h"
@protocol IRenderTarget
-(void) setNewLayer: (CALayer*) layer;
diff --git a/native/Avalonia.Native/src/OSX/AutoFitContentView.h b/native/Avalonia.Native/src/OSX/AutoFitContentView.h
new file mode 100644
index 0000000000..7f1f764141
--- /dev/null
+++ b/native/Avalonia.Native/src/OSX/AutoFitContentView.h
@@ -0,0 +1,17 @@
+//
+// Created by Dan Walmsley on 05/05/2022.
+// Copyright (c) 2022 Avalonia. All rights reserved.
+//
+
+#pragma once
+
+#import
+#include "avalonia-native.h"
+
+@interface AutoFitContentView : NSView
+-(AutoFitContentView* _Nonnull) initWithContent: (NSView* _Nonnull) content;
+-(void) ShowTitleBar: (bool) show;
+-(void) SetTitleBarHeightHint: (double) height;
+
+-(void) ShowBlur: (bool) show;
+@end
\ No newline at end of file
diff --git a/native/Avalonia.Native/src/OSX/AutoFitContentView.mm b/native/Avalonia.Native/src/OSX/AutoFitContentView.mm
new file mode 100644
index 0000000000..0fa4540726
--- /dev/null
+++ b/native/Avalonia.Native/src/OSX/AutoFitContentView.mm
@@ -0,0 +1,106 @@
+//
+// Created by Dan Walmsley on 05/05/2022.
+// Copyright (c) 2022 Avalonia. All rights reserved.
+//
+
+#include "AvnView.h"
+#include "AutoFitContentView.h"
+#include "WindowInterfaces.h"
+#include "WindowProtocol.h"
+
+@implementation AutoFitContentView
+{
+ NSVisualEffectView* _titleBarMaterial;
+ NSBox* _titleBarUnderline;
+ NSView* _content;
+ NSVisualEffectView* _blurBehind;
+ double _titleBarHeightHint;
+ bool _settingSize;
+}
+
+-(AutoFitContentView* _Nonnull) initWithContent:(NSView *)content
+{
+ _titleBarHeightHint = -1;
+ _content = content;
+ _settingSize = false;
+
+ [self setAutoresizesSubviews:true];
+ [self setWantsLayer:true];
+
+ _titleBarMaterial = [NSVisualEffectView new];
+ [_titleBarMaterial setBlendingMode:NSVisualEffectBlendingModeWithinWindow];
+ [_titleBarMaterial setMaterial:NSVisualEffectMaterialTitlebar];
+ [_titleBarMaterial setWantsLayer:true];
+ _titleBarMaterial.hidden = true;
+
+ _titleBarUnderline = [NSBox new];
+ _titleBarUnderline.boxType = NSBoxSeparator;
+ _titleBarUnderline.fillColor = [NSColor underPageBackgroundColor];
+ _titleBarUnderline.hidden = true;
+
+ [self addSubview:_titleBarMaterial];
+ [self addSubview:_titleBarUnderline];
+
+ _blurBehind = [NSVisualEffectView new];
+ [_blurBehind setBlendingMode:NSVisualEffectBlendingModeBehindWindow];
+ [_blurBehind setMaterial:NSVisualEffectMaterialLight];
+ [_blurBehind setWantsLayer:true];
+ _blurBehind.hidden = true;
+
+ [_blurBehind setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
+ [_content setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
+
+ [self addSubview:_blurBehind];
+ [self addSubview:_content];
+
+ [self setWantsLayer:true];
+ return self;
+}
+
+-(void) ShowBlur:(bool)show
+{
+ _blurBehind.hidden = !show;
+}
+
+-(void) ShowTitleBar: (bool) show
+{
+ _titleBarMaterial.hidden = !show;
+ _titleBarUnderline.hidden = !show;
+}
+
+-(void) SetTitleBarHeightHint: (double) height
+{
+ _titleBarHeightHint = height;
+
+ [self setFrameSize:self.frame.size];
+}
+
+-(void)setFrameSize:(NSSize)newSize
+{
+ if(_settingSize)
+ {
+ return;
+ }
+
+ _settingSize = true;
+ [super setFrameSize:newSize];
+
+ auto window = (id ) [self window];
+
+ // TODO get actual titlebar size
+
+ double height = _titleBarHeightHint == -1 ? [window getExtendedTitleBarHeight] : _titleBarHeightHint;
+
+ NSRect tbar;
+ tbar.origin.x = 0;
+ tbar.origin.y = newSize.height - height;
+ tbar.size.width = newSize.width;
+ tbar.size.height = height;
+
+ [_titleBarMaterial setFrame:tbar];
+ tbar.size.height = height < 1 ? 0 : 1;
+ [_titleBarUnderline setFrame:tbar];
+
+ _settingSize = false;
+}
+@end
\ No newline at end of file
diff --git a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj
index 85fcf20034..6fc3977d4e 100644
--- a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj
+++ b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj
@@ -7,6 +7,24 @@
objects = {
/* Begin PBXBuildFile section */
+ 18391068E48EF96E3DB5FDAB /* ResizeScope.mm in Sources */ = {isa = PBXBuildFile; fileRef = 18391E45702740FE9DD69695 /* ResizeScope.mm */; };
+ 1839125F057B0A4EB1760058 /* WindowImpl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 183919BF108EB72A029F7671 /* WindowImpl.mm */; };
+ 183914E50CF6D2EFC1667F7C /* WindowInterfaces.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391DB45C7D892E61BF388C /* WindowInterfaces.h */; };
+ 1839151F32D1BB1AB51A7BB6 /* AvnPanelWindow.mm in Sources */ = {isa = PBXBuildFile; fileRef = 18391884C7476DA4E53A492D /* AvnPanelWindow.mm */; };
+ 183916173528EC2737DBE5E1 /* WindowBaseImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 183915BFF0E234CD3604A7CD /* WindowBaseImpl.h */; };
+ 1839171DCC651B0638603AC4 /* INSWindowHolder.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391BBB7782C296D424071F /* INSWindowHolder.h */; };
+ 1839179A55FC1421BEE83330 /* WindowBaseImpl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 18391676ECF0E983F4964357 /* WindowBaseImpl.mm */; };
+ 183919D91DB9AAB5D700C2EA /* WindowImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391CD090AA776E7E841AC9 /* WindowImpl.h */; };
+ 18391AA7E0BBA74D184C5734 /* AutoFitContentView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1839166350F32661F3ABD70F /* AutoFitContentView.mm */; };
+ 18391AC16726CBC45856233B /* AvnWindow.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1839155B28B20FFB672D29C6 /* AvnWindow.mm */; };
+ 18391AC65ADD7DDD33FBE737 /* PopupImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 183910513F396141938832B5 /* PopupImpl.h */; };
+ 18391C28BF1823B5464FDD36 /* ResizeScope.h in Headers */ = {isa = PBXBuildFile; fileRef = 1839171D898F9BFC1373631A /* ResizeScope.h */; };
+ 18391CF07316F819E76B617C /* IWindowStateChanged.h in Headers */ = {isa = PBXBuildFile; fileRef = 183913C6BFD6856BD42D19FD /* IWindowStateChanged.h */; };
+ 18391D4EB311BC7EF8B8C0A6 /* AvnView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1839132D0E2454D911F1D1F9 /* AvnView.mm */; };
+ 18391D8CD1756DC858DC1A09 /* PopupImpl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 18391BB698579F40F1783F31 /* PopupImpl.mm */; };
+ 18391E1381E2D5BFD60265A9 /* AutoFitContentView.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391654EF0E7AB3D3AB4071 /* AutoFitContentView.h */; };
+ 18391ED5F611FF62C45F196D /* AvnView.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391D1669284AD2EC9E866A /* AvnView.h */; };
+ 18391F1E2411C79405A9943A /* WindowProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 1839122E037567BDD1D09DEB /* WindowProtocol.h */; };
1A002B9E232135EE00021753 /* app.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A002B9D232135EE00021753 /* app.mm */; };
1A1852DC23E05814008F0DED /* deadlock.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A1852DB23E05814008F0DED /* deadlock.mm */; };
1A3E5EA823E9E83B00EDE661 /* rendertarget.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A3E5EA723E9E83B00EDE661 /* rendertarget.mm */; };
@@ -28,11 +46,30 @@
AB00E4F72147CA920032A60A /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB00E4F62147CA920032A60A /* main.mm */; };
AB1E522C217613570091CD71 /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB1E522B217613570091CD71 /* OpenGL.framework */; };
AB661C1E2148230F00291242 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB661C1D2148230F00291242 /* AppKit.framework */; };
- AB661C202148286E00291242 /* window.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB661C1F2148286E00291242 /* window.mm */; };
AB8F7D6B21482D7F0057DBA5 /* platformthreading.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB8F7D6A21482D7F0057DBA5 /* platformthreading.mm */; };
+ BC11A5BE2608D58F0017BAD0 /* automation.h in Headers */ = {isa = PBXBuildFile; fileRef = BC11A5BC2608D58F0017BAD0 /* automation.h */; };
+ BC11A5BF2608D58F0017BAD0 /* automation.mm in Sources */ = {isa = PBXBuildFile; fileRef = BC11A5BD2608D58F0017BAD0 /* automation.mm */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
+ 183910513F396141938832B5 /* PopupImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PopupImpl.h; sourceTree = ""; };
+ 1839122E037567BDD1D09DEB /* WindowProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowProtocol.h; sourceTree = ""; };
+ 1839132D0E2454D911F1D1F9 /* AvnView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AvnView.mm; sourceTree = ""; };
+ 183913C6BFD6856BD42D19FD /* IWindowStateChanged.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IWindowStateChanged.h; sourceTree = ""; };
+ 1839155B28B20FFB672D29C6 /* AvnWindow.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AvnWindow.mm; sourceTree = ""; };
+ 183915BFF0E234CD3604A7CD /* WindowBaseImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowBaseImpl.h; sourceTree = ""; };
+ 18391654EF0E7AB3D3AB4071 /* AutoFitContentView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AutoFitContentView.h; sourceTree = ""; };
+ 1839166350F32661F3ABD70F /* AutoFitContentView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AutoFitContentView.mm; sourceTree = ""; };
+ 18391676ECF0E983F4964357 /* WindowBaseImpl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WindowBaseImpl.mm; sourceTree = ""; };
+ 1839171D898F9BFC1373631A /* ResizeScope.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ResizeScope.h; sourceTree = ""; };
+ 18391884C7476DA4E53A492D /* AvnPanelWindow.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AvnPanelWindow.mm; sourceTree = ""; };
+ 183919BF108EB72A029F7671 /* WindowImpl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WindowImpl.mm; sourceTree = ""; };
+ 18391BB698579F40F1783F31 /* PopupImpl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PopupImpl.mm; sourceTree = ""; };
+ 18391BBB7782C296D424071F /* INSWindowHolder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = INSWindowHolder.h; sourceTree = ""; };
+ 18391CD090AA776E7E841AC9 /* WindowImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowImpl.h; sourceTree = ""; };
+ 18391D1669284AD2EC9E866A /* AvnView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AvnView.h; sourceTree = ""; };
+ 18391DB45C7D892E61BF388C /* WindowInterfaces.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowInterfaces.h; sourceTree = ""; };
+ 18391E45702740FE9DD69695 /* ResizeScope.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ResizeScope.mm; sourceTree = ""; };
1A002B9D232135EE00021753 /* app.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = app.mm; sourceTree = ""; };
1A1852DB23E05814008F0DED /* deadlock.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = deadlock.mm; sourceTree = ""; };
1A3E5EA723E9E83B00EDE661 /* rendertarget.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = rendertarget.mm; sourceTree = ""; };
@@ -46,7 +83,6 @@
37A4E71A2178846A00EACBCD /* headers */ = {isa = PBXFileReference; lastKnownFileType = folder; name = headers; path = ../../inc; sourceTree = ""; };
37A517B22159597E00FBA241 /* Screens.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = Screens.mm; sourceTree = ""; };
37C09D8721580FE4006A6758 /* SystemDialogs.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SystemDialogs.mm; sourceTree = ""; };
- 37C09D8A21581EF2006A6758 /* window.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = window.h; sourceTree = ""; };
37DDA9AF219330F8002E132B /* AvnString.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = AvnString.mm; sourceTree = ""; };
37DDA9B121933371002E132B /* AvnString.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AvnString.h; sourceTree = ""; };
37E2330E21583241000CB7E2 /* KeyTransform.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = KeyTransform.mm; sourceTree = ""; };
@@ -60,10 +96,11 @@
AB00E4F62147CA920032A60A /* main.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = main.mm; sourceTree = ""; };
AB1E522B217613570091CD71 /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = System/Library/Frameworks/OpenGL.framework; sourceTree = SDKROOT; };
AB661C1D2148230F00291242 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
- AB661C1F2148286E00291242 /* window.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = window.mm; sourceTree = ""; };
AB661C212148288600291242 /* common.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = common.h; sourceTree = ""; };
AB7A61EF2147C815003C5833 /* libAvalonia.Native.OSX.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libAvalonia.Native.OSX.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
AB8F7D6A21482D7F0057DBA5 /* platformthreading.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = platformthreading.mm; sourceTree = ""; };
+ BC11A5BC2608D58F0017BAD0 /* automation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = automation.h; sourceTree = ""; };
+ BC11A5BD2608D58F0017BAD0 /* automation.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = automation.mm; sourceTree = ""; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -97,6 +134,8 @@
AB7A61E62147C814003C5833 = {
isa = PBXGroup;
children = (
+ BC11A5BC2608D58F0017BAD0 /* automation.h */,
+ BC11A5BD2608D58F0017BAD0 /* automation.mm */,
1A1852DB23E05814008F0DED /* deadlock.mm */,
1A002B9D232135EE00021753 /* app.mm */,
37DDA9B121933371002E132B /* AvnString.h */,
@@ -112,8 +151,6 @@
AB661C212148288600291242 /* common.h */,
379860FE214DA0C000CD0246 /* KeyTransform.h */,
37E2330E21583241000CB7E2 /* KeyTransform.mm */,
- AB661C1F2148286E00291242 /* window.mm */,
- 37C09D8A21581EF2006A6758 /* window.h */,
AB00E4F62147CA920032A60A /* main.mm */,
37155CE3233C00EB0034DCE9 /* menu.h */,
520624B222973F4100C4DCEF /* menu.mm */,
@@ -124,6 +161,24 @@
37C09D8721580FE4006A6758 /* SystemDialogs.mm */,
AB7A61F02147C815003C5833 /* Products */,
AB661C1C2148230E00291242 /* Frameworks */,
+ 18391676ECF0E983F4964357 /* WindowBaseImpl.mm */,
+ 183915BFF0E234CD3604A7CD /* WindowBaseImpl.h */,
+ 18391BBB7782C296D424071F /* INSWindowHolder.h */,
+ 183919BF108EB72A029F7671 /* WindowImpl.mm */,
+ 18391CD090AA776E7E841AC9 /* WindowImpl.h */,
+ 183913C6BFD6856BD42D19FD /* IWindowStateChanged.h */,
+ 18391E45702740FE9DD69695 /* ResizeScope.mm */,
+ 1839171D898F9BFC1373631A /* ResizeScope.h */,
+ 1839132D0E2454D911F1D1F9 /* AvnView.mm */,
+ 18391D1669284AD2EC9E866A /* AvnView.h */,
+ 1839166350F32661F3ABD70F /* AutoFitContentView.mm */,
+ 18391654EF0E7AB3D3AB4071 /* AutoFitContentView.h */,
+ 18391884C7476DA4E53A492D /* AvnPanelWindow.mm */,
+ 1839122E037567BDD1D09DEB /* WindowProtocol.h */,
+ 1839155B28B20FFB672D29C6 /* AvnWindow.mm */,
+ 18391DB45C7D892E61BF388C /* WindowInterfaces.h */,
+ 18391BB698579F40F1783F31 /* PopupImpl.mm */,
+ 183910513F396141938832B5 /* PopupImpl.h */,
);
sourceTree = "";
};
@@ -143,6 +198,17 @@
buildActionMask = 2147483647;
files = (
37155CE4233C00EB0034DCE9 /* menu.h in Headers */,
+ BC11A5BE2608D58F0017BAD0 /* automation.h in Headers */,
+ 183916173528EC2737DBE5E1 /* WindowBaseImpl.h in Headers */,
+ 1839171DCC651B0638603AC4 /* INSWindowHolder.h in Headers */,
+ 183919D91DB9AAB5D700C2EA /* WindowImpl.h in Headers */,
+ 18391CF07316F819E76B617C /* IWindowStateChanged.h in Headers */,
+ 18391C28BF1823B5464FDD36 /* ResizeScope.h in Headers */,
+ 18391ED5F611FF62C45F196D /* AvnView.h in Headers */,
+ 18391E1381E2D5BFD60265A9 /* AutoFitContentView.h in Headers */,
+ 18391F1E2411C79405A9943A /* WindowProtocol.h in Headers */,
+ 183914E50CF6D2EFC1667F7C /* WindowInterfaces.h in Headers */,
+ 18391AC65ADD7DDD33FBE737 /* PopupImpl.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -213,6 +279,7 @@
AB8F7D6B21482D7F0057DBA5 /* platformthreading.mm in Sources */,
1A3E5EA823E9E83B00EDE661 /* rendertarget.mm in Sources */,
1A3E5EAE23E9FB1300EDE661 /* cgl.mm in Sources */,
+ BC11A5BF2608D58F0017BAD0 /* automation.mm in Sources */,
37E2330F21583241000CB7E2 /* KeyTransform.mm in Sources */,
520624B322973F4100C4DCEF /* menu.mm in Sources */,
37A517B32159597E00FBA241 /* Screens.mm in Sources */,
@@ -220,7 +287,14 @@
1A465D10246AB61600C5858B /* dnd.mm in Sources */,
AB00E4F72147CA920032A60A /* main.mm in Sources */,
37C09D8821580FE4006A6758 /* SystemDialogs.mm in Sources */,
- AB661C202148286E00291242 /* window.mm in Sources */,
+ 1839179A55FC1421BEE83330 /* WindowBaseImpl.mm in Sources */,
+ 1839125F057B0A4EB1760058 /* WindowImpl.mm in Sources */,
+ 18391068E48EF96E3DB5FDAB /* ResizeScope.mm in Sources */,
+ 18391D4EB311BC7EF8B8C0A6 /* AvnView.mm in Sources */,
+ 18391AA7E0BBA74D184C5734 /* AutoFitContentView.mm in Sources */,
+ 1839151F32D1BB1AB51A7BB6 /* AvnPanelWindow.mm in Sources */,
+ 18391AC16726CBC45856233B /* AvnWindow.mm in Sources */,
+ 18391D8CD1756DC858DC1A09 /* PopupImpl.mm in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
diff --git a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/xcshareddata/xcschemes/Avalonia.Native.OSX.xcscheme b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/xcshareddata/xcschemes/Avalonia.Native.OSX.xcscheme
index 5d20a135b9..87a8312c38 100644
--- a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/xcshareddata/xcschemes/Avalonia.Native.OSX.xcscheme
+++ b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/xcshareddata/xcschemes/Avalonia.Native.OSX.xcscheme
@@ -56,10 +56,14 @@
+
+
* array);
extern IAvnStringArray* CreateAvnStringArray(NSArray* array);
extern IAvnStringArray* CreateAvnStringArray(NSString* string);
extern IAvnString* CreateByteArray(void* data, int len);
+extern NSString* GetNSStringAndRelease(IAvnString* s);
#endif /* AvnString_h */
diff --git a/native/Avalonia.Native/src/OSX/AvnString.mm b/native/Avalonia.Native/src/OSX/AvnString.mm
index cd0e2cdf94..5e50068c51 100644
--- a/native/Avalonia.Native/src/OSX/AvnString.mm
+++ b/native/Avalonia.Native/src/OSX/AvnString.mm
@@ -153,3 +153,19 @@ IAvnString* CreateByteArray(void* data, int len)
{
return new AvnStringImpl(data, len);
}
+
+NSString* GetNSStringAndRelease(IAvnString* s)
+{
+ NSString* result = nil;
+
+ if (s != nullptr)
+ {
+ char* p;
+ if (s->Pointer((void**)&p) == S_OK && p != nullptr)
+ result = [NSString stringWithUTF8String:p];
+
+ s->Release();
+ }
+
+ return result;
+}
diff --git a/native/Avalonia.Native/src/OSX/AvnView.h b/native/Avalonia.Native/src/OSX/AvnView.h
new file mode 100644
index 0000000000..86a68d34c5
--- /dev/null
+++ b/native/Avalonia.Native/src/OSX/AvnView.h
@@ -0,0 +1,27 @@
+//
+// Created by Dan Walmsley on 05/05/2022.
+// Copyright (c) 2022 Avalonia. All rights reserved.
+//
+#pragma once
+#import
+
+
+#import
+#import
+#include "common.h"
+#include "WindowImpl.h"
+#include "KeyTransform.h"
+
+@class AvnAccessibilityElement;
+
+@interface AvnView : NSView
+-(AvnView* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent;
+-(NSEvent* _Nonnull) lastMouseDownEvent;
+-(AvnPoint) translateLocalPoint:(AvnPoint)pt;
+-(void) setSwRenderedFrame: (AvnFramebuffer* _Nonnull) fb dispose: (IUnknown* _Nonnull) dispose;
+-(void) onClosed;
+
+-(AvnPlatformResizeReason) getResizeReason;
+-(void) setResizeReason:(AvnPlatformResizeReason)reason;
++ (AvnPoint)toAvnPoint:(CGPoint)p;
+@end
\ No newline at end of file
diff --git a/native/Avalonia.Native/src/OSX/AvnView.mm b/native/Avalonia.Native/src/OSX/AvnView.mm
new file mode 100644
index 0000000000..02526afbcb
--- /dev/null
+++ b/native/Avalonia.Native/src/OSX/AvnView.mm
@@ -0,0 +1,712 @@
+//
+// Created by Dan Walmsley on 05/05/2022.
+// Copyright (c) 2022 Avalonia. All rights reserved.
+//
+
+#import
+#include "AvnView.h"
+#include "automation.h"
+#import "WindowInterfaces.h"
+
+@implementation AvnView
+{
+ ComPtr _parent;
+ NSTrackingArea* _area;
+ bool _isLeftPressed, _isMiddlePressed, _isRightPressed, _isXButton1Pressed, _isXButton2Pressed;
+ AvnInputModifiers _modifierState;
+ NSEvent* _lastMouseDownEvent;
+ bool _lastKeyHandled;
+ AvnPixelSize _lastPixelSize;
+ NSObject* _renderTarget;
+ AvnPlatformResizeReason _resizeReason;
+ AvnAccessibilityElement* _accessibilityChild;
+}
+
+- (void)onClosed
+{
+ @synchronized (self)
+ {
+ _parent = nullptr;
+ }
+}
+
+- (NSEvent*) lastMouseDownEvent
+{
+ return _lastMouseDownEvent;
+}
+
+- (void) updateRenderTarget
+{
+ [_renderTarget resize:_lastPixelSize withScale:static_cast([[self window] backingScaleFactor])];
+ [self setNeedsDisplayInRect:[self frame]];
+}
+
+-(AvnView*) initWithParent: (WindowBaseImpl*) parent
+{
+ self = [super init];
+ _renderTarget = parent->renderTarget;
+ [self setWantsLayer:YES];
+ [self setLayerContentsRedrawPolicy: NSViewLayerContentsRedrawDuringViewResize];
+
+ _parent = parent;
+ _area = nullptr;
+ _lastPixelSize.Height = 100;
+ _lastPixelSize.Width = 100;
+ [self registerForDraggedTypes: @[@"public.data", GetAvnCustomDataType()]];
+
+ _modifierState = AvnInputModifiersNone;
+ return self;
+}
+
+- (BOOL)isFlipped
+{
+ return YES;
+}
+
+- (BOOL)wantsUpdateLayer
+{
+ return YES;
+}
+
+- (void)setLayer:(CALayer *)layer
+{
+ [_renderTarget setNewLayer: layer];
+ [super setLayer: layer];
+}
+
+- (BOOL)isOpaque
+{
+ return YES;
+}
+
+- (BOOL)acceptsFirstResponder
+{
+ return true;
+}
+
+- (BOOL)acceptsFirstMouse:(NSEvent *)event
+{
+ return true;
+}
+
+- (BOOL)canBecomeKeyView
+{
+ return true;
+}
+
+-(void)setFrameSize:(NSSize)newSize
+{
+ [super setFrameSize:newSize];
+
+ if(_area != nullptr)
+ {
+ [self removeTrackingArea:_area];
+ _area = nullptr;
+ }
+
+ if (_parent == nullptr)
+ {
+ return;
+ }
+
+ NSRect rect = NSZeroRect;
+ rect.size = newSize;
+
+ NSTrackingAreaOptions options = NSTrackingActiveAlways | NSTrackingMouseMoved | NSTrackingMouseEnteredAndExited | NSTrackingEnabledDuringMouseDrag;
+ _area = [[NSTrackingArea alloc] initWithRect:rect options:options owner:self userInfo:nullptr];
+ [self addTrackingArea:_area];
+
+ _parent->UpdateCursor();
+
+ auto fsize = [self convertSizeToBacking: [self frame].size];
+
+ if(_lastPixelSize.Width != (int)fsize.width || _lastPixelSize.Height != (int)fsize.height)
+ {
+ _lastPixelSize.Width = (int)fsize.width;
+ _lastPixelSize.Height = (int)fsize.height;
+ [self updateRenderTarget];
+
+ auto reason = [self inLiveResize] ? ResizeUser : _resizeReason;
+ _parent->BaseEvents->Resized(AvnSize{newSize.width, newSize.height}, reason);
+ }
+}
+
+- (void)updateLayer
+{
+ AvnInsidePotentialDeadlock deadlock;
+ if (_parent == nullptr)
+ {
+ return;
+ }
+
+ _parent->BaseEvents->RunRenderPriorityJobs();
+
+ if (_parent == nullptr)
+ {
+ return;
+ }
+
+ _parent->BaseEvents->Paint();
+}
+
+- (void)drawRect:(NSRect)dirtyRect
+{
+ return;
+}
+
+-(void) setSwRenderedFrame: (AvnFramebuffer*) fb dispose: (IUnknown*) dispose
+{
+ @autoreleasepool {
+ [_renderTarget setSwFrame:fb];
+ dispose->Release();
+ }
+}
+
+- (AvnPoint) translateLocalPoint:(AvnPoint)pt
+{
+ pt.Y = [self bounds].size.height - pt.Y;
+ return pt;
+}
+
++ (AvnPoint)toAvnPoint:(CGPoint)p
+{
+ AvnPoint result;
+
+ result.X = p.x;
+ result.Y = p.y;
+
+ return result;
+}
+
+- (void) viewDidChangeBackingProperties
+{
+ auto fsize = [self convertSizeToBacking: [self frame].size];
+ _lastPixelSize.Width = (int)fsize.width;
+ _lastPixelSize.Height = (int)fsize.height;
+ [self updateRenderTarget];
+
+ if(_parent != nullptr)
+ {
+ _parent->BaseEvents->ScalingChanged([_parent->Window backingScaleFactor]);
+ }
+
+ [super viewDidChangeBackingProperties];
+}
+
+- (bool) ignoreUserInput:(bool)trigerInputWhenDisabled
+{
+ if(_parent == nullptr)
+ {
+ return TRUE;
+ }
+
+ auto parentWindow = _parent->GetWindowProtocol();
+
+ if(parentWindow == nil || ![parentWindow shouldTryToHandleEvents])
+ {
+ if(trigerInputWhenDisabled)
+ {
+ auto window = dynamic_cast(_parent.getRaw());
+
+ if(window != nullptr)
+ {
+ window->WindowEvents->GotInputWhenDisabled();
+ }
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+- (void)mouseEvent:(NSEvent *)event withType:(AvnRawMouseEventType) type
+{
+ bool triggerInputWhenDisabled = type != Move;
+
+ if([self ignoreUserInput: triggerInputWhenDisabled])
+ {
+ return;
+ }
+
+ auto localPoint = [self convertPoint:[event locationInWindow] toView:self];
+ auto avnPoint = [AvnView toAvnPoint:localPoint];
+ auto point = [self translateLocalPoint:avnPoint];
+ AvnVector delta = { 0, 0};
+
+ if(type == Wheel)
+ {
+ auto speed = 5;
+
+ if([event hasPreciseScrollingDeltas])
+ {
+ speed = 50;
+ }
+
+ delta.X = [event scrollingDeltaX] / speed;
+ delta.Y = [event scrollingDeltaY] / speed;
+
+ if(delta.X == 0 && delta.Y == 0)
+ {
+ return;
+ }
+ }
+ else if (type == Magnify)
+ {
+ delta.X = delta.Y = [event magnification];
+ }
+ else if (type == Rotate)
+ {
+ delta.X = delta.Y = [event rotation];
+ }
+ else if (type == Swipe)
+ {
+ delta.X = [event deltaX];
+ delta.Y = [event deltaY];
+ }
+
+ uint32 timestamp = static_cast([event timestamp] * 1000);
+ auto modifiers = [self getModifiers:[event modifierFlags]];
+
+ if(type != Move ||
+ (
+ [self window] != nil &&
+ (
+ [[self window] firstResponder] == nil
+ || ![[[self window] firstResponder] isKindOfClass: [NSView class]]
+ )
+ )
+ )
+ [self becomeFirstResponder];
+
+ if(_parent != nullptr)
+ {
+ _parent->BaseEvents->RawMouseEvent(type, timestamp, modifiers, point, delta);
+ }
+
+ [super mouseMoved:event];
+}
+
+- (BOOL) resignFirstResponder
+{
+ _parent->BaseEvents->LostFocus();
+ return YES;
+}
+
+- (void)mouseMoved:(NSEvent *)event
+{
+ [self mouseEvent:event withType:Move];
+}
+
+- (void)mouseDown:(NSEvent *)event
+{
+ _isLeftPressed = true;
+ _lastMouseDownEvent = event;
+ [self mouseEvent:event withType:LeftButtonDown];
+}
+
+- (void)otherMouseDown:(NSEvent *)event
+{
+ _lastMouseDownEvent = event;
+
+ switch(event.buttonNumber)
+ {
+ case 2:
+ case 3:
+ _isMiddlePressed = true;
+ [self mouseEvent:event withType:MiddleButtonDown];
+ break;
+ case 4:
+ _isXButton1Pressed = true;
+ [self mouseEvent:event withType:XButton1Down];
+ break;
+ case 5:
+ _isXButton2Pressed = true;
+ [self mouseEvent:event withType:XButton2Down];
+ break;
+
+ default:
+ break;
+ }
+}
+
+- (void)rightMouseDown:(NSEvent *)event
+{
+ _isRightPressed = true;
+ _lastMouseDownEvent = event;
+ [self mouseEvent:event withType:RightButtonDown];
+}
+
+- (void)mouseUp:(NSEvent *)event
+{
+ _isLeftPressed = false;
+ [self mouseEvent:event withType:LeftButtonUp];
+}
+
+- (void)otherMouseUp:(NSEvent *)event
+{
+ switch(event.buttonNumber)
+ {
+ case 2:
+ case 3:
+ _isMiddlePressed = false;
+ [self mouseEvent:event withType:MiddleButtonUp];
+ break;
+ case 4:
+ _isXButton1Pressed = false;
+ [self mouseEvent:event withType:XButton1Up];
+ break;
+ case 5:
+ _isXButton2Pressed = false;
+ [self mouseEvent:event withType:XButton2Up];
+ break;
+
+ default:
+ break;
+ }
+}
+
+- (void)rightMouseUp:(NSEvent *)event
+{
+ _isRightPressed = false;
+ [self mouseEvent:event withType:RightButtonUp];
+}
+
+- (void)mouseDragged:(NSEvent *)event
+{
+ [self mouseEvent:event withType:Move];
+ [super mouseDragged:event];
+}
+
+- (void)otherMouseDragged:(NSEvent *)event
+{
+ [self mouseEvent:event withType:Move];
+ [super otherMouseDragged:event];
+}
+
+- (void)rightMouseDragged:(NSEvent *)event
+{
+ [self mouseEvent:event withType:Move];
+ [super rightMouseDragged:event];
+}
+
+- (void)scrollWheel:(NSEvent *)event
+{
+ [self mouseEvent:event withType:Wheel];
+ [super scrollWheel:event];
+}
+
+- (void)magnifyWithEvent:(NSEvent *)event
+{
+ [self mouseEvent:event withType:Magnify];
+ [super magnifyWithEvent:event];
+}
+
+- (void)rotateWithEvent:(NSEvent *)event
+{
+ [self mouseEvent:event withType:Rotate];
+ [super rotateWithEvent:event];
+}
+
+- (void)swipeWithEvent:(NSEvent *)event
+{
+ [self mouseEvent:event withType:Swipe];
+ [super swipeWithEvent:event];
+}
+
+- (void)mouseEntered:(NSEvent *)event
+{
+ [super mouseEntered:event];
+}
+
+- (void)mouseExited:(NSEvent *)event
+{
+ [self mouseEvent:event withType:LeaveWindow];
+ [super mouseExited:event];
+}
+
+- (void) keyboardEvent: (NSEvent *) event withType: (AvnRawKeyEventType)type
+{
+ if([self ignoreUserInput: false])
+ {
+ return;
+ }
+
+ auto key = s_KeyMap[[event keyCode]];
+
+ uint32_t timestamp = static_cast([event timestamp] * 1000);
+ auto modifiers = [self getModifiers:[event modifierFlags]];
+
+ if(_parent != nullptr)
+ {
+ _lastKeyHandled = _parent->BaseEvents->RawKeyEvent(type, timestamp, modifiers, key);
+ }
+}
+
+- (BOOL)performKeyEquivalent:(NSEvent *)event
+{
+ bool result = _lastKeyHandled;
+
+ _lastKeyHandled = false;
+
+ return result;
+}
+
+- (void)flagsChanged:(NSEvent *)event
+{
+ auto newModifierState = [self getModifiers:[event modifierFlags]];
+
+ bool isAltCurrentlyPressed = (_modifierState & Alt) == Alt;
+ bool isControlCurrentlyPressed = (_modifierState & Control) == Control;
+ bool isShiftCurrentlyPressed = (_modifierState & Shift) == Shift;
+ bool isCommandCurrentlyPressed = (_modifierState & Windows) == Windows;
+
+ bool isAltPressed = (newModifierState & Alt) == Alt;
+ bool isControlPressed = (newModifierState & Control) == Control;
+ bool isShiftPressed = (newModifierState & Shift) == Shift;
+ bool isCommandPressed = (newModifierState & Windows) == Windows;
+
+
+ if (isAltPressed && !isAltCurrentlyPressed)
+ {
+ [self keyboardEvent:event withType:KeyDown];
+ }
+ else if (isAltCurrentlyPressed && !isAltPressed)
+ {
+ [self keyboardEvent:event withType:KeyUp];
+ }
+
+ if (isControlPressed && !isControlCurrentlyPressed)
+ {
+ [self keyboardEvent:event withType:KeyDown];
+ }
+ else if (isControlCurrentlyPressed && !isControlPressed)
+ {
+ [self keyboardEvent:event withType:KeyUp];
+ }
+
+ if (isShiftPressed && !isShiftCurrentlyPressed)
+ {
+ [self keyboardEvent:event withType:KeyDown];
+ }
+ else if(isShiftCurrentlyPressed && !isShiftPressed)
+ {
+ [self keyboardEvent:event withType:KeyUp];
+ }
+
+ if(isCommandPressed && !isCommandCurrentlyPressed)
+ {
+ [self keyboardEvent:event withType:KeyDown];
+ }
+ else if(isCommandCurrentlyPressed && ! isCommandPressed)
+ {
+ [self keyboardEvent:event withType:KeyUp];
+ }
+
+ _modifierState = newModifierState;
+
+ [[self inputContext] handleEvent:event];
+ [super flagsChanged:event];
+}
+
+- (void)keyDown:(NSEvent *)event
+{
+ [self keyboardEvent:event withType:KeyDown];
+ [[self inputContext] handleEvent:event];
+ [super keyDown:event];
+}
+
+- (void)keyUp:(NSEvent *)event
+{
+ [self keyboardEvent:event withType:KeyUp];
+ [super keyUp:event];
+}
+
+- (AvnInputModifiers)getModifiers:(NSEventModifierFlags)mod
+{
+ unsigned int rv = 0;
+
+ if (mod & NSEventModifierFlagControl)
+ rv |= Control;
+ if (mod & NSEventModifierFlagShift)
+ rv |= Shift;
+ if (mod & NSEventModifierFlagOption)
+ rv |= Alt;
+ if (mod & NSEventModifierFlagCommand)
+ rv |= Windows;
+
+ if (_isLeftPressed)
+ rv |= LeftMouseButton;
+ if (_isMiddlePressed)
+ rv |= MiddleMouseButton;
+ if (_isRightPressed)
+ rv |= RightMouseButton;
+ if (_isXButton1Pressed)
+ rv |= XButton1MouseButton;
+ if (_isXButton2Pressed)
+ rv |= XButton2MouseButton;
+
+ return (AvnInputModifiers)rv;
+}
+
+- (BOOL)hasMarkedText
+{
+ return _lastKeyHandled;
+}
+
+- (NSRange)markedRange
+{
+ return NSMakeRange(NSNotFound, 0);
+}
+
+- (NSRange)selectedRange
+{
+ return NSMakeRange(NSNotFound, 0);
+}
+
+- (void)setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
+{
+
+}
+
+- (void)unmarkText
+{
+
+}
+
+- (NSArray *)validAttributesForMarkedText
+{
+ return [NSArray new];
+}
+
+- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)range actualRange:(NSRangePointer)actualRange
+{
+ return [NSAttributedString new];
+}
+
+- (void)insertText:(id)string replacementRange:(NSRange)replacementRange
+{
+ if(!_lastKeyHandled)
+ {
+ if(_parent != nullptr)
+ {
+ _lastKeyHandled = _parent->BaseEvents->RawTextInputEvent(0, [string UTF8String]);
+ }
+ }
+}
+
+- (NSUInteger)characterIndexForPoint:(NSPoint)point
+{
+ return 0;
+}
+
+- (NSRect)firstRectForCharacterRange:(NSRange)range actualRange:(NSRangePointer)actualRange
+{
+ CGRect result = { 0 };
+
+ return result;
+}
+
+- (NSDragOperation)triggerAvnDragEvent: (AvnDragEventType) type info: (id )info
+{
+ auto localPoint = [self convertPoint:[info draggingLocation] toView:self];
+ auto avnPoint = [AvnView toAvnPoint:localPoint];
+ auto point = [self translateLocalPoint:avnPoint];
+ auto modifiers = [self getModifiers:[[NSApp currentEvent] modifierFlags]];
+ NSDragOperation nsop = [info draggingSourceOperationMask];
+
+ auto effects = ConvertDragDropEffects(nsop);
+ int reffects = (int)_parent->BaseEvents
+ ->DragEvent(type, point, modifiers, effects,
+ CreateClipboard([info draggingPasteboard], nil),
+ GetAvnDataObjectHandleFromDraggingInfo(info));
+
+ NSDragOperation ret = static_cast(0);
+
+ // Ensure that the managed part didn't add any new effects
+ reffects = (int)effects & reffects;
+
+ // OSX requires exactly one operation
+ if((reffects & (int)AvnDragDropEffects::Copy) != 0)
+ ret = NSDragOperationCopy;
+ else if((reffects & (int)AvnDragDropEffects::Move) != 0)
+ ret = NSDragOperationMove;
+ else if((reffects & (int)AvnDragDropEffects::Link) != 0)
+ ret = NSDragOperationLink;
+ if(ret == 0)
+ ret = NSDragOperationNone;
+ return ret;
+}
+
+- (NSDragOperation)draggingEntered:(id )sender
+{
+ return [self triggerAvnDragEvent: AvnDragEventType::Enter info:sender];
+}
+
+- (NSDragOperation)draggingUpdated:(id )sender
+{
+ return [self triggerAvnDragEvent: AvnDragEventType::Over info:sender];
+}
+
+- (void)draggingExited:(id )sender
+{
+ [self triggerAvnDragEvent: AvnDragEventType::Leave info:sender];
+}
+
+- (BOOL)prepareForDragOperation:(id )sender
+{
+ return [self triggerAvnDragEvent: AvnDragEventType::Over info:sender] != NSDragOperationNone;
+}
+
+- (BOOL)performDragOperation:(id )sender
+{
+ return [self triggerAvnDragEvent: AvnDragEventType::Drop info:sender] != NSDragOperationNone;
+}
+
+- (void)concludeDragOperation:(nullable id )sender
+{
+
+}
+
+- (AvnPlatformResizeReason)getResizeReason
+{
+ return _resizeReason;
+}
+
+- (void)setResizeReason:(AvnPlatformResizeReason)reason
+{
+ _resizeReason = reason;
+}
+
+- (AvnAccessibilityElement *) accessibilityChild
+{
+ if (_accessibilityChild == nil)
+ {
+ auto peer = _parent->BaseEvents->GetAutomationPeer();
+
+ if (peer == nil)
+ return nil;
+
+ _accessibilityChild = [AvnAccessibilityElement acquire:peer];
+ }
+
+ return _accessibilityChild;
+}
+
+- (NSArray *)accessibilityChildren
+{
+ auto child = [self accessibilityChild];
+ return NSAccessibilityUnignoredChildrenForOnlyChild(child);
+}
+
+- (id)accessibilityHitTest:(NSPoint)point
+{
+ return [[self accessibilityChild] accessibilityHitTest:point];
+}
+
+- (id)accessibilityFocusedUIElement
+{
+ return [[self accessibilityChild] accessibilityFocusedUIElement];
+}
+
+@end
\ No newline at end of file
diff --git a/native/Avalonia.Native/src/OSX/AvnWindow.mm b/native/Avalonia.Native/src/OSX/AvnWindow.mm
new file mode 100644
index 0000000000..f51c693777
--- /dev/null
+++ b/native/Avalonia.Native/src/OSX/AvnWindow.mm
@@ -0,0 +1,466 @@
+//
+// Created by Dan Walmsley on 06/05/2022.
+// Copyright (c) 2022 Avalonia. All rights reserved.
+//
+
+
+#import
+#import "WindowProtocol.h"
+#import "WindowBaseImpl.h"
+
+#ifdef IS_NSPANEL
+#define BASE_CLASS NSPanel
+#define CLASS_NAME AvnPanel
+#else
+#define BASE_CLASS NSWindow
+#define CLASS_NAME AvnWindow
+#endif
+
+#import
+#include "common.h"
+#include "menu.h"
+#include "automation.h"
+#include "WindowBaseImpl.h"
+#include "WindowImpl.h"
+#include "AvnView.h"
+#include "WindowInterfaces.h"
+#include "PopupImpl.h"
+
+@implementation CLASS_NAME
+{
+ ComPtr _parent;
+ bool _closed;
+ bool _isEnabled;
+ bool _canBecomeKeyWindow;
+ bool _isExtended;
+ AvnMenu* _menu;
+}
+
+-(void) setIsExtended:(bool)value;
+{
+ _isExtended = value;
+}
+
+-(bool) isDialog
+{
+ return _parent->IsDialog();
+}
+
+-(double) getExtendedTitleBarHeight
+{
+ if(_isExtended)
+ {
+ for (id subview in self.contentView.superview.subviews)
+ {
+ if ([subview isKindOfClass:NSClassFromString(@"NSTitlebarContainerView")])
+ {
+ NSView *titlebarView = [subview subviews][0];
+
+ return (double)titlebarView.frame.size.height;
+ }
+ }
+
+ return -1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+- (void)performClose:(id)sender
+{
+ if([[self delegate] respondsToSelector:@selector(windowShouldClose:)])
+ {
+ if(![[self delegate] windowShouldClose:self]) return;
+ }
+ else if([self respondsToSelector:@selector(windowShouldClose:)])
+ {
+ if(![self windowShouldClose:self]) return;
+ }
+
+ [self close];
+}
+
+- (void)pollModalSession:(nonnull NSModalSession)session
+{
+ auto response = [NSApp runModalSession:session];
+
+ if(response == NSModalResponseContinue)
+ {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [self pollModalSession:session];
+ });
+ }
+ else if (!_closed)
+ {
+ [self orderOut:self];
+ [NSApp endModalSession:session];
+ }
+}
+
+-(void) showWindowMenuWithAppMenu
+{
+ if(_menu != nullptr)
+ {
+ auto appMenuItem = ::GetAppMenuItem();
+
+ if(appMenuItem != nullptr)
+ {
+ auto appMenu = [appMenuItem menu];
+
+ [appMenu removeItem:appMenuItem];
+
+ [_menu insertItem:appMenuItem atIndex:0];
+
+ [_menu setHasGlobalMenuItem:true];
+ }
+
+ [NSApp setMenu:_menu];
+ }
+ else
+ {
+ [self showAppMenuOnly];
+ }
+}
+
+-(void) showAppMenuOnly
+{
+ auto appMenuItem = ::GetAppMenuItem();
+
+ if(appMenuItem != nullptr)
+ {
+ auto appMenu = ::GetAppMenu();
+
+ auto nativeAppMenu = dynamic_cast(appMenu);
+
+ [[appMenuItem menu] removeItem:appMenuItem];
+
+ if(_menu != nullptr)
+ {
+ [_menu setHasGlobalMenuItem:false];
+ }
+
+ [nativeAppMenu->GetNative() addItem:appMenuItem];
+
+ [NSApp setMenu:nativeAppMenu->GetNative()];
+ }
+}
+
+-(void) applyMenu:(AvnMenu *)menu
+{
+ if(menu == nullptr)
+ {
+ menu = [AvnMenu new];
+ }
+
+ _menu = menu;
+}
+
+-(CLASS_NAME*) initWithParent: (WindowBaseImpl*) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask;
+{
+ // https://jameshfisher.com/2020/07/10/why-is-the-contentrect-of-my-nswindow-ignored/
+ // create nswindow with specific contentRect, otherwise we wont be able to resize the window
+ // until several ms after the window is physically on the screen.
+ self = [super initWithContentRect:contentRect styleMask: styleMask backing:NSBackingStoreBuffered defer:false];
+
+ [self setReleasedWhenClosed:false];
+ _parent = parent;
+ [self setDelegate:self];
+ _closed = false;
+ _isEnabled = true;
+
+ [self backingScaleFactor];
+ [self setOpaque:NO];
+ [self setBackgroundColor: [NSColor clearColor]];
+
+ _isExtended = false;
+
+#ifdef IS_NSPANEL
+ [self setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces|NSWindowCollectionBehaviorFullScreenAuxiliary];
+#endif
+
+ return self;
+}
+
+- (BOOL)windowShouldClose:(NSWindow *)sender
+{
+ auto window = dynamic_cast(_parent.getRaw());
+
+ if(window != nullptr)
+ {
+ return !window->WindowEvents->Closing();
+ }
+
+ return true;
+}
+
+- (void)windowDidChangeBackingProperties:(NSNotification *)notification
+{
+ [self backingScaleFactor];
+}
+
+
+
+- (void)windowWillClose:(NSNotification *)notification
+{
+ _closed = true;
+ if(_parent)
+ {
+ ComPtr parent = _parent;
+ _parent = NULL;
+ [self restoreParentWindow];
+ parent->BaseEvents->Closed();
+ [parent->View onClosed];
+ }
+}
+
+-(BOOL)canBecomeKeyWindow
+{
+ if(_canBecomeKeyWindow)
+ {
+ // If the window has a child window being shown as a dialog then don't allow it to become the key window.
+ for(NSWindow* uch in [self childWindows])
+ {
+ if (![uch conformsToProtocol:@protocol(AvnWindowProtocol)])
+ {
+ continue;
+ }
+
+ id ch = (id ) uch;
+
+ if(ch.isDialog)
+ return false;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+#ifndef IS_NSPANEL
+-(BOOL)canBecomeMainWindow
+{
+ return true;
+}
+#endif
+
+-(void)setCanBecomeKeyWindow:(bool)value
+{
+ _canBecomeKeyWindow = value;
+}
+
+-(bool)shouldTryToHandleEvents
+{
+ return _isEnabled;
+}
+
+-(void) setEnabled:(bool)enable
+{
+ _isEnabled = enable;
+}
+
+-(void)becomeKeyWindow
+{
+ [self showWindowMenuWithAppMenu];
+
+ if(_parent != nullptr)
+ {
+ _parent->BaseEvents->Activated();
+ }
+
+ [super becomeKeyWindow];
+}
+
+-(void) restoreParentWindow;
+{
+ auto parent = [self parentWindow];
+
+ if(parent != nil)
+ {
+ [parent removeChildWindow:self];
+ }
+}
+
+- (void)windowDidMiniaturize:(NSNotification *)notification
+{
+ auto parent = dynamic_cast(_parent.operator->());
+
+ if(parent != nullptr)
+ {
+ parent->WindowStateChanged();
+ }
+}
+
+- (void)windowDidDeminiaturize:(NSNotification *)notification
+{
+ auto parent = dynamic_cast(_parent.operator->());
+
+ if(parent != nullptr)
+ {
+ parent->WindowStateChanged();
+ }
+}
+
+- (void)windowDidResize:(NSNotification *)notification
+{
+ auto parent = dynamic_cast(_parent.operator->());
+
+ if(parent != nullptr)
+ {
+ parent->WindowStateChanged();
+ }
+}
+
+- (void)windowWillExitFullScreen:(NSNotification *)notification
+{
+ auto parent = dynamic_cast(_parent.operator->());
+
+ if(parent != nullptr)
+ {
+ parent->StartStateTransition();
+ }
+}
+
+- (void)windowDidExitFullScreen:(NSNotification *)notification
+{
+ auto parent = dynamic_cast(_parent.operator->());
+
+ if(parent != nullptr)
+ {
+ parent->EndStateTransition();
+
+ if(parent->Decorations() != SystemDecorationsFull && parent->WindowState() == Maximized)
+ {
+ NSRect screenRect = [[self screen] visibleFrame];
+ [self setFrame:screenRect display:YES];
+ }
+
+ if(parent->WindowState() == Minimized)
+ {
+ [self miniaturize:nullptr];
+ }
+
+ parent->WindowStateChanged();
+ }
+}
+
+- (void)windowWillEnterFullScreen:(NSNotification *)notification
+{
+ auto parent = dynamic_cast(_parent.operator->());
+
+ if(parent != nullptr)
+ {
+ parent->StartStateTransition();
+ }
+}
+
+- (void)windowDidEnterFullScreen:(NSNotification *)notification
+{
+ auto parent = dynamic_cast(_parent.operator->());
+
+ if(parent != nullptr)
+ {
+ parent->EndStateTransition();
+ parent->WindowStateChanged();
+ }
+}
+
+- (BOOL)windowShouldZoom:(NSWindow *)window toFrame:(NSRect)newFrame
+{
+ return true;
+}
+
+-(void)resignKeyWindow
+{
+ if(_parent)
+ _parent->BaseEvents->Deactivated();
+
+ [self showAppMenuOnly];
+
+ [super resignKeyWindow];
+}
+
+- (void)windowDidMove:(NSNotification *)notification
+{
+ AvnPoint position;
+
+ if(_parent != nullptr)
+ {
+ auto cparent = dynamic_cast(_parent.getRaw());
+
+ if(cparent != nullptr)
+ {
+ if(!cparent->IsShown())
+ {
+ return;
+ }
+
+ if(cparent->WindowState() == Maximized)
+ {
+ cparent->SetWindowState(Normal);
+ }
+ }
+
+ _parent->GetPosition(&position);
+ _parent->BaseEvents->PositionChanged(position);
+ }
+}
+
+- (AvnPoint) translateLocalPoint:(AvnPoint)pt
+{
+ pt.Y = [self frame].size.height - pt.Y;
+ return pt;
+}
+
+- (void)sendEvent:(NSEvent *)event
+{
+ [super sendEvent:event];
+
+ /// This is to detect non-client clicks. This can only be done on Windows... not popups, hence the dynamic_cast.
+ if(_parent != nullptr && dynamic_cast(_parent.getRaw()) != nullptr)
+ {
+ switch(event.type)
+ {
+ case NSEventTypeLeftMouseDown:
+ {
+ AvnView* view = _parent->View;
+ NSPoint windowPoint = [event locationInWindow];
+ NSPoint viewPoint = [view convertPoint:windowPoint fromView:nil];
+
+ if (!NSPointInRect(viewPoint, view.bounds))
+ {
+ auto avnPoint = [AvnView toAvnPoint:windowPoint];
+ auto point = [self translateLocalPoint:avnPoint];
+ AvnVector delta = { 0, 0 };
+
+ _parent->BaseEvents->RawMouseEvent(NonClientLeftButtonDown, static_cast([event timestamp] * 1000), AvnInputModifiersNone, point, delta);
+ }
+ }
+ break;
+
+ case NSEventTypeMouseEntered:
+ {
+ _parent->UpdateCursor();
+ }
+ break;
+
+ case NSEventTypeMouseExited:
+ {
+ [[NSCursor arrowCursor] set];
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+- (void)disconnectParent {
+ _parent = nullptr;
+}
+
+@end
+
diff --git a/native/Avalonia.Native/src/OSX/INSWindowHolder.h b/native/Avalonia.Native/src/OSX/INSWindowHolder.h
new file mode 100644
index 0000000000..ae64a53e7d
--- /dev/null
+++ b/native/Avalonia.Native/src/OSX/INSWindowHolder.h
@@ -0,0 +1,17 @@
+//
+// Created by Dan Walmsley on 04/05/2022.
+// Copyright (c) 2022 Avalonia. All rights reserved.
+//
+
+#ifndef AVALONIA_NATIVE_OSX_INSWINDOWHOLDER_H
+#define AVALONIA_NATIVE_OSX_INSWINDOWHOLDER_H
+
+@class AvnView;
+
+struct INSWindowHolder
+{
+ virtual NSWindow* _Nonnull GetNSWindow () = 0;
+ virtual NSView* _Nonnull GetNSView () = 0;
+};
+
+#endif //AVALONIA_NATIVE_OSX_INSWINDOWHOLDER_H
diff --git a/native/Avalonia.Native/src/OSX/IWindowStateChanged.h b/native/Avalonia.Native/src/OSX/IWindowStateChanged.h
new file mode 100644
index 0000000000..f0905da3ac
--- /dev/null
+++ b/native/Avalonia.Native/src/OSX/IWindowStateChanged.h
@@ -0,0 +1,18 @@
+//
+// Created by Dan Walmsley on 04/05/2022.
+// Copyright (c) 2022 Avalonia. All rights reserved.
+//
+
+#ifndef AVALONIA_NATIVE_OSX_IWINDOWSTATECHANGED_H
+#define AVALONIA_NATIVE_OSX_IWINDOWSTATECHANGED_H
+
+struct IWindowStateChanged
+{
+ virtual void WindowStateChanged () = 0;
+ virtual void StartStateTransition () = 0;
+ virtual void EndStateTransition () = 0;
+ virtual SystemDecorations Decorations () = 0;
+ virtual AvnWindowState WindowState () = 0;
+};
+
+#endif //AVALONIA_NATIVE_OSX_IWINDOWSTATECHANGED_H
diff --git a/native/Avalonia.Native/src/OSX/PopupImpl.h b/native/Avalonia.Native/src/OSX/PopupImpl.h
new file mode 100644
index 0000000000..451019a6a4
--- /dev/null
+++ b/native/Avalonia.Native/src/OSX/PopupImpl.h
@@ -0,0 +1,9 @@
+//
+// Created by Dan Walmsley on 06/05/2022.
+// Copyright (c) 2022 Avalonia. All rights reserved.
+//
+
+#ifndef AVALONIA_NATIVE_OSX_POPUPIMPL_H
+#define AVALONIA_NATIVE_OSX_POPUPIMPL_H
+
+#endif //AVALONIA_NATIVE_OSX_POPUPIMPL_H
diff --git a/native/Avalonia.Native/src/OSX/PopupImpl.mm b/native/Avalonia.Native/src/OSX/PopupImpl.mm
new file mode 100644
index 0000000000..3c5afd9424
--- /dev/null
+++ b/native/Avalonia.Native/src/OSX/PopupImpl.mm
@@ -0,0 +1,61 @@
+//
+// Created by Dan Walmsley on 06/05/2022.
+// Copyright (c) 2022 Avalonia. All rights reserved.
+//
+
+#include "WindowInterfaces.h"
+#include "AvnView.h"
+#include "WindowImpl.h"
+#include "automation.h"
+#include "menu.h"
+#include "common.h"
+#import "WindowBaseImpl.h"
+#import "WindowProtocol.h"
+#import
+#include "PopupImpl.h"
+
+class PopupImpl : public virtual WindowBaseImpl, public IAvnPopup
+{
+private:
+ BEGIN_INTERFACE_MAP()
+ INHERIT_INTERFACE_MAP(WindowBaseImpl)
+ INTERFACE_MAP_ENTRY(IAvnPopup, IID_IAvnPopup)
+ END_INTERFACE_MAP()
+ virtual ~PopupImpl(){}
+ ComPtr WindowEvents;
+ PopupImpl(IAvnWindowEvents* events, IAvnGlContext* gl) : WindowBaseImpl(events, gl)
+ {
+ WindowEvents = events;
+ }
+protected:
+ virtual NSWindowStyleMask GetStyle() override
+ {
+ return NSWindowStyleMaskBorderless;
+ }
+
+ virtual void OnInitialiseNSWindow () override
+ {
+ [Window setLevel:NSPopUpMenuWindowLevel];
+ }
+
+public:
+ virtual bool ShouldTakeFocusOnShow() override
+ {
+ return false;
+ }
+
+ virtual HRESULT Show(bool activate, bool isDialog) override
+ {
+ return WindowBaseImpl::Show(activate, true);
+ }
+};
+
+
+extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events, IAvnGlContext* gl)
+{
+ @autoreleasepool
+ {
+ IAvnPopup* ptr = dynamic_cast(new PopupImpl(events, gl));
+ return ptr;
+ }
+}
diff --git a/native/Avalonia.Native/src/OSX/ResizeScope.h b/native/Avalonia.Native/src/OSX/ResizeScope.h
new file mode 100644
index 0000000000..9a43c158fe
--- /dev/null
+++ b/native/Avalonia.Native/src/OSX/ResizeScope.h
@@ -0,0 +1,24 @@
+//
+// Created by Dan Walmsley on 04/05/2022.
+// Copyright (c) 2022 Avalonia. All rights reserved.
+//
+
+#ifndef AVALONIA_NATIVE_OSX_RESIZESCOPE_H
+#define AVALONIA_NATIVE_OSX_RESIZESCOPE_H
+
+#include "avalonia-native.h"
+
+@class AvnView;
+
+class ResizeScope
+{
+public:
+ ResizeScope(AvnView* _Nonnull view, AvnPlatformResizeReason reason);
+
+ ~ResizeScope();
+private:
+ AvnView* _Nonnull _view;
+ AvnPlatformResizeReason _restore;
+};
+
+#endif //AVALONIA_NATIVE_OSX_RESIZESCOPE_H
diff --git a/native/Avalonia.Native/src/OSX/ResizeScope.mm b/native/Avalonia.Native/src/OSX/ResizeScope.mm
new file mode 100644
index 0000000000..9f1177af8b
--- /dev/null
+++ b/native/Avalonia.Native/src/OSX/ResizeScope.mm
@@ -0,0 +1,18 @@
+//
+// Created by Dan Walmsley on 04/05/2022.
+// Copyright (c) 2022 Avalonia. All rights reserved.
+//
+
+#import
+#include "ResizeScope.h"
+#include "AvnView.h"
+
+ResizeScope::ResizeScope(AvnView *view, AvnPlatformResizeReason reason) {
+ _view = view;
+ _restore = [view getResizeReason];
+ [view setResizeReason:reason];
+}
+
+ResizeScope::~ResizeScope() {
+ [_view setResizeReason:_restore];
+}
diff --git a/native/Avalonia.Native/src/OSX/SystemDialogs.mm b/native/Avalonia.Native/src/OSX/SystemDialogs.mm
index a47221056b..535b6c3b66 100644
--- a/native/Avalonia.Native/src/OSX/SystemDialogs.mm
+++ b/native/Avalonia.Native/src/OSX/SystemDialogs.mm
@@ -1,5 +1,5 @@
#include "common.h"
-#include "window.h"
+#include "INSWindowHolder.h"
class SystemDialogs : public ComSingleObject
{
diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h
new file mode 100644
index 0000000000..83850e780c
--- /dev/null
+++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h
@@ -0,0 +1,136 @@
+//
+// Created by Dan Walmsley on 04/05/2022.
+// Copyright (c) 2022 Avalonia. All rights reserved.
+//
+
+#ifndef AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H
+#define AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H
+
+#include "rendertarget.h"
+#include "INSWindowHolder.h"
+
+@class AutoFitContentView;
+@class AvnMenu;
+@protocol AvnWindowProtocol;
+
+class WindowBaseImpl : public virtual ComObject,
+ public virtual IAvnWindowBase,
+ public INSWindowHolder {
+
+public:
+ FORWARD_IUNKNOWN()
+
+BEGIN_INTERFACE_MAP()
+ INTERFACE_MAP_ENTRY(IAvnWindowBase, IID_IAvnWindowBase)
+ END_INTERFACE_MAP()
+
+ virtual ~WindowBaseImpl();
+
+ WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl);
+
+ virtual HRESULT ObtainNSWindowHandle(void **ret) override;
+
+ virtual HRESULT ObtainNSWindowHandleRetained(void **ret) override;
+
+ virtual HRESULT ObtainNSViewHandle(void **ret) override;
+
+ virtual HRESULT ObtainNSViewHandleRetained(void **ret) override;
+
+ virtual NSWindow *GetNSWindow() override;
+
+ virtual NSView *GetNSView() override;
+
+ virtual HRESULT Show(bool activate, bool isDialog) override;
+
+ virtual bool IsShown ();
+
+ virtual bool ShouldTakeFocusOnShow();
+
+ virtual HRESULT Hide() override;
+
+ virtual HRESULT Activate() override;
+
+ virtual HRESULT SetTopMost(bool value) override;
+
+ virtual HRESULT Close() override;
+
+ virtual HRESULT GetClientSize(AvnSize *ret) override;
+
+ virtual HRESULT GetFrameSize(AvnSize *ret) override;
+
+ virtual HRESULT GetScaling(double *ret) override;
+
+ virtual HRESULT SetMinMaxSize(AvnSize minSize, AvnSize maxSize) override;
+
+ virtual HRESULT Resize(double x, double y, AvnPlatformResizeReason reason) override;
+
+ virtual HRESULT Invalidate(__attribute__((unused)) AvnRect rect) override;
+
+ virtual HRESULT SetMainMenu(IAvnMenu *menu) override;
+
+ virtual HRESULT BeginMoveDrag() override;
+
+ virtual HRESULT BeginResizeDrag(__attribute__((unused)) AvnWindowEdge edge) override;
+
+ virtual HRESULT GetPosition(AvnPoint *ret) override;
+
+ virtual HRESULT SetPosition(AvnPoint point) override;
+
+ virtual HRESULT PointToClient(AvnPoint point, AvnPoint *ret) override;
+
+ virtual HRESULT PointToScreen(AvnPoint point, AvnPoint *ret) override;
+
+ virtual HRESULT ThreadSafeSetSwRenderedFrame(AvnFramebuffer *fb, IUnknown *dispose) override;
+
+ virtual HRESULT SetCursor(IAvnCursor *cursor) override;
+
+ virtual void UpdateCursor();
+
+ virtual HRESULT CreateGlRenderTarget(IAvnGlSurfaceRenderTarget **ppv) override;
+
+ virtual HRESULT CreateNativeControlHost(IAvnNativeControlHost **retOut) override;
+
+ virtual HRESULT SetBlurEnabled(bool enable) override;
+
+ virtual HRESULT BeginDragAndDropOperation(AvnDragDropEffects effects, AvnPoint point,
+ IAvnClipboard *clipboard, IAvnDndResultCallback *cb,
+ void *sourceHandle) override;
+
+ virtual bool IsDialog();
+
+ id GetWindowProtocol ();
+
+protected:
+ virtual NSWindowStyleMask GetStyle();
+
+ void UpdateStyle();
+
+ virtual void OnInitialiseNSWindow ();
+
+private:
+ void CreateNSWindow (bool isDialog);
+ void CleanNSWindow ();
+ void InitialiseNSWindow ();
+
+ NSCursor *cursor;
+ ComPtr _glContext;
+ bool hasPosition;
+ NSSize lastSize;
+ NSSize lastMinSize;
+ NSSize lastMaxSize;
+ AvnMenu* lastMenu;
+ bool _inResize;
+
+protected:
+ AvnPoint lastPositionSet;
+ AutoFitContentView *StandardContainer;
+ bool _shown;
+
+public:
+ NSObject *renderTarget;
+ NSWindow * Window;
+ ComPtr BaseEvents;
+ AvnView *View;
+};
+
+#endif //AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H
diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm
new file mode 100644
index 0000000000..6dc59ae4d8
--- /dev/null
+++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm
@@ -0,0 +1,618 @@
+//
+// Created by Dan Walmsley on 04/05/2022.
+// Copyright (c) 2022 Avalonia. All rights reserved.
+//
+
+#import
+#include "common.h"
+#include "AvnView.h"
+#include "menu.h"
+#include "automation.h"
+#include "cursor.h"
+#include "ResizeScope.h"
+#include "AutoFitContentView.h"
+#import "WindowProtocol.h"
+#import "WindowInterfaces.h"
+#include "WindowBaseImpl.h"
+
+
+WindowBaseImpl::~WindowBaseImpl() {
+ View = nullptr;
+ Window = nullptr;
+}
+
+WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl) {
+ _shown = false;
+ _inResize = false;
+ BaseEvents = events;
+ _glContext = gl;
+ renderTarget = [[IOSurfaceRenderTarget alloc] initWithOpenGlContext:gl];
+ View = [[AvnView alloc] initWithParent:this];
+ StandardContainer = [[AutoFitContentView new] initWithContent:View];
+
+ lastPositionSet = { 0, 0 };
+ hasPosition = false;
+ lastSize = NSSize { 100, 100 };
+ lastMaxSize = NSSize { CGFLOAT_MAX, CGFLOAT_MAX};
+ lastMinSize = NSSize { 0, 0 };
+
+ Window = nullptr;
+ lastMenu = nullptr;
+}
+
+HRESULT WindowBaseImpl::ObtainNSViewHandle(void **ret) {
+ START_COM_CALL;
+
+ if (ret == nullptr) {
+ return E_POINTER;
+ }
+
+ *ret = (__bridge void *) View;
+
+ return S_OK;
+}
+
+HRESULT WindowBaseImpl::ObtainNSViewHandleRetained(void **ret) {
+ START_COM_CALL;
+
+ if (ret == nullptr) {
+ return E_POINTER;
+ }
+
+ *ret = (__bridge_retained void *) View;
+
+ return S_OK;
+}
+
+NSWindow *WindowBaseImpl::GetNSWindow() {
+ return Window;
+}
+
+NSView *WindowBaseImpl::GetNSView() {
+ return View;
+}
+
+HRESULT WindowBaseImpl::ObtainNSWindowHandleRetained(void **ret) {
+ START_COM_CALL;
+
+ if (ret == nullptr) {
+ return E_POINTER;
+ }
+
+ *ret = (__bridge_retained void *) Window;
+
+ return S_OK;
+}
+
+HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) {
+ START_COM_CALL;
+
+ @autoreleasepool {
+ CreateNSWindow(isDialog);
+ InitialiseNSWindow();
+
+ if(hasPosition)
+ {
+ SetPosition(lastPositionSet);
+ } else
+ {
+ [Window center];
+ }
+
+ UpdateStyle();
+
+ if (ShouldTakeFocusOnShow() && activate) {
+ [Window orderFront:Window];
+ [Window makeKeyAndOrderFront:Window];
+ [Window makeFirstResponder:View];
+ [NSApp activateIgnoringOtherApps:YES];
+ } else {
+ [Window orderFront:Window];
+ }
+
+ _shown = true;
+
+ return S_OK;
+ }
+}
+
+bool WindowBaseImpl::IsShown ()
+{
+ return _shown;
+}
+
+bool WindowBaseImpl::ShouldTakeFocusOnShow() {
+ return true;
+}
+
+HRESULT WindowBaseImpl::ObtainNSWindowHandle(void **ret) {
+ START_COM_CALL;
+
+ if (ret == nullptr) {
+ return E_POINTER;
+ }
+
+ *ret = (__bridge void *) Window;
+
+ return S_OK;
+}
+
+HRESULT WindowBaseImpl::Hide() {
+ START_COM_CALL;
+
+ @autoreleasepool {
+ if (Window != nullptr) {
+ [Window orderOut:Window];
+
+ [GetWindowProtocol() restoreParentWindow];
+ }
+
+ return S_OK;
+ }
+}
+
+HRESULT WindowBaseImpl::Activate() {
+ START_COM_CALL;
+
+ @autoreleasepool {
+ if (Window != nullptr) {
+ [Window makeKeyAndOrderFront:nil];
+ [NSApp activateIgnoringOtherApps:YES];
+ }
+ }
+
+ return S_OK;
+}
+
+HRESULT WindowBaseImpl::SetTopMost(bool value) {
+ START_COM_CALL;
+
+ @autoreleasepool {
+ [Window setLevel:value ? NSFloatingWindowLevel : NSNormalWindowLevel];
+
+ return S_OK;
+ }
+}
+
+HRESULT WindowBaseImpl::Close() {
+ START_COM_CALL;
+
+ @autoreleasepool {
+ if (Window != nullptr) {
+ auto window = Window;
+ Window = nullptr;
+
+ try {
+ // Seems to throw sometimes on application exit.
+ [window close];
+ }
+ catch (NSException *) {}
+ }
+
+ return S_OK;
+ }
+}
+
+HRESULT WindowBaseImpl::GetClientSize(AvnSize *ret) {
+ START_COM_CALL;
+
+ @autoreleasepool {
+ if (ret == nullptr)
+ return E_POINTER;
+
+ ret->Width = lastSize.width;
+ ret->Height = lastSize.height;
+
+ return S_OK;
+ }
+}
+
+HRESULT WindowBaseImpl::GetFrameSize(AvnSize *ret) {
+ START_COM_CALL;
+
+ @autoreleasepool {
+ if (ret == nullptr)
+ return E_POINTER;
+
+ if(Window != nullptr){
+ auto frame = [Window frame];
+ ret->Width = frame.size.width;
+ ret->Height = frame.size.height;
+ }
+
+ return S_OK;
+ }
+}
+
+HRESULT WindowBaseImpl::GetScaling(double *ret) {
+ START_COM_CALL;
+
+ @autoreleasepool {
+ if (ret == nullptr)
+ return E_POINTER;
+
+ if (Window == nullptr) {
+ *ret = 1;
+ return S_OK;
+ }
+
+ *ret = [Window backingScaleFactor];
+ return S_OK;
+ }
+}
+
+HRESULT WindowBaseImpl::SetMinMaxSize(AvnSize minSize, AvnSize maxSize) {
+ START_COM_CALL;
+
+ @autoreleasepool {
+ lastMinSize = ToNSSize(minSize);
+ lastMaxSize = ToNSSize(maxSize);
+
+ if(Window != nullptr) {
+ [Window setContentMinSize:lastMinSize];
+ [Window setContentMaxSize:lastMaxSize];
+ }
+
+ return S_OK;
+ }
+}
+
+HRESULT WindowBaseImpl::Resize(double x, double y, AvnPlatformResizeReason reason) {
+ if (_inResize) {
+ return S_OK;
+ }
+
+ _inResize = true;
+
+ START_COM_CALL;
+ auto resizeBlock = ResizeScope(View, reason);
+
+ @autoreleasepool {
+ auto maxSize = lastMaxSize;
+ auto minSize = lastMinSize;
+
+ if (x < minSize.width) {
+ x = minSize.width;
+ }
+
+ if (y < minSize.height) {
+ y = minSize.height;
+ }
+
+ if (x > maxSize.width) {
+ x = maxSize.width;
+ }
+
+ if (y > maxSize.height) {
+ y = maxSize.height;
+ }
+
+ @try {
+ lastSize = NSSize {x, y};
+
+ if (!_shown) {
+ BaseEvents->Resized(AvnSize{x, y}, reason);
+ }
+
+ if(Window != nullptr) {
+ [Window setContentSize:lastSize];
+ [Window invalidateShadow];
+ }
+ }
+ @finally {
+ _inResize = false;
+ }
+
+ return S_OK;
+ }
+}
+
+HRESULT WindowBaseImpl::Invalidate(__attribute__((unused)) AvnRect rect) {
+ START_COM_CALL;
+
+ @autoreleasepool {
+ [View setNeedsDisplayInRect:[View frame]];
+
+ return S_OK;
+ }
+}
+
+HRESULT WindowBaseImpl::SetMainMenu(IAvnMenu *menu) {
+ START_COM_CALL;
+
+ auto nativeMenu = dynamic_cast(menu);
+
+ lastMenu = nativeMenu->GetNative();
+
+ if(Window != nullptr) {
+ [GetWindowProtocol() applyMenu:lastMenu];
+
+ if ([Window isKeyWindow]) {
+ [GetWindowProtocol() showWindowMenuWithAppMenu];
+ }
+ }
+
+ return S_OK;
+}
+
+HRESULT WindowBaseImpl::BeginMoveDrag() {
+ START_COM_CALL;
+
+ @autoreleasepool {
+ auto lastEvent = [View lastMouseDownEvent];
+
+ if (lastEvent == nullptr) {
+ return S_OK;
+ }
+
+ [Window performWindowDragWithEvent:lastEvent];
+
+ return S_OK;
+ }
+}
+
+HRESULT WindowBaseImpl::BeginResizeDrag(__attribute__((unused)) AvnWindowEdge edge) {
+ START_COM_CALL;
+
+ return S_OK;
+}
+
+HRESULT WindowBaseImpl::GetPosition(AvnPoint *ret) {
+ START_COM_CALL;
+
+ @autoreleasepool {
+ if (ret == nullptr) {
+ return E_POINTER;
+ }
+
+ if(Window != nullptr) {
+ auto frame = [Window frame];
+
+ ret->X = frame.origin.x;
+ ret->Y = frame.origin.y + frame.size.height;
+
+ *ret = ConvertPointY(*ret);
+ } else
+ {
+ *ret = lastPositionSet;
+ }
+
+ return S_OK;
+ }
+}
+
+HRESULT WindowBaseImpl::SetPosition(AvnPoint point) {
+ START_COM_CALL;
+
+ @autoreleasepool {
+ lastPositionSet = point;
+ hasPosition = true;
+
+ if(Window != nullptr) {
+ [Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(point))];
+ }
+
+ return S_OK;
+ }
+}
+
+HRESULT WindowBaseImpl::PointToClient(AvnPoint point, AvnPoint *ret) {
+ START_COM_CALL;
+
+ @autoreleasepool {
+ if (ret == nullptr) {
+ return E_POINTER;
+ }
+
+ point = ConvertPointY(point);
+ NSRect convertRect = [Window convertRectFromScreen:NSMakeRect(point.X, point.Y, 0.0, 0.0)];
+ auto viewPoint = NSMakePoint(convertRect.origin.x, convertRect.origin.y);
+
+ *ret = [View translateLocalPoint:ToAvnPoint(viewPoint)];
+
+ return S_OK;
+ }
+}
+
+HRESULT WindowBaseImpl::PointToScreen(AvnPoint point, AvnPoint *ret) {
+ START_COM_CALL;
+
+ @autoreleasepool {
+ if (ret == nullptr) {
+ return E_POINTER;
+ }
+
+ auto cocoaViewPoint = ToNSPoint([View translateLocalPoint:point]);
+ NSRect convertRect = [Window convertRectToScreen:NSMakeRect(cocoaViewPoint.x, cocoaViewPoint.y, 0.0, 0.0)];
+ auto cocoaScreenPoint = NSPointFromCGPoint(NSMakePoint(convertRect.origin.x, convertRect.origin.y));
+ *ret = ConvertPointY(ToAvnPoint(cocoaScreenPoint));
+
+ return S_OK;
+ }
+}
+
+HRESULT WindowBaseImpl::ThreadSafeSetSwRenderedFrame(AvnFramebuffer *fb, IUnknown *dispose) {
+ START_COM_CALL;
+
+ [View setSwRenderedFrame:fb dispose:dispose];
+ return S_OK;
+}
+
+HRESULT WindowBaseImpl::SetCursor(IAvnCursor *cursor) {
+ START_COM_CALL;
+
+ @autoreleasepool {
+ Cursor *avnCursor = dynamic_cast(cursor);
+ this->cursor = avnCursor->GetNative();
+ UpdateCursor();
+
+ if (avnCursor->IsHidden()) {
+ [NSCursor hide];
+ } else {
+ [NSCursor unhide];
+ }
+
+ return S_OK;
+ }
+}
+
+void WindowBaseImpl::UpdateCursor() {
+ if (cursor != nil) {
+ [cursor set];
+ }
+}
+
+HRESULT WindowBaseImpl::CreateGlRenderTarget(IAvnGlSurfaceRenderTarget **ppv) {
+ START_COM_CALL;
+
+ if (View == NULL)
+ return E_FAIL;
+ *ppv = [renderTarget createSurfaceRenderTarget];
+ return static_cast(*ppv == nil ? E_FAIL : S_OK);
+}
+
+HRESULT WindowBaseImpl::CreateNativeControlHost(IAvnNativeControlHost **retOut) {
+ START_COM_CALL;
+
+ if (View == NULL)
+ return E_FAIL;
+ *retOut = ::CreateNativeControlHost(View);
+ return S_OK;
+}
+
+HRESULT WindowBaseImpl::SetBlurEnabled(bool enable) {
+ START_COM_CALL;
+
+ [StandardContainer ShowBlur:enable];
+
+ return S_OK;
+}
+
+HRESULT WindowBaseImpl::BeginDragAndDropOperation(AvnDragDropEffects effects, AvnPoint point, IAvnClipboard *clipboard, IAvnDndResultCallback *cb, void *sourceHandle) {
+ START_COM_CALL;
+
+ auto item = TryGetPasteboardItem(clipboard);
+ [item setString:@"" forType:GetAvnCustomDataType()];
+ if (item == nil)
+ return E_INVALIDARG;
+ if (View == NULL)
+ return E_FAIL;
+
+ auto nsevent = [NSApp currentEvent];
+ auto nseventType = [nsevent type];
+
+ // If current event isn't a mouse one (probably due to malfunctioning user app)
+ // attempt to forge a new one
+ if (!((nseventType >= NSEventTypeLeftMouseDown && nseventType <= NSEventTypeMouseExited)
+ || (nseventType >= NSEventTypeOtherMouseDown && nseventType <= NSEventTypeOtherMouseDragged))) {
+ NSRect convertRect = [Window convertRectToScreen:NSMakeRect(point.X, point.Y, 0.0, 0.0)];
+ auto nspoint = NSMakePoint(convertRect.origin.x, convertRect.origin.y);
+ CGPoint cgpoint = NSPointToCGPoint(nspoint);
+ auto cgevent = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseDown, cgpoint, kCGMouseButtonLeft);
+ nsevent = [NSEvent eventWithCGEvent:cgevent];
+ CFRelease(cgevent);
+ }
+
+ auto dragItem = [[NSDraggingItem alloc] initWithPasteboardWriter:item];
+
+ auto dragItemImage = [NSImage imageNamed:NSImageNameMultipleDocuments];
+ NSRect dragItemRect = {(float) point.X, (float) point.Y, [dragItemImage size].width, [dragItemImage size].height};
+ [dragItem setDraggingFrame:dragItemRect contents:dragItemImage];
+
+ int op = 0;
+ int ieffects = (int) effects;
+ if ((ieffects & (int) AvnDragDropEffects::Copy) != 0)
+ op |= NSDragOperationCopy;
+ if ((ieffects & (int) AvnDragDropEffects::Link) != 0)
+ op |= NSDragOperationLink;
+ if ((ieffects & (int) AvnDragDropEffects::Move) != 0)
+ op |= NSDragOperationMove;
+ [View beginDraggingSessionWithItems:@[dragItem] event:nsevent
+ source:CreateDraggingSource((NSDragOperation) op, cb, sourceHandle)];
+ return S_OK;
+}
+
+bool WindowBaseImpl::IsDialog() {
+ return false;
+}
+
+NSWindowStyleMask WindowBaseImpl::GetStyle() {
+ return NSWindowStyleMaskBorderless;
+}
+
+void WindowBaseImpl::UpdateStyle() {
+ [Window setStyleMask:GetStyle()];
+}
+
+void WindowBaseImpl::CleanNSWindow() {
+ if(Window != nullptr) {
+ [GetWindowProtocol() disconnectParent];
+ [Window close];
+ Window = nullptr;
+ }
+}
+
+void WindowBaseImpl::CreateNSWindow(bool isDialog) {
+ if (isDialog) {
+ if (![Window isKindOfClass:[AvnPanel class]]) {
+ CleanNSWindow();
+
+ Window = [[AvnPanel alloc] initWithParent:this contentRect:NSRect{0, 0, lastSize} styleMask:GetStyle()];
+ }
+ } else {
+ if (![Window isKindOfClass:[AvnWindow class]]) {
+ CleanNSWindow();
+
+ Window = [[AvnWindow alloc] initWithParent:this contentRect:NSRect{0, 0, lastSize} styleMask:GetStyle()];
+ }
+ }
+}
+
+void WindowBaseImpl::OnInitialiseNSWindow()
+{
+
+}
+
+void WindowBaseImpl::InitialiseNSWindow() {
+ if(Window != nullptr) {
+ [Window setContentView:StandardContainer];
+ [Window setStyleMask:NSWindowStyleMaskBorderless];
+ [Window setBackingType:NSBackingStoreBuffered];
+
+ [Window setContentSize:lastSize];
+ [Window setContentMinSize:lastMinSize];
+ [Window setContentMaxSize:lastMaxSize];
+
+ [Window setOpaque:false];
+
+ [Window invalidateShadow];
+
+ if (lastMenu != nullptr) {
+ [GetWindowProtocol() applyMenu:lastMenu];
+
+ if ([Window isKeyWindow]) {
+ [GetWindowProtocol() showWindowMenuWithAppMenu];
+ }
+ }
+
+ OnInitialiseNSWindow();
+ }
+}
+
+id WindowBaseImpl::GetWindowProtocol() {
+ if(Window == nullptr)
+ {
+ return nullptr;
+ }
+
+ return (id ) Window;
+}
+
+extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events, IAvnGlContext* gl)
+{
+ @autoreleasepool
+ {
+ IAvnWindow* ptr = (IAvnWindow*)new WindowImpl(events, gl);
+ return ptr;
+ }
+}
diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.h b/native/Avalonia.Native/src/OSX/WindowImpl.h
new file mode 100644
index 0000000000..db19497b29
--- /dev/null
+++ b/native/Avalonia.Native/src/OSX/WindowImpl.h
@@ -0,0 +1,101 @@
+//
+// Created by Dan Walmsley on 04/05/2022.
+// Copyright (c) 2022 Avalonia. All rights reserved.
+//
+
+#ifndef AVALONIA_NATIVE_OSX_WINDOWIMPL_H
+#define AVALONIA_NATIVE_OSX_WINDOWIMPL_H
+
+#import "WindowBaseImpl.h"
+#include "IWindowStateChanged.h"
+
+class WindowImpl : public virtual WindowBaseImpl, public virtual IAvnWindow, public IWindowStateChanged
+{
+private:
+ bool _canResize;
+ bool _fullScreenActive;
+ SystemDecorations _decorations;
+ AvnWindowState _lastWindowState;
+ AvnWindowState _actualWindowState;
+ bool _inSetWindowState;
+ NSRect _preZoomSize;
+ bool _transitioningWindowState;
+ bool _isClientAreaExtended;
+ bool _isDialog;
+ AvnExtendClientAreaChromeHints _extendClientHints;
+
+ FORWARD_IUNKNOWN()
+BEGIN_INTERFACE_MAP()
+ INHERIT_INTERFACE_MAP(WindowBaseImpl)
+ INTERFACE_MAP_ENTRY(IAvnWindow, IID_IAvnWindow)
+ END_INTERFACE_MAP()
+ virtual ~WindowImpl()
+ {
+ }
+
+ ComPtr WindowEvents;
+
+ WindowImpl(IAvnWindowEvents* events, IAvnGlContext* gl);
+
+ void HideOrShowTrafficLights ();
+
+ virtual HRESULT Show (bool activate, bool isDialog) override;
+
+ virtual HRESULT SetEnabled (bool enable) override;
+
+ virtual HRESULT SetParent (IAvnWindow* parent) override;
+
+ void StartStateTransition () override ;
+
+ void EndStateTransition () override ;
+
+ SystemDecorations Decorations () override ;
+
+ AvnWindowState WindowState () override ;
+
+ void WindowStateChanged () override ;
+
+ bool UndecoratedIsMaximized ();
+
+ bool IsZoomed ();
+
+ void DoZoom();
+
+ virtual HRESULT SetCanResize(bool value) override;
+
+ virtual HRESULT SetDecorations(SystemDecorations value) override;
+
+ virtual HRESULT SetTitle (char* utf8title) override;
+
+ virtual HRESULT SetTitleBarColor(AvnColor color) override;
+
+ virtual HRESULT GetWindowState (AvnWindowState*ret) override;
+
+ virtual HRESULT TakeFocusFromChildren () override;
+
+ virtual HRESULT SetExtendClientArea (bool enable) override;
+
+ virtual HRESULT SetExtendClientAreaHints (AvnExtendClientAreaChromeHints hints) override;
+
+ virtual HRESULT GetExtendTitleBarHeight (double*ret) override;
+
+ virtual HRESULT SetExtendTitleBarHeight (double value) override;
+
+ void EnterFullScreenMode ();
+
+ void ExitFullScreenMode ();
+
+ virtual HRESULT SetWindowState (AvnWindowState state) override;
+
+ virtual bool IsDialog() override;
+
+ virtual void OnInitialiseNSWindow() override;
+
+protected:
+ virtual NSWindowStyleMask GetStyle() override;
+
+private:
+ NSString *_lastTitle;
+};
+
+#endif //AVALONIA_NATIVE_OSX_WINDOWIMPL_H
diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm
new file mode 100644
index 0000000000..d96fe717ab
--- /dev/null
+++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm
@@ -0,0 +1,554 @@
+//
+// Created by Dan Walmsley on 04/05/2022.
+// Copyright (c) 2022 Avalonia. All rights reserved.
+//
+
+#import
+#include "AutoFitContentView.h"
+#include "AvnView.h"
+#include "automation.h"
+#include "WindowProtocol.h"
+
+WindowImpl::WindowImpl(IAvnWindowEvents *events, IAvnGlContext *gl) : WindowBaseImpl(events, gl) {
+ _isClientAreaExtended = false;
+ _extendClientHints = AvnDefaultChrome;
+ _fullScreenActive = false;
+ _canResize = true;
+ _decorations = SystemDecorationsFull;
+ _transitioningWindowState = false;
+ _inSetWindowState = false;
+ _lastWindowState = Normal;
+ _actualWindowState = Normal;
+ _lastTitle = @"";
+ WindowEvents = events;
+}
+
+void WindowImpl::HideOrShowTrafficLights() {
+ if (Window == nil) {
+ return;
+ }
+
+ for (id subview in Window.contentView.superview.subviews) {
+ if ([subview isKindOfClass:NSClassFromString(@"NSTitlebarContainerView")]) {
+ NSView *titlebarView = [subview subviews][0];
+ for (id button in titlebarView.subviews) {
+ if ([button isKindOfClass:[NSButton class]]) {
+ if (_isClientAreaExtended) {
+ auto wantsChrome = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome);
+
+ [button setHidden:!wantsChrome];
+ } else {
+ [button setHidden:(_decorations != SystemDecorationsFull)];
+ }
+
+ [button setWantsLayer:true];
+ }
+ }
+ }
+ }
+}
+
+void WindowImpl::OnInitialiseNSWindow(){
+ [GetWindowProtocol() setCanBecomeKeyWindow:true];
+ [Window disableCursorRects];
+ [Window setTabbingMode:NSWindowTabbingModeDisallowed];
+ [Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
+
+ [Window setTitle:_lastTitle];
+
+ if(_isClientAreaExtended)
+ {
+ [GetWindowProtocol() setIsExtended:true];
+ SetExtendClientArea(true);
+ }
+}
+
+HRESULT WindowImpl::Show(bool activate, bool isDialog) {
+ START_COM_CALL;
+
+ @autoreleasepool {
+ _isDialog = isDialog;
+
+ WindowBaseImpl::Show(activate, isDialog);
+
+ HideOrShowTrafficLights();
+
+ return SetWindowState(_lastWindowState);
+ }
+}
+
+HRESULT WindowImpl::SetEnabled(bool enable) {
+ START_COM_CALL;
+
+ @autoreleasepool {
+ [GetWindowProtocol() setEnabled:enable];
+ return S_OK;
+ }
+}
+
+HRESULT WindowImpl::SetParent(IAvnWindow *parent) {
+ START_COM_CALL;
+
+ @autoreleasepool {
+ if (parent == nullptr)
+ return E_POINTER;
+
+ auto cparent = dynamic_cast(parent);
+ if (cparent == nullptr)
+ return E_INVALIDARG;
+
+ // If one tries to show a child window with a minimized parent window, then the parent window will be
+ // restored but macOS isn't kind enough to *tell* us that, so the window will be left in a non-interactive
+ // state. Detect this and explicitly restore the parent window ourselves to avoid this situation.
+ if (cparent->WindowState() == Minimized)
+ cparent->SetWindowState(Normal);
+
+ [Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary];
+ [cparent->Window addChildWindow:Window ordered:NSWindowAbove];
+
+ UpdateStyle();
+
+ return S_OK;
+ }
+}
+
+void WindowImpl::StartStateTransition() {
+ _transitioningWindowState = true;
+}
+
+void WindowImpl::EndStateTransition() {
+ _transitioningWindowState = false;
+}
+
+SystemDecorations WindowImpl::Decorations() {
+ return _decorations;
+}
+
+AvnWindowState WindowImpl::WindowState() {
+ return _lastWindowState;
+}
+
+void WindowImpl::WindowStateChanged() {
+ if (_shown && !_inSetWindowState && !_transitioningWindowState) {
+ AvnWindowState state;
+ GetWindowState(&state);
+
+ if (_lastWindowState != state) {
+ if (_isClientAreaExtended) {
+ if (_lastWindowState == FullScreen) {
+ // we exited fs.
+ if (_extendClientHints & AvnOSXThickTitleBar) {
+ Window.toolbar = [NSToolbar new];
+ Window.toolbar.showsBaselineSeparator = false;
+ }
+
+ [Window setTitlebarAppearsTransparent:true];
+
+ [StandardContainer setFrameSize:StandardContainer.frame.size];
+ } else if (state == FullScreen) {
+ // we entered fs.
+ if (_extendClientHints & AvnOSXThickTitleBar) {
+ Window.toolbar = nullptr;
+ }
+
+ [Window setTitlebarAppearsTransparent:false];
+
+ [StandardContainer setFrameSize:StandardContainer.frame.size];
+ }
+ }
+
+ _lastWindowState = state;
+ _actualWindowState = state;
+ WindowEvents->WindowStateChanged(state);
+ }
+ }
+}
+
+bool WindowImpl::UndecoratedIsMaximized() {
+ auto windowSize = [Window frame];
+ auto available = [Window screen].visibleFrame;
+ return CGRectEqualToRect(windowSize, available);
+}
+
+bool WindowImpl::IsZoomed() {
+ return _decorations == SystemDecorationsFull ? [Window isZoomed] : UndecoratedIsMaximized();
+}
+
+void WindowImpl::DoZoom() {
+ switch (_decorations) {
+ case SystemDecorationsNone:
+ case SystemDecorationsBorderOnly:
+ [Window setFrame:[Window screen].visibleFrame display:true];
+ break;
+
+
+ case SystemDecorationsFull:
+ [Window performZoom:Window];
+ break;
+ }
+}
+
+HRESULT WindowImpl::SetCanResize(bool value) {
+ START_COM_CALL;
+
+ @autoreleasepool {
+ _canResize = value;
+ UpdateStyle();
+ return S_OK;
+ }
+}
+
+HRESULT WindowImpl::SetDecorations(SystemDecorations value) {
+ START_COM_CALL;
+
+ @autoreleasepool {
+ auto currentWindowState = _lastWindowState;
+ _decorations = value;
+
+ if (_fullScreenActive) {
+ return S_OK;
+ }
+
+ UpdateStyle();
+
+ HideOrShowTrafficLights();
+
+ switch (_decorations) {
+ case SystemDecorationsNone:
+ [Window setHasShadow:NO];
+ [Window setTitleVisibility:NSWindowTitleHidden];
+ [Window setTitlebarAppearsTransparent:YES];
+
+ if (currentWindowState == Maximized) {
+ if (!UndecoratedIsMaximized()) {
+ DoZoom();
+ }
+ }
+ break;
+
+ case SystemDecorationsBorderOnly:
+ [Window setHasShadow:YES];
+ [Window setTitleVisibility:NSWindowTitleHidden];
+ [Window setTitlebarAppearsTransparent:YES];
+
+ if (currentWindowState == Maximized) {
+ if (!UndecoratedIsMaximized()) {
+ DoZoom();
+ }
+ }
+ break;
+
+ case SystemDecorationsFull:
+ [Window setHasShadow:YES];
+ [Window setTitleVisibility:NSWindowTitleVisible];
+ [Window setTitlebarAppearsTransparent:NO];
+ [Window setTitle:_lastTitle];
+
+ if (currentWindowState == Maximized) {
+ auto newFrame = [Window contentRectForFrameRect:[Window frame]].size;
+
+ [View setFrameSize:newFrame];
+ }
+ break;
+ }
+
+ return S_OK;
+ }
+}
+
+HRESULT WindowImpl::SetTitle(char *utf8title) {
+ START_COM_CALL;
+
+ @autoreleasepool {
+ _lastTitle = [NSString stringWithUTF8String:(const char *) utf8title];
+ [Window setTitle:_lastTitle];
+
+ return S_OK;
+ }
+}
+
+HRESULT WindowImpl::SetTitleBarColor(AvnColor color) {
+ START_COM_CALL;
+
+ @autoreleasepool {
+ float a = (float) color.Alpha / 255.0f;
+ float r = (float) color.Red / 255.0f;
+ float g = (float) color.Green / 255.0f;
+ float b = (float) color.Blue / 255.0f;
+
+ auto nscolor = [NSColor colorWithSRGBRed:r green:g blue:b alpha:a];
+
+ // Based on the titlebar color we have to choose either light or dark
+ // OSX doesnt let you set a foreground color for titlebar.
+ if ((r * 0.299 + g * 0.587 + b * 0.114) > 186.0f / 255.0f) {
+ [Window setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameVibrantLight]];
+ } else {
+ [Window setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameVibrantDark]];
+ }
+
+ [Window setTitlebarAppearsTransparent:true];
+ [Window setBackgroundColor:nscolor];
+ }
+
+ return S_OK;
+}
+
+HRESULT WindowImpl::GetWindowState(AvnWindowState *ret) {
+ START_COM_CALL;
+
+ @autoreleasepool {
+ if (ret == nullptr) {
+ return E_POINTER;
+ }
+
+ if (([Window styleMask] & NSWindowStyleMaskFullScreen) == NSWindowStyleMaskFullScreen) {
+ *ret = FullScreen;
+ return S_OK;
+ }
+
+ if ([Window isMiniaturized]) {
+ *ret = Minimized;
+ return S_OK;
+ }
+
+ if (IsZoomed()) {
+ *ret = Maximized;
+ return S_OK;
+ }
+
+ *ret = Normal;
+
+ return S_OK;
+ }
+}
+
+HRESULT WindowImpl::TakeFocusFromChildren() {
+ START_COM_CALL;
+
+ @autoreleasepool {
+ if (Window == nil)
+ return S_OK;
+ if ([Window isKeyWindow])
+ [Window makeFirstResponder:View];
+
+ return S_OK;
+ }
+}
+
+HRESULT WindowImpl::SetExtendClientArea(bool enable) {
+ START_COM_CALL;
+
+ @autoreleasepool {
+ _isClientAreaExtended = enable;
+
+ if(Window != nullptr) {
+ if (enable) {
+ Window.titleVisibility = NSWindowTitleHidden;
+
+ [Window setTitlebarAppearsTransparent:true];
+
+ auto wantsTitleBar = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome);
+
+ if (wantsTitleBar) {
+ [StandardContainer ShowTitleBar:true];
+ } else {
+ [StandardContainer ShowTitleBar:false];
+ }
+
+ if (_extendClientHints & AvnOSXThickTitleBar) {
+ Window.toolbar = [NSToolbar new];
+ Window.toolbar.showsBaselineSeparator = false;
+ } else {
+ Window.toolbar = nullptr;
+ }
+ } else {
+ Window.titleVisibility = NSWindowTitleVisible;
+ Window.toolbar = nullptr;
+ [Window setTitlebarAppearsTransparent:false];
+ View.layer.zPosition = 0;
+ }
+
+ [GetWindowProtocol() setIsExtended:enable];
+
+ HideOrShowTrafficLights();
+
+ UpdateStyle();
+ }
+
+ return S_OK;
+ }
+}
+
+HRESULT WindowImpl::SetExtendClientAreaHints(AvnExtendClientAreaChromeHints hints) {
+ START_COM_CALL;
+
+ @autoreleasepool {
+ _extendClientHints = hints;
+
+ SetExtendClientArea(_isClientAreaExtended);
+ return S_OK;
+ }
+}
+
+HRESULT WindowImpl::GetExtendTitleBarHeight(double *ret) {
+ START_COM_CALL;
+
+ @autoreleasepool {
+ if (ret == nullptr) {
+ return E_POINTER;
+ }
+
+ *ret = [GetWindowProtocol() getExtendedTitleBarHeight];
+
+ return S_OK;
+ }
+}
+
+HRESULT WindowImpl::SetExtendTitleBarHeight(double value) {
+ START_COM_CALL;
+
+ @autoreleasepool {
+ [StandardContainer SetTitleBarHeightHint:value];
+ return S_OK;
+ }
+}
+
+void WindowImpl::EnterFullScreenMode() {
+ _fullScreenActive = true;
+
+ [Window setTitle:_lastTitle];
+ [Window toggleFullScreen:nullptr];
+}
+
+void WindowImpl::ExitFullScreenMode() {
+ [Window toggleFullScreen:nullptr];
+
+ _fullScreenActive = false;
+
+ SetDecorations(_decorations);
+}
+
+HRESULT WindowImpl::SetWindowState(AvnWindowState state) {
+ START_COM_CALL;
+
+ @autoreleasepool {
+ auto currentState = _actualWindowState;
+ _lastWindowState = state;
+
+ if (Window == nullptr) {
+ return S_OK;
+ }
+
+ if (_actualWindowState == state) {
+ return S_OK;
+ }
+
+ _inSetWindowState = true;
+
+ if (currentState == Normal) {
+ _preZoomSize = [Window frame];
+ }
+
+ if (_shown) {
+ switch (state) {
+ case Maximized:
+ if (currentState == FullScreen) {
+ ExitFullScreenMode();
+ }
+
+ lastPositionSet.X = 0;
+ lastPositionSet.Y = 0;
+
+ if ([Window isMiniaturized]) {
+ [Window deminiaturize:Window];
+ }
+
+ if (!IsZoomed()) {
+ DoZoom();
+ }
+ break;
+
+ case Minimized:
+ if (currentState == FullScreen) {
+ ExitFullScreenMode();
+ } else {
+ [Window miniaturize:Window];
+ }
+ break;
+
+ case FullScreen:
+ if ([Window isMiniaturized]) {
+ [Window deminiaturize:Window];
+ }
+
+ EnterFullScreenMode();
+ break;
+
+ case Normal:
+ if ([Window isMiniaturized]) {
+ [Window deminiaturize:Window];
+ }
+
+ if (currentState == FullScreen) {
+ ExitFullScreenMode();
+ }
+
+ if (IsZoomed()) {
+ if (_decorations == SystemDecorationsFull) {
+ DoZoom();
+ } else {
+ [Window setFrame:_preZoomSize display:true];
+ auto newFrame = [Window contentRectForFrameRect:[Window frame]].size;
+
+ [View setFrameSize:newFrame];
+ }
+
+ }
+ break;
+ }
+
+ _actualWindowState = _lastWindowState;
+ WindowEvents->WindowStateChanged(_actualWindowState);
+ }
+
+
+ _inSetWindowState = false;
+
+ return S_OK;
+ }
+}
+
+bool WindowImpl::IsDialog() {
+ return _isDialog;
+}
+
+NSWindowStyleMask WindowImpl::GetStyle() {
+ unsigned long s = this->_isDialog ? NSWindowStyleMaskDocModalWindow : NSWindowStyleMaskBorderless;
+
+ switch (_decorations) {
+ case SystemDecorationsNone:
+ s = s | NSWindowStyleMaskFullSizeContentView;
+ break;
+
+ case SystemDecorationsBorderOnly:
+ s = s | NSWindowStyleMaskTitled | NSWindowStyleMaskFullSizeContentView;
+ break;
+
+ case SystemDecorationsFull:
+ s = s | NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskBorderless;
+
+ if (_canResize) {
+ s = s | NSWindowStyleMaskResizable;
+ }
+ break;
+ }
+
+ if ([Window parentWindow] == nullptr) {
+ s |= NSWindowStyleMaskMiniaturizable;
+ }
+
+ if (_isClientAreaExtended) {
+ s |= NSWindowStyleMaskFullSizeContentView | NSWindowStyleMaskTexturedBackground;
+ }
+ return s;
+}
diff --git a/native/Avalonia.Native/src/OSX/WindowInterfaces.h b/native/Avalonia.Native/src/OSX/WindowInterfaces.h
new file mode 100644
index 0000000000..6e6d62e85e
--- /dev/null
+++ b/native/Avalonia.Native/src/OSX/WindowInterfaces.h
@@ -0,0 +1,17 @@
+//
+// Created by Dan Walmsley on 06/05/2022.
+// Copyright (c) 2022 Avalonia. All rights reserved.
+//
+
+#import
+#import
+#include "WindowProtocol.h"
+#include "WindowBaseImpl.h"
+
+@interface AvnWindow : NSWindow
+-(AvnWindow* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask;
+@end
+
+@interface AvnPanel : NSPanel
+-(AvnPanel* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask;
+@end
\ No newline at end of file
diff --git a/native/Avalonia.Native/src/OSX/WindowProtocol.h b/native/Avalonia.Native/src/OSX/WindowProtocol.h
new file mode 100644
index 0000000000..0e5c5869e7
--- /dev/null
+++ b/native/Avalonia.Native/src/OSX/WindowProtocol.h
@@ -0,0 +1,27 @@
+//
+// Created by Dan Walmsley on 06/05/2022.
+// Copyright (c) 2022 Avalonia. All rights reserved.
+//
+
+#pragma once
+
+#import
+
+@class AvnMenu;
+
+@protocol AvnWindowProtocol
+-(void) pollModalSession: (NSModalSession _Nonnull) session;
+-(void) restoreParentWindow;
+-(bool) shouldTryToHandleEvents;
+-(void) setEnabled: (bool) enable;
+-(void) showAppMenuOnly;
+-(void) showWindowMenuWithAppMenu;
+-(void) applyMenu:(AvnMenu* _Nullable)menu;
+
+-(double) getExtendedTitleBarHeight;
+-(void) setIsExtended:(bool)value;
+-(void) disconnectParent;
+-(bool) isDialog;
+
+-(void) setCanBecomeKeyWindow:(bool)value;
+@end
diff --git a/native/Avalonia.Native/src/OSX/app.mm b/native/Avalonia.Native/src/OSX/app.mm
index 79175d9ff1..14f1f6888c 100644
--- a/native/Avalonia.Native/src/OSX/app.mm
+++ b/native/Avalonia.Native/src/OSX/app.mm
@@ -73,22 +73,15 @@ ComPtr _events;
_isHandlingSendEvent = true;
@try {
[super sendEvent: event];
+ if ([event type] == NSEventTypeKeyUp && ([event modifierFlags] & NSEventModifierFlagCommand))
+ {
+ [[self keyWindow] sendEvent:event];
+ }
+
} @finally {
_isHandlingSendEvent = oldHandling;
}
}
-
-// This is needed for certain embedded controls
-- (BOOL) isHandlingSendEvent
-{
- return _isHandlingSendEvent;
-}
-
-- (void)setHandlingSendEvent:(BOOL)handlingSendEvent
-{
- _isHandlingSendEvent = handlingSendEvent;
-}
-
@end
extern void InitializeAvnApp(IAvnApplicationEvents* events)
diff --git a/native/Avalonia.Native/src/OSX/automation.h b/native/Avalonia.Native/src/OSX/automation.h
new file mode 100644
index 0000000000..367df3619d
--- /dev/null
+++ b/native/Avalonia.Native/src/OSX/automation.h
@@ -0,0 +1,12 @@
+#pragma once
+
+#import
+NS_ASSUME_NONNULL_BEGIN
+
+class IAvnAutomationPeer;
+
+@interface AvnAccessibilityElement : NSAccessibilityElement
++ (AvnAccessibilityElement *) acquire:(IAvnAutomationPeer *) peer;
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/native/Avalonia.Native/src/OSX/automation.mm b/native/Avalonia.Native/src/OSX/automation.mm
new file mode 100644
index 0000000000..d0c8d7a9db
--- /dev/null
+++ b/native/Avalonia.Native/src/OSX/automation.mm
@@ -0,0 +1,497 @@
+#include "common.h"
+#include "automation.h"
+#include "AvnString.h"
+#include "INSWindowHolder.h"
+#include "AvnView.h"
+
+@interface AvnAccessibilityElement (Events)
+- (void) raiseChildrenChanged;
+@end
+
+@interface AvnRootAccessibilityElement : AvnAccessibilityElement
+- (AvnView *) ownerView;
+- (AvnRootAccessibilityElement *) initWithPeer:(IAvnAutomationPeer *) peer owner:(AvnView*) owner;
+- (void) raiseFocusChanged;
+@end
+
+class AutomationNode : public ComSingleObject
+{
+public:
+ FORWARD_IUNKNOWN()
+
+ AutomationNode(AvnAccessibilityElement* owner)
+ {
+ _owner = owner;
+ }
+
+ AvnAccessibilityElement* GetOwner()
+ {
+ return _owner;
+ }
+
+ virtual void Dispose() override
+ {
+ _owner = nil;
+ }
+
+ virtual void ChildrenChanged () override
+ {
+ [_owner raiseChildrenChanged];
+ }
+
+ virtual void PropertyChanged (AvnAutomationProperty property) override
+ {
+
+ }
+
+ virtual void FocusChanged () override
+ {
+ [(AvnRootAccessibilityElement*)_owner raiseFocusChanged];
+ }
+
+private:
+ __strong AvnAccessibilityElement* _owner;
+};
+
+@implementation AvnAccessibilityElement
+{
+ IAvnAutomationPeer* _peer;
+ AutomationNode* _node;
+ NSMutableArray* _children;
+}
+
++ (AvnAccessibilityElement *)acquire:(IAvnAutomationPeer *)peer
+{
+ if (peer == nullptr)
+ return nil;
+
+ auto instance = peer->GetNode();
+
+ if (instance != nullptr)
+ return dynamic_cast(instance)->GetOwner();
+
+ if (peer->IsRootProvider())
+ {
+ auto window = peer->RootProvider_GetWindow();
+ auto holder = dynamic_cast(window);
+ auto view = holder->GetNSView();
+ return [[AvnRootAccessibilityElement alloc] initWithPeer:peer owner:view];
+ }
+ else
+ {
+ return [[AvnAccessibilityElement alloc] initWithPeer:peer];
+ }
+}
+
+- (AvnAccessibilityElement *)initWithPeer:(IAvnAutomationPeer *)peer
+{
+ self = [super init];
+ _peer = peer;
+ _node = new AutomationNode(self);
+ _peer->SetNode(_node);
+ return self;
+}
+
+- (void)dealloc
+{
+ if (_node)
+ delete _node;
+ _node = nullptr;
+}
+
+- (NSString *)description
+{
+ return [NSString stringWithFormat:@"%@ '%@' (%p)",
+ GetNSStringAndRelease(_peer->GetClassName()),
+ GetNSStringAndRelease(_peer->GetName()),
+ _peer];
+}
+
+- (IAvnAutomationPeer *)peer
+{
+ return _peer;
+}
+
+- (BOOL)isAccessibilityElement
+{
+ return _peer->IsControlElement();
+}
+
+- (NSAccessibilityRole)accessibilityRole
+{
+ auto controlType = _peer->GetAutomationControlType();
+
+ switch (controlType) {
+ case AutomationButton: return NSAccessibilityButtonRole;
+ case AutomationCalendar: return NSAccessibilityGridRole;
+ case AutomationCheckBox: return NSAccessibilityCheckBoxRole;
+ case AutomationComboBox: return NSAccessibilityPopUpButtonRole;
+ case AutomationComboBoxItem: return NSAccessibilityMenuItemRole;
+ case AutomationEdit: return NSAccessibilityTextFieldRole;
+ case AutomationHyperlink: return NSAccessibilityLinkRole;
+ case AutomationImage: return NSAccessibilityImageRole;
+ case AutomationListItem: return NSAccessibilityRowRole;
+ case AutomationList: return NSAccessibilityTableRole;
+ case AutomationMenu: return NSAccessibilityMenuBarRole;
+ case AutomationMenuBar: return NSAccessibilityMenuBarRole;
+ case AutomationMenuItem: return NSAccessibilityMenuItemRole;
+ case AutomationProgressBar: return NSAccessibilityProgressIndicatorRole;
+ case AutomationRadioButton: return NSAccessibilityRadioButtonRole;
+ case AutomationScrollBar: return NSAccessibilityScrollBarRole;
+ case AutomationSlider: return NSAccessibilitySliderRole;
+ case AutomationSpinner: return NSAccessibilityIncrementorRole;
+ case AutomationStatusBar: return NSAccessibilityTableRole;
+ case AutomationTab: return NSAccessibilityTabGroupRole;
+ case AutomationTabItem: return NSAccessibilityRadioButtonRole;
+ case AutomationText: return NSAccessibilityStaticTextRole;
+ case AutomationToolBar: return NSAccessibilityToolbarRole;
+ case AutomationToolTip: return NSAccessibilityPopoverRole;
+ case AutomationTree: return NSAccessibilityOutlineRole;
+ case AutomationTreeItem: return NSAccessibilityCellRole;
+ case AutomationCustom: return NSAccessibilityUnknownRole;
+ case AutomationGroup: return NSAccessibilityGroupRole;
+ case AutomationThumb: return NSAccessibilityHandleRole;
+ case AutomationDataGrid: return NSAccessibilityGridRole;
+ case AutomationDataItem: return NSAccessibilityCellRole;
+ case AutomationDocument: return NSAccessibilityStaticTextRole;
+ case AutomationSplitButton: return NSAccessibilityPopUpButtonRole;
+ case AutomationWindow: return NSAccessibilityWindowRole;
+ case AutomationPane: return NSAccessibilityGroupRole;
+ case AutomationHeader: return NSAccessibilityGroupRole;
+ case AutomationHeaderItem: return NSAccessibilityButtonRole;
+ case AutomationTable: return NSAccessibilityTableRole;
+ case AutomationTitleBar: return NSAccessibilityGroupRole;
+ // Treat unknown roles as generic group container items. Returning
+ // NSAccessibilityUnknownRole is also possible but makes the screen
+ // reader focus on the item instead of passing focus to child items.
+ default: return NSAccessibilityGroupRole;
+ }
+}
+
+- (NSString *)accessibilityIdentifier
+{
+ return GetNSStringAndRelease(_peer->GetAutomationId());
+}
+
+- (NSString *)accessibilityTitle
+{
+ // StaticText exposes its text via the value property.
+ if (_peer->GetAutomationControlType() != AutomationText)
+ {
+ return GetNSStringAndRelease(_peer->GetName());
+ }
+
+ return [super accessibilityTitle];
+}
+
+- (id)accessibilityValue
+{
+ if (_peer->IsRangeValueProvider())
+ {
+ return [NSNumber numberWithDouble:_peer->RangeValueProvider_GetValue()];
+ }
+ else if (_peer->IsToggleProvider())
+ {
+ switch (_peer->ToggleProvider_GetToggleState()) {
+ case 0: return [NSNumber numberWithBool:NO];
+ case 1: return [NSNumber numberWithBool:YES];
+ default: return [NSNumber numberWithInt:2];
+ }
+ }
+ else if (_peer->IsValueProvider())
+ {
+ return GetNSStringAndRelease(_peer->ValueProvider_GetValue());
+ }
+ else if (_peer->GetAutomationControlType() == AutomationText)
+ {
+ return GetNSStringAndRelease(_peer->GetName());
+ }
+
+ return [super accessibilityValue];
+}
+
+- (id)accessibilityMinValue
+{
+ if (_peer->IsRangeValueProvider())
+ {
+ return [NSNumber numberWithDouble:_peer->RangeValueProvider_GetMinimum()];
+ }
+
+ return [super accessibilityMinValue];
+}
+
+- (id)accessibilityMaxValue
+{
+ if (_peer->IsRangeValueProvider())
+ {
+ return [NSNumber numberWithDouble:_peer->RangeValueProvider_GetMaximum()];
+ }
+
+ return [super accessibilityMaxValue];
+}
+
+- (BOOL)isAccessibilityEnabled
+{
+ return _peer->IsEnabled();
+}
+
+- (BOOL)isAccessibilityFocused
+{
+ return _peer->HasKeyboardFocus();
+}
+
+- (NSArray *)accessibilityChildren
+{
+ if (_children == nullptr && _peer != nullptr)
+ [self recalculateChildren];
+ return _children;
+}
+
+- (NSRect)accessibilityFrame
+{
+ id topLevel = [self accessibilityTopLevelUIElement];
+ auto result = NSZeroRect;
+
+ if ([topLevel isKindOfClass:[AvnRootAccessibilityElement class]])
+ {
+ auto root = (AvnRootAccessibilityElement*)topLevel;
+ auto view = [root ownerView];
+
+ if (view)
+ {
+ auto window = [view window];
+ auto bounds = ToNSRect(_peer->GetBoundingRectangle());
+ auto windowBounds = [view convertRect:bounds toView:nil];
+ auto screenBounds = [window convertRectToScreen:windowBounds];
+ result = screenBounds;
+ }
+ }
+
+ return result;
+}
+
+- (id)accessibilityParent
+{
+ auto parentPeer = _peer->GetParent();
+ return parentPeer ? [AvnAccessibilityElement acquire:parentPeer] : [NSApplication sharedApplication];
+}
+
+- (id)accessibilityTopLevelUIElement
+{
+ auto rootPeer = _peer->GetRootPeer();
+ return [AvnAccessibilityElement acquire:rootPeer];
+}
+
+- (id)accessibilityWindow
+{
+ id topLevel = [self accessibilityTopLevelUIElement];
+ return [topLevel isKindOfClass:[NSWindow class]] ? topLevel : nil;
+}
+
+- (BOOL)isAccessibilityExpanded
+{
+ if (!_peer->IsExpandCollapseProvider())
+ return NO;
+ return _peer->ExpandCollapseProvider_GetIsExpanded();
+}
+
+- (void)setAccessibilityExpanded:(BOOL)accessibilityExpanded
+{
+ if (!_peer->IsExpandCollapseProvider())
+ return;
+ if (accessibilityExpanded)
+ _peer->ExpandCollapseProvider_Expand();
+ else
+ _peer->ExpandCollapseProvider_Collapse();
+}
+
+- (BOOL)accessibilityPerformPress
+{
+ if (_peer->IsInvokeProvider())
+ {
+ _peer->InvokeProvider_Invoke();
+ }
+ else if (_peer->IsExpandCollapseProvider())
+ {
+ _peer->ExpandCollapseProvider_Expand();
+ }
+ else if (_peer->IsToggleProvider())
+ {
+ _peer->ToggleProvider_Toggle();
+ }
+ return YES;
+}
+
+- (BOOL)accessibilityPerformIncrement
+{
+ if (!_peer->IsRangeValueProvider())
+ return NO;
+ auto value = _peer->RangeValueProvider_GetValue();
+ value += _peer->RangeValueProvider_GetSmallChange();
+ _peer->RangeValueProvider_SetValue(value);
+ return YES;
+}
+
+- (BOOL)accessibilityPerformDecrement
+{
+ if (!_peer->IsRangeValueProvider())
+ return NO;
+ auto value = _peer->RangeValueProvider_GetValue();
+ value -= _peer->RangeValueProvider_GetSmallChange();
+ _peer->RangeValueProvider_SetValue(value);
+ return YES;
+}
+
+- (BOOL)accessibilityPerformShowMenu
+{
+ if (!_peer->IsExpandCollapseProvider())
+ return NO;
+ _peer->ExpandCollapseProvider_Expand();
+ return YES;
+}
+
+- (BOOL)isAccessibilitySelected
+{
+ if (_peer->IsSelectionItemProvider())
+ return _peer->SelectionItemProvider_IsSelected();
+ return NO;
+}
+
+- (BOOL)isAccessibilitySelectorAllowed:(SEL)selector
+{
+ if (selector == @selector(accessibilityPerformShowMenu))
+ {
+ return _peer->IsExpandCollapseProvider() && _peer->ExpandCollapseProvider_GetShowsMenu();
+ }
+ else if (selector == @selector(isAccessibilityExpanded))
+ {
+ return _peer->IsExpandCollapseProvider();
+ }
+ else if (selector == @selector(accessibilityPerformPress))
+ {
+ return _peer->IsInvokeProvider() || _peer->IsExpandCollapseProvider() || _peer->IsToggleProvider();
+ }
+ else if (selector == @selector(accessibilityPerformIncrement) ||
+ selector == @selector(accessibilityPerformDecrement) ||
+ selector == @selector(accessibilityMinValue) ||
+ selector == @selector(accessibilityMaxValue))
+ {
+ return _peer->IsRangeValueProvider();
+ }
+
+ return [super isAccessibilitySelectorAllowed:selector];
+}
+
+- (void)raiseChildrenChanged
+{
+ auto changed = _children ? [NSMutableSet setWithArray:_children] : [NSMutableSet set];
+
+ [self recalculateChildren];
+
+ if (_children)
+ [changed addObjectsFromArray:_children];
+
+ NSAccessibilityPostNotificationWithUserInfo(
+ self,
+ NSAccessibilityLayoutChangedNotification,
+ @{ NSAccessibilityUIElementsKey: [changed allObjects]});
+}
+
+- (void)raisePropertyChanged
+{
+}
+
+- (void)setAccessibilityFocused:(BOOL)accessibilityFocused
+{
+ if (accessibilityFocused)
+ _peer->SetFocus();
+}
+
+- (void)recalculateChildren
+{
+ auto childPeers = _peer->GetChildren();
+ auto childCount = childPeers != nullptr ? childPeers->GetCount() : 0;
+
+ if (childCount > 0)
+ {
+ _children = [[NSMutableArray alloc] initWithCapacity:childCount];
+
+ for (int i = 0; i < childCount; ++i)
+ {
+ IAvnAutomationPeer* child;
+
+ if (childPeers->Get(i, &child) == S_OK)
+ {
+ auto element = [AvnAccessibilityElement acquire:child];
+ [_children addObject:element];
+ }
+ }
+ }
+ else
+ {
+ _children = nil;
+ }
+}
+
+@end
+
+@implementation AvnRootAccessibilityElement
+{
+ AvnView* _owner;
+}
+
+- (AvnRootAccessibilityElement *)initWithPeer:(IAvnAutomationPeer *)peer owner:(AvnView *)owner
+{
+ self = [super initWithPeer:peer];
+ _owner = owner;
+
+ // Seems we need to raise a focus changed notification here if we have focus
+ auto focusedPeer = [self peer]->RootProvider_GetFocus();
+ id focused = [AvnAccessibilityElement acquire:focusedPeer];
+
+ if (focused)
+ NSAccessibilityPostNotification(focused, NSAccessibilityFocusedUIElementChangedNotification);
+
+ return self;
+}
+
+- (AvnView *)ownerView
+{
+ return _owner;
+}
+
+- (id)accessibilityFocusedUIElement
+{
+ auto focusedPeer = [self peer]->RootProvider_GetFocus();
+ return [AvnAccessibilityElement acquire:focusedPeer];
+}
+
+- (id)accessibilityHitTest:(NSPoint)point
+{
+ auto clientPoint = [[_owner window] convertPointFromScreen:point];
+ auto localPoint = [_owner translateLocalPoint:ToAvnPoint(clientPoint)];
+ auto hit = [self peer]->RootProvider_GetPeerFromPoint(localPoint);
+ return [AvnAccessibilityElement acquire:hit];
+}
+
+- (id)accessibilityParent
+{
+ return _owner;
+}
+
+- (void)raiseFocusChanged
+{
+ id focused = [self accessibilityFocusedUIElement];
+ NSAccessibilityPostNotification(focused, NSAccessibilityFocusedUIElementChangedNotification);
+}
+
+// Although this method is marked as deprecated we get runtime warnings if we don't handle it.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-implementations"
+- (void)accessibilityPerformAction:(NSAccessibilityActionName)action
+{
+ [_owner accessibilityPerformAction:action];
+}
+#pragma clang diagnostic pop
+
+@end
diff --git a/native/Avalonia.Native/src/OSX/common.h b/native/Avalonia.Native/src/OSX/common.h
index 126c9aa87b..a90a235b9d 100644
--- a/native/Avalonia.Native/src/OSX/common.h
+++ b/native/Avalonia.Native/src/OSX/common.h
@@ -27,7 +27,7 @@ extern IAvnMenuItem* CreateAppMenuItem();
extern IAvnMenuItem* CreateAppMenuItemSeparator();
extern IAvnApplicationCommands* CreateApplicationCommands();
extern IAvnNativeControlHost* CreateNativeControlHost(NSView* parent);
-extern void SetAppMenu (NSString* appName, IAvnMenu* appMenu);
+extern void SetAppMenu(IAvnMenu *menu);
extern void SetServicesMenu (IAvnMenu* menu);
extern IAvnMenu* GetAppMenu ();
extern NSMenuItem* GetAppMenuItem ();
@@ -35,9 +35,9 @@ extern NSMenuItem* GetAppMenuItem ();
extern void InitializeAvnApp(IAvnApplicationEvents* events);
extern NSApplicationActivationPolicy AvnDesiredActivationPolicy;
extern NSPoint ToNSPoint (AvnPoint p);
+extern NSRect ToNSRect (AvnRect r);
extern AvnPoint ToAvnPoint (NSPoint p);
extern AvnPoint ConvertPointY (AvnPoint p);
-extern CGFloat PrimaryDisplayHeight();
extern NSSize ToNSSize (AvnSize s);
#ifdef DEBUG
#define NSDebugLog(...) NSLog(__VA_ARGS__)
diff --git a/native/Avalonia.Native/src/OSX/controlhost.mm b/native/Avalonia.Native/src/OSX/controlhost.mm
index f8e9a3b6d1..5683a5a975 100644
--- a/native/Avalonia.Native/src/OSX/controlhost.mm
+++ b/native/Avalonia.Native/src/OSX/controlhost.mm
@@ -36,7 +36,10 @@ public:
virtual void DestroyDefaultChild(void* child) override
{
// ARC will release the object for us
+ #pragma clang diagnostic push
+ #pragma clang diagnostic ignored "-Wunused-value"
(__bridge_transfer NSView*) child;
+ #pragma clang diagnostic pop
}
};
diff --git a/native/Avalonia.Native/src/OSX/cursor.mm b/native/Avalonia.Native/src/OSX/cursor.mm
index dc38294a18..8638a03531 100644
--- a/native/Avalonia.Native/src/OSX/cursor.mm
+++ b/native/Avalonia.Native/src/OSX/cursor.mm
@@ -1,6 +1,5 @@
#include "common.h"
#include "cursor.h"
-#include
-
+
-
diff --git a/nukebuild/numerge.config b/nukebuild/numerge.config
index e4e15d693d..d1c0408241 100644
--- a/nukebuild/numerge.config
+++ b/nukebuild/numerge.config
@@ -11,11 +11,6 @@
"Id": "Avalonia.Build.Tasks",
"IgnoreMissingFrameworkBinaries": true,
"DoNotMergeDependencies": true
- },
- {
- "Id": "Avalonia.DesktopRuntime",
- "IgnoreMissingFrameworkBinaries": true,
- "IgnoreMissingFrameworkDependencies": true
}
]
}
diff --git a/packages/Avalonia/Avalonia.csproj b/packages/Avalonia/Avalonia.csproj
index 4b28527465..4d0ed866a3 100644
--- a/packages/Avalonia/Avalonia.csproj
+++ b/packages/Avalonia/Avalonia.csproj
@@ -8,7 +8,9 @@
all
-
+ true
+ TargetFramework=netstandard2.0
+
diff --git a/readme.md b/readme.md
index 96c7937559..1cdaf3b8f8 100644
--- a/readme.md
+++ b/readme.md
@@ -1,11 +1,11 @@
[](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
-Avalonia is a cross-platform UI framework for dotnet, providing a flexible styling system and supporting a wide range of Operating Systems such as Windows, Linux, MacOs. Avalonia is mature and production ready. We also have in beta release support for iOS, Android and in early stages support for browser via WASM.
+Avalonia is a cross-platform UI framework for dotnet, providing a flexible styling system and supporting a wide range of Operating Systems such as Windows, Linux, macOS. Avalonia is mature and production ready. We also have in beta release support for iOS, Android and in early stages support for browser via WASM.

diff --git a/samples/BindingDemo/BindingDemo.csproj b/samples/BindingDemo/BindingDemo.csproj
index 2c6ff74e5e..bd6054327f 100644
--- a/samples/BindingDemo/BindingDemo.csproj
+++ b/samples/BindingDemo/BindingDemo.csproj
@@ -5,6 +5,7 @@
+
diff --git a/samples/ControlCatalog.Android/ControlCatalog.Android.csproj b/samples/ControlCatalog.Android/ControlCatalog.Android.csproj
index 1654d20c80..04c67e84e8 100644
--- a/samples/ControlCatalog.Android/ControlCatalog.Android.csproj
+++ b/samples/ControlCatalog.Android/ControlCatalog.Android.csproj
@@ -23,10 +23,25 @@
True
True
True
+ True
-
+
+
+ False
+ False
+
+
+
+ True
+
+
+
+
+
+
+
-
+
\ No newline at end of file
diff --git a/samples/ControlCatalog.Android/MainActivity.cs b/samples/ControlCatalog.Android/MainActivity.cs
index 137f280e43..44290d9816 100644
--- a/samples/ControlCatalog.Android/MainActivity.cs
+++ b/samples/ControlCatalog.Android/MainActivity.cs
@@ -1,19 +1,16 @@
using Android.App;
-using Android.OS;
using Android.Content.PM;
+using Avalonia;
using Avalonia.Android;
namespace ControlCatalog.Android
{
- [Activity(Label = "ControlCatalog.Android", Theme = "@style/MyTheme.NoActionBar", Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleInstance)]
- public class MainActivity : AvaloniaActivity
+ [Activity(Label = "ControlCatalog.Android", Theme = "@style/MyTheme.NoActionBar", Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleInstance, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)]
+ public class MainActivity : AvaloniaActivity
{
- protected override void OnCreate(Bundle? savedInstanceState)
+ protected override AppBuilder CustomizeAppBuilder(AppBuilder builder)
{
- base.OnCreate(savedInstanceState);
-
- Content = new MainView();
+ return base.CustomizeAppBuilder(builder);
}
}
}
-
diff --git a/samples/ControlCatalog.Android/Resources/values/styles.xml b/samples/ControlCatalog.Android/Resources/values/styles.xml
index e017b6facf..2759d2904a 100644
--- a/samples/ControlCatalog.Android/Resources/values/styles.xml
+++ b/samples/ControlCatalog.Android/Resources/values/styles.xml
@@ -4,7 +4,7 @@
-
diff --git a/samples/ControlCatalog.Android/SplashActivity.cs b/samples/ControlCatalog.Android/SplashActivity.cs
index 9729713833..dc292fd37b 100644
--- a/samples/ControlCatalog.Android/SplashActivity.cs
+++ b/samples/ControlCatalog.Android/SplashActivity.cs
@@ -1,9 +1,6 @@
using Android.App;
using Android.Content;
using Android.OS;
-using Application = Android.App.Application;
-
-using Avalonia;
namespace ControlCatalog.Android
{
@@ -19,13 +16,6 @@ namespace ControlCatalog.Android
{
base.OnResume();
- if (Avalonia.Application.Current == null)
- {
- AppBuilder.Configure()
- .UseAndroid()
- .SetupWithoutStarting();
- }
-
StartActivity(new Intent(Application.Context, typeof(MainActivity)));
}
}
diff --git a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
index 2d4fc45171..2b45ac1508 100644
--- a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
+++ b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
@@ -6,7 +6,14 @@
true
+
+ true
+ https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet7/nuget/v3/index.json
+ 7.0.0-*
+
+
+
@@ -15,6 +22,13 @@
+
+
+
+
+
+
+
en
diff --git a/samples/ControlCatalog.NetCore/Program.cs b/samples/ControlCatalog.NetCore/Program.cs
index 0c8fd9465c..4464413e63 100644
--- a/samples/ControlCatalog.NetCore/Program.cs
+++ b/samples/ControlCatalog.NetCore/Program.cs
@@ -117,7 +117,13 @@ namespace ControlCatalog.NetCore
EnableMultitouch = true
})
.UseSkia()
- .UseManagedSystemDialogs()
+ .AfterSetup(builder =>
+ {
+ builder.Instance!.AttachDevTools(new Avalonia.Diagnostics.DevToolsOptions()
+ {
+ StartupScreenIndex = 1,
+ });
+ })
.LogToTrace();
static void SilenceConsole()
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 db1e16166a..513ac44f83 100644
--- a/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj
+++ b/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj
@@ -1,186 +1,16 @@
-
-
+
- Debug
- iPhoneSimulator
- {57E0455D-D565-44BB-B069-EE1AA20F8337}
- {FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
Exe
- ControlCatalog.iOS
- Resources
- ControlCatalogiOS
- true
- NSUrlSessionHandler
- PackageReference
- automatic
+ manual
+ net6.0-ios
+ 10.0
+
+ True
+ iossimulator-x64
+
-
- true
- full
- false
- bin\iPhoneSimulator\Debug
- DEBUG
- prompt
- 4
- false
- x86_64
- None
- True
- 9.1
- False
- False
- False
- False
- False
- False
- False
- False
- True
- Default
- HttpClientHandler
- False
-
-
- none
- true
- bin\iPhoneSimulator\Release
- prompt
- 4
- None
- x86_64
- false
-
-
- true
- full
- false
- bin\iPhone\Debug
- DEBUG
- prompt
- 4
- false
- ARMv7, ARM64
- Entitlements.plist
- iPhone Developer
- true
-
-
- none
- true
- bin\iPhone\Release
- prompt
- 4
- Entitlements.plist
- ARMv7, ARM64
- false
- iPhone Developer
-
-
- none
- True
- bin\iPhone\Ad-Hoc
- prompt
- 4
- False
- ARMv7, ARM64
- Entitlements.plist
- True
- Automatic:AdHoc
- iPhone Distribution
-
-
- none
- True
- bin\iPhone\AppStore
- prompt
- 4
- False
- ARMv7, ARM64
- Entitlements.plist
- Automatic:AppStore
- iPhone Distribution
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {4488AD85-1495-4809-9AA4-DDFE0A48527E}
- Avalonia.iOS
- false
- false
-
-
- {3E53A01A-B331-47F3-B828-4A5717E77A24}
- Avalonia.Markup.Xaml
-
-
- {6417E941-21BC-467B-A771-0DE389353CE6}
- Avalonia.Markup
-
-
- {D211E587-D8BC-45B9-95A4-F297C8FA5200}
- Avalonia.Animation
-
-
- {B09B78D8-9B26-48B0-9149-D64A2F120F3F}
- Avalonia.Base
-
-
- {D2221C82-4A25-4583-9B43-D791E3F6820C}
- Avalonia.Controls
-
-
- {7062AE20-5DCC-4442-9645-8195BDECE63E}
- Avalonia.Diagnostics
-
-
- {62024B2D-53EB-4638-B26B-85EEAA54866E}
- Avalonia.Input
-
-
- {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}
- Avalonia.Interactivity
-
-
- {42472427-4774-4C81-8AFF-9F27B8E31721}
- Avalonia.Layout
-
-
- {EB582467-6ABB-43A1-B052-E981BA910E3A}
- Avalonia.Visuals
-
-
- {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}
- Avalonia.Styling
-
-
- {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}
- Avalonia.Themes.Default
-
-
- {7d2d3083-71dd-4cc9-8907-39a0d86fb322}
- Avalonia.Skia
-
-
- {d0a739b9-3c68-4ba6-a328-41606954b6bd}
- ControlCatalog
-
-
+
+
-
-
-
-
-
+
\ No newline at end of file
diff --git a/samples/ControlCatalog.iOS/Info.plist b/samples/ControlCatalog.iOS/Info.plist
index 216fd9c333..6ffe3ba662 100644
--- a/samples/ControlCatalog.iOS/Info.plist
+++ b/samples/ControlCatalog.iOS/Info.plist
@@ -5,7 +5,7 @@
CFBundleDisplayName
ControlCatalog.iOS
CFBundleIdentifier
- com.companyname.ControlCatalog.iOS
+ Avalonia.ControlCatalog
CFBundleShortVersionString
1.0
CFBundleVersion
@@ -13,7 +13,7 @@
LSRequiresIPhoneOS
MinimumOSVersion
- 8.0
+ 10.0
UIDeviceFamily
1
@@ -28,6 +28,7 @@
UISupportedInterfaceOrientations
UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
@@ -38,5 +39,9 @@
UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
+ UIStatusBarHidden
+
+ UIViewControllerBasedStatusBarAppearance
+
diff --git a/samples/ControlCatalog.iOS/Main.cs b/samples/ControlCatalog.iOS/Main.cs
index fe039ba69e..2400115041 100644
--- a/samples/ControlCatalog.iOS/Main.cs
+++ b/samples/ControlCatalog.iOS/Main.cs
@@ -9,7 +9,7 @@ namespace ControlCatalog.iOS
{
// if you want to use a different Application Delegate class from "AppDelegate"
// you can specify it here.
- UIApplication.Main(args, null, "AppDelegate");
+ UIApplication.Main(args, null, typeof(AppDelegate));
}
}
-}
\ No newline at end of file
+}
diff --git a/samples/ControlCatalog.iOS/Properties/AssemblyInfo.cs b/samples/ControlCatalog.iOS/Properties/AssemblyInfo.cs
deleted file mode 100644
index 0a5a598651..0000000000
--- a/samples/ControlCatalog.iOS/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-// General Information about an assembly is controlled through the following
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
-[assembly: AssemblyTitle("ControlCatalog.iOS")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("ControlCatalog.iOS")]
-[assembly: AssemblyCopyright("Copyright © 2016")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
-// COM, set the ComVisible attribute to true on that type.
-[assembly: ComVisible(false)]
-
-// The following GUID is for the ID of the typelib if this project is exposed to COM
-[assembly: Guid("57e0455d-d565-44bb-b069-ee1aa20f8337")]
-
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-//
-// You can specify all the values or you can default the Build and Revision Numbers
-// by using the '*' as shown below:
-// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/samples/ControlCatalog.iOS/Resources/LaunchScreen.xib b/samples/ControlCatalog.iOS/Resources/LaunchScreen.xib
index be4abb2b43..5d3ccc97db 100644
--- a/samples/ControlCatalog.iOS/Resources/LaunchScreen.xib
+++ b/samples/ControlCatalog.iOS/Resources/LaunchScreen.xib
@@ -11,7 +11,7 @@
-
-
+
+
+ TextFormatterPage.axaml
+ Code
+
+
diff --git a/samples/RenderDemo/ViewModels/Transform3DPageViewModel.cs b/samples/RenderDemo/ViewModels/Transform3DPageViewModel.cs
new file mode 100644
index 0000000000..c8d1d40e3a
--- /dev/null
+++ b/samples/RenderDemo/ViewModels/Transform3DPageViewModel.cs
@@ -0,0 +1,55 @@
+using System;
+using MiniMvvm;
+using Avalonia.Animation;
+
+namespace RenderDemo.ViewModels
+{
+ public class Transform3DPageViewModel : ViewModelBase
+ {
+ private double _depth = 200;
+
+ private double _centerX = 0;
+ private double _centerY = 0;
+ private double _centerZ = 0;
+ private double _angleX = 0;
+ private double _angleY = 0;
+ private double _angleZ = 0;
+
+ public double Depth
+ {
+ get => _depth;
+ set => RaiseAndSetIfChanged(ref _depth, value);
+ }
+
+ public double CenterX
+ {
+ get => _centerX;
+ set => RaiseAndSetIfChanged(ref _centerX, value);
+ }
+ public double CenterY
+ {
+ get => _centerY;
+ set => RaiseAndSetIfChanged(ref _centerY, value);
+ }
+ public double CenterZ
+ {
+ get => _centerZ;
+ set => RaiseAndSetIfChanged(ref _centerZ, value);
+ }
+ public double AngleX
+ {
+ get => _angleX;
+ set => RaiseAndSetIfChanged(ref _angleX, value);
+ }
+ public double AngleY
+ {
+ get => _angleY;
+ set => RaiseAndSetIfChanged(ref _angleY, value);
+ }
+ public double AngleZ
+ {
+ get => _angleZ;
+ set => RaiseAndSetIfChanged(ref _angleZ, value);
+ }
+ }
+}
diff --git a/samples/SampleControls/HamburgerMenu/HamburgerMenu.cs b/samples/SampleControls/HamburgerMenu/HamburgerMenu.cs
index bbfd3d87ca..ab61dcde91 100644
--- a/samples/SampleControls/HamburgerMenu/HamburgerMenu.cs
+++ b/samples/SampleControls/HamburgerMenu/HamburgerMenu.cs
@@ -43,14 +43,13 @@ namespace ControlSamples
_splitView = e.NameScope.Find("PART_NavigationPane");
}
- protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
if (change.Property == BoundsProperty && _splitView is not null)
{
- var oldBounds = change.OldValue.GetValueOrDefault();
- var newBounds = change.NewValue.GetValueOrDefault();
+ var (oldBounds, newBounds) = change.GetOldAndNewValue();
EnsureSplitViewMode(oldBounds, newBounds);
}
}
diff --git a/samples/SampleControls/HamburgerMenu/HamburgerMenu.xaml b/samples/SampleControls/HamburgerMenu/HamburgerMenu.xaml
index 1aa7f8ea04..1d58c465a0 100644
--- a/samples/SampleControls/HamburgerMenu/HamburgerMenu.xaml
+++ b/samples/SampleControls/HamburgerMenu/HamburgerMenu.xaml
@@ -22,7 +22,7 @@
40
- 200
+ 220
36
36
32
@@ -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}" />
+
+
+
+
diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Default/ColorSlider.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Default/ColorSlider.xaml
new file mode 100644
index 0000000000..19f10201a5
--- /dev/null
+++ b/src/Avalonia.Controls.ColorPicker/Themes/Default/ColorSlider.xaml
@@ -0,0 +1,194 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Default/ColorSpectrum.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Default/ColorSpectrum.xaml
new file mode 100644
index 0000000000..78e6da8aa3
--- /dev/null
+++ b/src/Avalonia.Controls.ColorPicker/Themes/Default/ColorSpectrum.xaml
@@ -0,0 +1,134 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Default/Default.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Default/Default.xaml
new file mode 100644
index 0000000000..6d2f979f6e
--- /dev/null
+++ b/src/Avalonia.Controls.ColorPicker/Themes/Default/Default.xaml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorPreviewer.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorPreviewer.xaml
new file mode 100644
index 0000000000..cb764a738c
--- /dev/null
+++ b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorPreviewer.xaml
@@ -0,0 +1,86 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSlider.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSlider.xaml
new file mode 100644
index 0000000000..18a081721a
--- /dev/null
+++ b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSlider.xaml
@@ -0,0 +1,194 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSpectrum.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSpectrum.xaml
new file mode 100644
index 0000000000..ac8e2a9c06
--- /dev/null
+++ b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSpectrum.xaml
@@ -0,0 +1,137 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/Fluent.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/Fluent.xaml
new file mode 100644
index 0000000000..c25d79727f
--- /dev/null
+++ b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/Fluent.xaml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Avalonia.Controls.DataGrid/ApiCompatBaseline.txt b/src/Avalonia.Controls.DataGrid/ApiCompatBaseline.txt
new file mode 100644
index 0000000000..bfcec2960a
--- /dev/null
+++ b/src/Avalonia.Controls.DataGrid/ApiCompatBaseline.txt
@@ -0,0 +1,4 @@
+Compat issues with assembly Avalonia.Controls.DataGrid:
+MembersMustExist : Member 'protected void Avalonia.Controls.DataGridCheckBoxColumn.OnPropertyChanged(Avalonia.AvaloniaPropertyChangedEventArgs)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'protected void Avalonia.Controls.DataGridTextColumn.OnPropertyChanged(Avalonia.AvaloniaPropertyChangedEventArgs)' does not exist in the implementation but it does exist in the contract.
+Total Issues: 2
diff --git a/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj b/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj
index c89157dc0f..6369961f0f 100644
--- a/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj
+++ b/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj
@@ -4,14 +4,8 @@
Avalonia.Controls.DataGrid
-
-
-
-
-
-
@@ -23,4 +17,10 @@
+
+
+
+
+
+
diff --git a/src/Avalonia.Controls.DataGrid/Collections/DataGridCollectionView.cs b/src/Avalonia.Controls.DataGrid/Collections/DataGridCollectionView.cs
index fe6acdc532..906ec661ae 100644
--- a/src/Avalonia.Controls.DataGrid/Collections/DataGridCollectionView.cs
+++ b/src/Avalonia.Controls.DataGrid/Collections/DataGridCollectionView.cs
@@ -3946,7 +3946,7 @@ namespace Avalonia.Collections
{
sort.Initialize(itemType);
- if(seq is IOrderedEnumerable
-
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/src/Avalonia.Controls/Border.cs b/src/Avalonia.Controls/Border.cs
index ee3be1d5b3..bc740c133a 100644
--- a/src/Avalonia.Controls/Border.cs
+++ b/src/Avalonia.Controls/Border.cs
@@ -1,8 +1,10 @@
+using System;
using Avalonia.Collections;
using Avalonia.Controls.Shapes;
using Avalonia.Controls.Utils;
using Avalonia.Layout;
using Avalonia.Media;
+using Avalonia.Utilities;
using Avalonia.VisualTree;
namespace Avalonia.Controls
@@ -69,6 +71,8 @@ namespace Avalonia.Controls
AvaloniaProperty.Register(nameof(BorderLineJoin), PenLineJoin.Miter);
private readonly BorderRenderHelper _borderRenderHelper = new BorderRenderHelper();
+ private Thickness? _layoutThickness;
+ private double _scale;
///
/// Initializes static members of the class.
@@ -88,6 +92,18 @@ namespace Avalonia.Controls
AffectsMeasure(BorderThicknessProperty);
}
+ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+ {
+ base.OnPropertyChanged(change);
+ switch (change.Property.Name)
+ {
+ case nameof(UseLayoutRounding):
+ case nameof(BorderThickness):
+ _layoutThickness = null;
+ break;
+ }
+ }
+
///
/// Gets or sets a brush with which to paint the background.
///
@@ -169,13 +185,43 @@ namespace Avalonia.Controls
set => SetValue(BoxShadowProperty, value);
}
+ private Thickness LayoutThickness
+ {
+ get
+ {
+ VerifyScale();
+
+ if (_layoutThickness == null)
+ {
+ var borderThickness = BorderThickness;
+
+ if (UseLayoutRounding)
+ borderThickness = LayoutHelper.RoundLayoutThickness(borderThickness, _scale, _scale);
+
+ _layoutThickness = borderThickness;
+ }
+
+ return _layoutThickness.Value;
+ }
+ }
+
+ private void VerifyScale()
+ {
+ var currentScale = LayoutHelper.GetLayoutScale(this);
+ if (MathUtilities.AreClose(currentScale, _scale))
+ return;
+
+ _scale = currentScale;
+ _layoutThickness = null;
+ }
+
///
/// Renders the control.
///
/// The drawing context.
public override void Render(DrawingContext context)
{
- _borderRenderHelper.Render(context, Bounds.Size, BorderThickness, CornerRadius, Background, BorderBrush,
+ _borderRenderHelper.Render(context, Bounds.Size, LayoutThickness, CornerRadius, Background, BorderBrush,
BoxShadow, BorderDashOffset, BorderLineCap, BorderLineJoin, BorderDashArray);
}
diff --git a/src/Avalonia.Controls/Button.cs b/src/Avalonia.Controls/Button.cs
index a7a4759182..db4ca6bc43 100644
--- a/src/Avalonia.Controls/Button.cs
+++ b/src/Avalonia.Controls/Button.cs
@@ -1,6 +1,7 @@
using System;
using System.Linq;
using System.Windows.Input;
+using Avalonia.Automation.Peers;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
using Avalonia.Data;
@@ -28,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.
///
@@ -91,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.
@@ -106,7 +111,6 @@ namespace Avalonia.Controls
///
public Button()
{
- UpdatePseudoClasses(IsPressed);
}
///
@@ -237,7 +241,7 @@ namespace Avalonia.Controls
{
HotKey = _hotkey;
}
-
+
base.OnAttachedToLogicalTree(e);
if (Command != null)
@@ -305,6 +309,8 @@ namespace Avalonia.Controls
IsPressed = false;
e.Handled = true;
}
+
+ base.OnKeyUp(e);
}
///
@@ -327,11 +333,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)
{
@@ -370,6 +395,8 @@ namespace Avalonia.Controls
///
protected override void OnPointerCaptureLost(PointerCaptureLostEventArgs e)
{
+ base.OnPointerCaptureLost(e);
+
IsPressed = false;
}
@@ -382,7 +409,17 @@ namespace Avalonia.Controls
}
///
- protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+ protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
+ {
+ base.OnApplyTemplate(e);
+
+ UnregisterFlyoutEvents(Flyout);
+ RegisterFlyoutEvents(Flyout);
+ UpdatePseudoClasses();
+ }
+
+ ///
+ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
@@ -390,12 +427,13 @@ namespace Avalonia.Controls
{
if (((ILogical)this).IsAttachedToLogicalTree)
{
- if (change.OldValue.GetValueOrDefault() is ICommand oldCommand)
+ var (oldValue, newValue) = change.GetOldAndNewValue();
+ if (oldValue is ICommand oldCommand)
{
oldCommand.CanExecuteChanged -= CanExecuteChanged;
}
- if (change.NewValue.GetValueOrDefault() is ICommand newCommand)
+ if (newValue is ICommand newCommand)
{
newCommand.CanExecuteChanged += CanExecuteChanged;
}
@@ -409,7 +447,7 @@ namespace Avalonia.Controls
}
else if (change.Property == IsCancelProperty)
{
- var isCancel = change.NewValue.GetValueOrDefault();
+ var isCancel = change.GetNewValue();
if (VisualRoot is IInputElement inputRoot)
{
@@ -425,7 +463,7 @@ namespace Avalonia.Controls
}
else if (change.Property == IsDefaultProperty)
{
- var isDefault = change.NewValue.GetValueOrDefault();
+ var isDefault = change.GetNewValue();
if (VisualRoot is IInputElement inputRoot)
{
@@ -441,27 +479,40 @@ namespace Avalonia.Controls
}
else if (change.Property == IsPressedProperty)
{
- UpdatePseudoClasses(change.NewValue.GetValueOrDefault());
+ UpdatePseudoClasses();
}
else if (change.Property == FlyoutProperty)
{
+ var (oldFlyout, newFlyout) = change.GetOldAndNewValue();
+
// 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();
}
}
+ protected override AutomationPeer OnCreateAutomationPeer() => new ButtonAutomationPeer(this);
+
///
- protected override void UpdateDataValidation(AvaloniaProperty property, BindingValue value)
+ protected override void UpdateDataValidation(
+ AvaloniaProperty property,
+ BindingValueType state,
+ Exception? error)
{
- base.UpdateDataValidation(property, value);
+ base.UpdateDataValidation(property, state, error);
if (property == CommandProperty)
{
- if (value.Type == BindingValueType.BindingError)
+ if (state == BindingValueType.BindingError)
{
if (_commandCanExecute)
{
@@ -472,6 +523,8 @@ namespace Avalonia.Controls
}
}
+ internal void PerformClick() => OnClick();
+
///
/// Called when the event fires.
///
@@ -488,6 +541,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 .
///
@@ -534,6 +613,7 @@ namespace Avalonia.Controls
if (e.Key == Key.Enter && IsVisible && IsEnabled)
{
OnClick();
+ e.Handled = true;
}
}
@@ -547,17 +627,60 @@ namespace Avalonia.Controls
if (e.Key == Key.Escape && IsVisible && IsEnabled)
{
OnClick();
+ e.Handled = true;
}
}
///
/// 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..e455c6c6f3 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
{
@@ -208,13 +210,13 @@ namespace Avalonia.Controls
}
}
- protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
if (change.Property == ButtonSpinnerLocationProperty)
{
- UpdatePseudoClasses(change.NewValue.GetValueOrDefault());
+ UpdatePseudoClasses(change.GetNewValue());
}
}
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
deleted file mode 100644
index c1f487c32d..0000000000
--- a/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs
+++ /dev/null
@@ -1,1136 +0,0 @@
-// (c) Copyright Microsoft Corporation.
-// This source is subject to the Microsoft Public License (Ms-PL).
-// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
-// All other rights reserved.
-
-using System;
-using System.Collections.ObjectModel;
-using System.Diagnostics;
-using System.Globalization;
-using Avalonia.Controls.Primitives;
-using Avalonia.Data;
-using Avalonia.Input;
-using Avalonia.Interactivity;
-using Avalonia.Layout;
-
-namespace Avalonia.Controls
-{
- ///
- /// Provides data for the
- ///
- /// event.
- ///
- public class CalendarDatePickerDateValidationErrorEventArgs : EventArgs
- {
- private bool _throwException;
-
- ///
- /// Initializes a new instance of the
- ///
- /// class.
- ///
- ///
- /// The initial exception from the
- ///
- /// event.
- ///
- ///
- /// The text that caused the
- ///
- /// event.
- ///
- public CalendarDatePickerDateValidationErrorEventArgs(Exception exception, string text)
- {
- this.Text = text;
- this.Exception = exception;
- }
-
- ///
- /// Gets the initial exception associated with the
- ///
- /// event.
- ///
- ///
- /// The exception associated with the validation failure.
- ///
- public Exception Exception { get; private set; }
-
- ///
- /// Gets the text that caused the
- ///
- /// event.
- ///
- ///
- /// The text that caused the validation failure.
- ///
- public string Text { get; private set; }
-
- ///
- /// Gets or sets a value indicating whether
- ///
- /// should be thrown.
- ///
- ///
- /// True if the exception should be thrown; otherwise, false.
- ///
- ///
- /// If set to true and
- ///
- /// is null.
- ///
- public bool ThrowException
- {
- get { return this._throwException; }
- set
- {
- if (value && this.Exception == null)
- {
- throw new ArgumentException("Cannot Throw Null Exception");
- }
- this._throwException = value;
- }
- }
- }
-
- ///
- /// Specifies date formats for a
- /// .
- ///
- public enum CalendarDatePickerFormat
- {
- ///
- /// Specifies that the date should be displayed using unabbreviated days
- /// of the week and month names.
- ///
- Long = 0,
-
- ///
- /// Specifies that the date should be displayed using abbreviated days
- /// of the week and month names.
- ///
- Short = 1,
-
- ///
- /// Specifies that the date should be displayed using a custom format string.
- ///
- Custom = 2
- }
-
- public class CalendarDatePicker : TemplatedControl
- {
- private const string ElementTextBox = "PART_TextBox";
- private const string ElementButton = "PART_Button";
- private const string ElementPopup = "PART_Popup";
- private const string ElementCalendar = "PART_Calendar";
-
- private Calendar? _calendar;
- private string _defaultText;
- private Button? _dropDownButton;
- //private Canvas _outsideCanvas;
- //private Canvas _outsidePopupCanvas;
- private Popup? _popUp;
- private TextBox? _textBox;
- private IDisposable? _textBoxTextChangedSubscription;
- private IDisposable? _buttonPointerPressedSubscription;
-
- private DateTime? _onOpenSelectedDate;
- private bool _settingSelectedDate;
-
- private DateTime _displayDate;
- private DateTime? _displayDateStart;
- private DateTime? _displayDateEnd;
- private bool _isDropDownOpen;
- private DateTime? _selectedDate;
- private string? _text;
- private bool _suspendTextChangeHandler = false;
- private bool _isPopupClosing = false;
- private bool _ignoreButtonClick = false;
-
- ///
- /// Gets a collection of dates that are marked as not selectable.
- ///
- ///
- /// A collection of dates that cannot be selected. The default value is
- /// an empty collection.
- ///
- public CalendarBlackoutDatesCollection? BlackoutDates { get; private set; }
-
- public static readonly DirectProperty DisplayDateProperty =
- AvaloniaProperty.RegisterDirect(
- nameof(DisplayDate),
- o => o.DisplayDate,
- (o, v) => o.DisplayDate = v);
- public static readonly DirectProperty DisplayDateStartProperty =
- AvaloniaProperty.RegisterDirect(
- nameof(DisplayDateStart),
- o => o.DisplayDateStart,
- (o, v) => o.DisplayDateStart = v);
- public static readonly DirectProperty DisplayDateEndProperty =
- AvaloniaProperty.RegisterDirect(
- nameof(DisplayDateEnd),
- o => o.DisplayDateEnd,
- (o, v) => o.DisplayDateEnd = v);
- public static readonly StyledProperty FirstDayOfWeekProperty =
- AvaloniaProperty.Register(nameof(FirstDayOfWeek));
-
- public static readonly DirectProperty IsDropDownOpenProperty =
- AvaloniaProperty.RegisterDirect(
- nameof(IsDropDownOpen),
- o => o.IsDropDownOpen,
- (o, v) => o.IsDropDownOpen = v);
-
- public static readonly StyledProperty IsTodayHighlightedProperty =
- AvaloniaProperty.Register(nameof(IsTodayHighlighted));
- public static readonly DirectProperty SelectedDateProperty =
- AvaloniaProperty.RegisterDirect(
- nameof(SelectedDate),
- o => o.SelectedDate,
- (o, v) => o.SelectedDate = v,
- enableDataValidation: true);
-
- public static readonly StyledProperty SelectedDateFormatProperty =
- AvaloniaProperty.Register(
- nameof(SelectedDateFormat),
- defaultValue: CalendarDatePickerFormat.Short,
- validate: IsValidSelectedDateFormat);
-
- public static readonly StyledProperty CustomDateFormatStringProperty =
- AvaloniaProperty.Register(
- nameof(CustomDateFormatString),
- defaultValue: "d",
- validate: IsValidDateFormatString);
-
- public static readonly DirectProperty TextProperty =
- AvaloniaProperty.RegisterDirect(
- nameof(Text),
- o => o.Text,
- (o, v) => o.Text = v);
- public static readonly StyledProperty WatermarkProperty =
- TextBox.WatermarkProperty.AddOwner();
- public static readonly StyledProperty UseFloatingWatermarkProperty =
- TextBox.UseFloatingWatermarkProperty.AddOwner();
-
-
- ///
- /// Defines the property.
- ///
- public static readonly StyledProperty HorizontalContentAlignmentProperty =
- ContentControl.HorizontalContentAlignmentProperty.AddOwner();
-
- ///
- /// Defines the property.
- ///
- public static readonly StyledProperty VerticalContentAlignmentProperty =
- ContentControl.VerticalContentAlignmentProperty.AddOwner();
-
- ///
- /// Gets or sets the date to display.
- ///
- ///
- /// The date to display. The default
- /// .
- ///
- ///
- /// The specified date is not in the range defined by
- ///
- /// and
- /// .
- ///
- public DateTime DisplayDate
- {
- get { return _displayDate; }
- set { SetAndRaise(DisplayDateProperty, ref _displayDate, value); }
- }
-
- ///
- /// Gets or sets the first date to be displayed.
- ///
- /// The first date to display.
- public DateTime? DisplayDateStart
- {
- get { return _displayDateStart; }
- set { SetAndRaise(DisplayDateStartProperty, ref _displayDateStart, value); }
- }
-
- ///
- /// Gets or sets the last date to be displayed.
- ///
- /// The last date to display.
- public DateTime? DisplayDateEnd
- {
- get { return _displayDateEnd; }
- set { SetAndRaise(DisplayDateEndProperty, ref _displayDateEnd, value); }
- }
-
- ///
- /// Gets or sets the day that is considered the beginning of the week.
- ///
- ///
- /// A representing the beginning of
- /// the week. The default is .
- ///
- public DayOfWeek FirstDayOfWeek
- {
- get { return GetValue(FirstDayOfWeekProperty); }
- set { SetValue(FirstDayOfWeekProperty, value); }
- }
-
- ///
- /// Gets or sets a value indicating whether the drop-down
- /// is open or closed.
- ///
- ///
- /// True if the is
- /// open; otherwise, false. The default is false.
- ///
- public bool IsDropDownOpen
- {
- get { return _isDropDownOpen; }
- set { SetAndRaise(IsDropDownOpenProperty, ref _isDropDownOpen, value); }
- }
-
- ///
- /// Gets or sets a value indicating whether the current date will be
- /// highlighted.
- ///
- ///
- /// True if the current date is highlighted; otherwise, false. The
- /// default is true.
- ///
- public bool IsTodayHighlighted
- {
- get { return GetValue(IsTodayHighlightedProperty); }
- set { SetValue(IsTodayHighlightedProperty, value); }
- }
-
- ///
- /// Gets or sets the currently selected date.
- ///
- ///
- /// The date currently selected. The default is null.
- ///
- ///
- /// The specified date is not in the range defined by
- ///
- /// and
- /// ,
- /// or the specified date is in the
- ///
- /// collection.
- ///
- public DateTime? SelectedDate
- {
- get { return _selectedDate; }
- set { SetAndRaise(SelectedDateProperty, ref _selectedDate, value); }
- }
-
- ///
- /// Gets or sets the format that is used to display the selected date.
- ///
- ///
- /// The format that is used to display the selected date. The default is
- /// .
- ///
- ///
- /// An specified format is not valid.
- ///
- public CalendarDatePickerFormat SelectedDateFormat
- {
- get { return GetValue(SelectedDateFormatProperty); }
- set { SetValue(SelectedDateFormatProperty, value); }
- }
-
- public string CustomDateFormatString
- {
- get { return GetValue(CustomDateFormatStringProperty); }
- set { SetValue(CustomDateFormatStringProperty, value); }
- }
-
- ///
- /// Gets or sets the text that is displayed by the
- /// .
- ///
- ///
- /// The text displayed by the
- /// .
- ///
- ///
- /// The text entered cannot be parsed to a valid date, and the exception
- /// is not suppressed.
- ///
- ///
- /// The text entered parses to a date that is not selectable.
- ///
- public string? Text
- {
- get { return _text; }
- set { SetAndRaise(TextProperty, ref _text, value); }
- }
-
- public string? Watermark
- {
- get { return GetValue(WatermarkProperty); }
- set { SetValue(WatermarkProperty, value); }
- }
- public bool UseFloatingWatermark
- {
- get { return GetValue(UseFloatingWatermarkProperty); }
- set { SetValue(UseFloatingWatermarkProperty, value); }
- }
-
-
- ///
- /// Gets or sets the horizontal alignment of the content within the control.
- ///
- public HorizontalAlignment HorizontalContentAlignment
- {
- get => GetValue(HorizontalContentAlignmentProperty);
- set => SetValue(HorizontalContentAlignmentProperty, value);
- }
-
- ///
- /// Gets or sets the vertical alignment of the content within the control.
- ///
- public VerticalAlignment VerticalContentAlignment
- {
- get => GetValue(VerticalContentAlignmentProperty);
- set => SetValue(VerticalContentAlignmentProperty, value);
- }
-
- ///
- /// Occurs when the drop-down
- /// is closed.
- ///
- public event EventHandler? CalendarClosed;
-
- ///
- /// Occurs when the drop-down
- /// is opened.
- ///
- public event EventHandler? CalendarOpened;
-
- ///
- /// Occurs when
- /// is assigned a value that cannot be interpreted as a date.
- ///
- public event EventHandler? DateValidationError;
-
- ///
- /// Occurs when the
- ///
- /// property is changed.
- ///
- public event EventHandler? SelectedDateChanged;
-
- static CalendarDatePicker()
- {
- FocusableProperty.OverrideDefaultValue(true);
-
- IsDropDownOpenProperty.Changed.AddClassHandler((x,e) => x.OnIsDropDownOpenChanged(e));
- SelectedDateProperty.Changed.AddClassHandler((x,e) => x.OnSelectedDateChanged(e));
- SelectedDateFormatProperty.Changed.AddClassHandler((x,e) => x.OnSelectedDateFormatChanged(e));
- CustomDateFormatStringProperty.Changed.AddClassHandler((x,e) => x.OnCustomDateFormatStringChanged(e));
- TextProperty.Changed.AddClassHandler((x,e) => x.OnTextChanged(e));
- }
- ///
- /// Initializes a new instance of the
- /// class.
- ///
- public CalendarDatePicker()
- {
- FirstDayOfWeek = DateTimeHelper.GetCurrentDateFormat().FirstDayOfWeek;
- _defaultText = string.Empty;
- DisplayDate = DateTime.Today;
- }
-
- protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
- {
- if (_calendar != null)
- {
- _calendar.DayButtonMouseUp -= Calendar_DayButtonMouseUp;
- _calendar.DisplayDateChanged -= Calendar_DisplayDateChanged;
- _calendar.SelectedDatesChanged -= Calendar_SelectedDatesChanged;
- _calendar.PointerReleased -= Calendar_PointerReleased;
- _calendar.KeyDown -= Calendar_KeyDown;
- }
- _calendar = e.NameScope.Find(ElementCalendar);
- if (_calendar != null)
- {
- _calendar.SelectionMode = CalendarSelectionMode.SingleDate;
-
- _calendar.DayButtonMouseUp += Calendar_DayButtonMouseUp;
- _calendar.DisplayDateChanged += Calendar_DisplayDateChanged;
- _calendar.SelectedDatesChanged += Calendar_SelectedDatesChanged;
- _calendar.PointerReleased += Calendar_PointerReleased;
- _calendar.KeyDown += Calendar_KeyDown;
-
- var currentBlackoutDays = BlackoutDates;
- BlackoutDates = _calendar.BlackoutDates;
- if(currentBlackoutDays != null)
- {
- foreach (var range in currentBlackoutDays)
- {
- BlackoutDates.Add(range);
- }
- }
- }
-
- if (_popUp != null)
- {
- _popUp.Child = null;
- _popUp.Closed -= PopUp_Closed;
- }
- _popUp = e.NameScope.Find(ElementPopup);
- if(_popUp != null)
- {
- _popUp.Closed += PopUp_Closed;
- if (IsDropDownOpen)
- {
- OpenDropDown();
- }
- }
-
- if(_dropDownButton != null)
- {
- _dropDownButton.Click -= DropDownButton_Click;
- _buttonPointerPressedSubscription?.Dispose();
- }
- _dropDownButton = e.NameScope.Find(ElementButton);
- if(_dropDownButton != null)
- {
- _dropDownButton.Click += DropDownButton_Click;
- _buttonPointerPressedSubscription =
- _dropDownButton.AddDisposableHandler(PointerPressedEvent, DropDownButton_PointerPressed, handledEventsToo: true);
- }
-
- if (_textBox != null)
- {
- _textBox.KeyDown -= TextBox_KeyDown;
- _textBox.GotFocus -= TextBox_GotFocus;
- _textBoxTextChangedSubscription?.Dispose();
- }
- _textBox = e.NameScope.Find(ElementTextBox);
-
- if(!SelectedDate.HasValue)
- {
- SetWaterMarkText();
- }
-
- if(_textBox != null)
- {
- _textBox.KeyDown += TextBox_KeyDown;
- _textBox.GotFocus += TextBox_GotFocus;
- _textBoxTextChangedSubscription = _textBox.GetObservable(TextBox.TextProperty).Subscribe(txt => TextBox_TextChanged());
-
- if(SelectedDate.HasValue)
- {
- _textBox.Text = DateTimeToString(SelectedDate.Value);
- }
- else if(!String.IsNullOrEmpty(_defaultText))
- {
- _textBox.Text = _defaultText;
- SetSelectedDate();
- }
- }
- }
-
- protected override void UpdateDataValidation(AvaloniaProperty property, BindingValue value)
- {
- if (property == SelectedDateProperty)
- {
- DataValidationErrors.SetError(this, value.Error);
- }
- }
-
- protected override void OnPointerWheelChanged(PointerWheelEventArgs e)
- {
- base.OnPointerWheelChanged(e);
- if (!e.Handled && SelectedDate.HasValue && _calendar != null)
- {
- DateTime selectedDate = this.SelectedDate.Value;
- DateTime? newDate = DateTimeHelper.AddDays(selectedDate, e.Delta.Y > 0 ? -1 : 1);
- if (newDate.HasValue && Calendar.IsValidDateSelection(_calendar, newDate.Value))
- {
- SelectedDate = newDate;
- e.Handled = true;
- }
- }
- }
- protected override void OnGotFocus(GotFocusEventArgs e)
- {
- base.OnGotFocus(e);
- if(IsEnabled && _textBox != null && e.NavigationMethod == NavigationMethod.Tab)
- {
- _textBox.Focus();
- var text = _textBox.Text;
- if(!string.IsNullOrEmpty(text))
- {
- _textBox.SelectionStart = 0;
- _textBox.SelectionEnd = text.Length;
- }
- }
- }
- protected override void OnLostFocus(RoutedEventArgs e)
- {
- base.OnLostFocus(e);
-
- SetSelectedDate();
- }
-
- private void OnIsDropDownOpenChanged(AvaloniaPropertyChangedEventArgs e)
- {
- var oldValue = (bool)e.OldValue!;
- var value = (bool)e.NewValue!;
-
- if (_popUp != null && _popUp.Child != null)
- {
- if (value != oldValue)
- {
- if (_calendar!.DisplayMode != CalendarMode.Month)
- {
- _calendar.DisplayMode = CalendarMode.Month;
- }
-
- if (value)
- {
- OpenDropDown();
- }
- else
- {
- _popUp.IsOpen = false;
- OnCalendarClosed(new RoutedEventArgs());
- }
- }
- }
- }
- private void OnSelectedDateChanged(AvaloniaPropertyChangedEventArgs e)
- {
- var addedDate = (DateTime?)e.NewValue;
- var removedDate = (DateTime?)e.OldValue;
-
- if (SelectedDate != null)
- {
- DateTime day = SelectedDate.Value;
-
- // When the SelectedDateProperty change is done from
- // OnTextPropertyChanged method, two-way binding breaks if
- // BeginInvoke is not used:
- Threading.Dispatcher.UIThread.InvokeAsync(() =>
- {
- _settingSelectedDate = true;
- Text = DateTimeToString(day);
- _settingSelectedDate = false;
- OnDateSelected(addedDate, removedDate);
- });
-
- // When DatePickerDisplayDateFlag is TRUE, the SelectedDate
- // change is coming from the Calendar UI itself, so, we
- // shouldn't change the DisplayDate since it will automatically
- // be changed by the Calendar
- if ((day.Month != DisplayDate.Month || day.Year != DisplayDate.Year) && (_calendar == null || !_calendar.CalendarDatePickerDisplayDateFlag))
- {
- DisplayDate = day;
- }
- if(_calendar != null)
- _calendar.CalendarDatePickerDisplayDateFlag = false;
- }
- else
- {
- _settingSelectedDate = true;
- SetWaterMarkText();
- _settingSelectedDate = false;
- OnDateSelected(addedDate, removedDate);
- }
- }
- private void OnDateFormatChanged()
- {
- if (_textBox != null)
- {
- if (SelectedDate.HasValue)
- {
- Text = DateTimeToString(SelectedDate.Value);
- }
- else if (string.IsNullOrEmpty(_textBox.Text))
- {
- SetWaterMarkText();
- }
- else
- {
- DateTime? date = ParseText(_textBox.Text);
-
- if (date != null)
- {
- string? s = DateTimeToString((DateTime)date);
- Text = s;
- }
- }
- }
- }
- private void OnSelectedDateFormatChanged(AvaloniaPropertyChangedEventArgs e)
- {
- OnDateFormatChanged();
- }
- private void OnCustomDateFormatStringChanged(AvaloniaPropertyChangedEventArgs e)
- {
- if(SelectedDateFormat == CalendarDatePickerFormat.Custom)
- {
- OnDateFormatChanged();
- }
- }
- private void OnTextChanged(AvaloniaPropertyChangedEventArgs e)
- {
- var oldValue = (string?)e.OldValue;
- var value = (string?)e.NewValue;
-
- if (!_suspendTextChangeHandler)
- {
- if (value != null)
- {
- if (_textBox != null)
- {
- _textBox.Text = value;
- }
- else
- {
- _defaultText = value;
- }
- if (!_settingSelectedDate)
- {
- SetSelectedDate();
- }
- }
- else
- {
- if (!_settingSelectedDate)
- {
- _settingSelectedDate = true;
- SelectedDate = null;
- _settingSelectedDate = false;
- }
- }
- }
- else
- {
- SetWaterMarkText();
- }
- }
-
- ///
- /// Raises the
- ///
- /// event.
- ///
- ///
- /// A
- ///
- /// that contains the event data.
- ///
- protected virtual void OnDateValidationError(CalendarDatePickerDateValidationErrorEventArgs e)
- {
- DateValidationError?.Invoke(this, e);
- }
- private void OnDateSelected(DateTime? addedDate, DateTime? removedDate)
- {
- EventHandler? handler = this.SelectedDateChanged;
- if (null != handler)
- {
- Collection addedItems = new Collection();
- Collection removedItems = new Collection();
-
- if (addedDate.HasValue)
- {
- addedItems.Add(addedDate.Value);
- }
-
- if (removedDate.HasValue)
- {
- removedItems.Add(removedDate.Value);
- }
-
- handler(this, new SelectionChangedEventArgs(SelectingItemsControl.SelectionChangedEvent, removedItems, addedItems));
- }
- }
- private void OnCalendarClosed(EventArgs e)
- {
- CalendarClosed?.Invoke(this, e);
- }
- private void OnCalendarOpened(EventArgs e)
- {
- CalendarOpened?.Invoke(this, e);
- }
-
- private void Calendar_DayButtonMouseUp(object? sender, PointerReleasedEventArgs e)
- {
- Focus();
- IsDropDownOpen = false;
- }
- private void Calendar_DisplayDateChanged(object? sender, CalendarDateChangedEventArgs e)
- {
- if (e.AddedDate != this.DisplayDate)
- {
- SetValue(DisplayDateProperty, (DateTime) e.AddedDate!);
- }
- }
- private void Calendar_SelectedDatesChanged(object? sender, SelectionChangedEventArgs e)
- {
- Debug.Assert(e.AddedItems.Count < 2, "There should be less than 2 AddedItems!");
-
- if (e.AddedItems.Count > 0 && SelectedDate.HasValue && DateTime.Compare((DateTime)e.AddedItems[0]!, SelectedDate.Value) != 0)
- {
- SelectedDate = (DateTime?)e.AddedItems[0];
- }
- else
- {
- if (e.AddedItems.Count == 0)
- {
- SelectedDate = null;
- return;
- }
-
- if (!SelectedDate.HasValue)
- {
- if (e.AddedItems.Count > 0)
- {
- SelectedDate = (DateTime?)e.AddedItems[0];
- }
- }
- }
- }
- private void Calendar_PointerReleased(object? sender, PointerReleasedEventArgs e)
- {
-
- if (e.InitialPressMouseButton == MouseButton.Left)
- {
- e.Handled = true;
- }
- }
- private void Calendar_KeyDown(object? sender, KeyEventArgs e)
- {
- Calendar? c = sender as Calendar ?? throw new ArgumentException("Sender must be Calendar.", nameof(sender));
-
- if (!e.Handled && (e.Key == Key.Enter || e.Key == Key.Space || e.Key == Key.Escape) && c.DisplayMode == CalendarMode.Month)
- {
- Focus();
- IsDropDownOpen = false;
-
- if (e.Key == Key.Escape)
- {
- SelectedDate = _onOpenSelectedDate;
- }
- }
- }
- private void TextBox_GotFocus(object? sender, RoutedEventArgs e)
- {
- IsDropDownOpen = false;
- }
- private void TextBox_KeyDown(object? sender, KeyEventArgs e)
- {
- if (!e.Handled)
- {
- e.Handled = ProcessDatePickerKey(e);
- }
- }
- private void TextBox_TextChanged()
- {
- if (_textBox != null)
- {
- _suspendTextChangeHandler = true;
- Text = _textBox.Text;
- _suspendTextChangeHandler = false;
- }
- }
- private void DropDownButton_PointerPressed(object? sender, PointerPressedEventArgs e)
- {
- _ignoreButtonClick = _isPopupClosing;
- }
- private void DropDownButton_Click(object? sender, RoutedEventArgs e)
- {
- if (!_ignoreButtonClick)
- {
- HandlePopUp();
- }
- else
- {
- _ignoreButtonClick = false;
- }
- }
- private void PopUp_Closed(object? sender, EventArgs e)
- {
- IsDropDownOpen = false;
-
- if(!_isPopupClosing)
- {
- _isPopupClosing = true;
- Threading.Dispatcher.UIThread.InvokeAsync(() => _isPopupClosing = false);
- }
- }
-
- private void HandlePopUp()
- {
- if (IsDropDownOpen)
- {
- Focus();
- IsDropDownOpen = false;
- }
- else
- {
- ProcessTextBox();
- }
- }
- private void OpenDropDown()
- {
- if (_calendar != null)
- {
- _calendar.Focus();
- OpenPopUp();
- _calendar.ResetStates();
- OnCalendarOpened(new RoutedEventArgs());
- }
- }
- private void OpenPopUp()
- {
- _onOpenSelectedDate = SelectedDate;
- _popUp!.IsOpen = true;
- }
-
- ///
- /// Input text is parsed in the correct format and changed into a
- /// DateTime object. If the text can not be parsed TextParseError Event
- /// is thrown.
- ///
- /// Inherited code: Requires comment.
- ///
- /// IT SHOULD RETURN NULL IF THE STRING IS NOT VALID, RETURN THE
- /// DATETIME VALUE IF IT IS VALID.
- ///
- private DateTime? ParseText(string text)
- {
- DateTime newSelectedDate;
-
- // TryParse is not used in order to be able to pass the exception to
- // the TextParseError event
- try
- {
- newSelectedDate = DateTime.Parse(text, DateTimeHelper.GetCurrentDateFormat());
-
- if (Calendar.IsValidDateSelection(this._calendar!, newSelectedDate))
- {
- return newSelectedDate;
- }
- else
- {
- var dateValidationError = new CalendarDatePickerDateValidationErrorEventArgs(new ArgumentOutOfRangeException(nameof(text), "SelectedDate value is not valid."), text);
- OnDateValidationError(dateValidationError);
-
- if (dateValidationError.ThrowException)
- {
- throw dateValidationError.Exception;
- }
- }
- }
- catch (FormatException ex)
- {
- CalendarDatePickerDateValidationErrorEventArgs textParseError = new CalendarDatePickerDateValidationErrorEventArgs(ex, text);
- OnDateValidationError(textParseError);
-
- if (textParseError.ThrowException)
- {
- throw textParseError.Exception;
- }
- }
- return null;
- }
- private string? DateTimeToString(DateTime d)
- {
- DateTimeFormatInfo dtfi = DateTimeHelper.GetCurrentDateFormat();
-
- switch (SelectedDateFormat)
- {
- case CalendarDatePickerFormat.Short:
- return string.Format(CultureInfo.CurrentCulture, d.ToString(dtfi.ShortDatePattern, dtfi));
- case CalendarDatePickerFormat.Long:
- return string.Format(CultureInfo.CurrentCulture, d.ToString(dtfi.LongDatePattern, dtfi));
- case CalendarDatePickerFormat.Custom:
- return string.Format(CultureInfo.CurrentCulture, d.ToString(CustomDateFormatString, dtfi));
- }
- return null;
- }
- private bool ProcessDatePickerKey(KeyEventArgs e)
- {
-
- switch (e.Key)
- {
- case Key.Enter:
- {
- SetSelectedDate();
- return true;
- }
- case Key.Down:
- {
- if ((e.KeyModifiers & KeyModifiers.Control) == KeyModifiers.Control)
- {
- HandlePopUp();
- return true;
- }
- break;
- }
- }
- return false;
- }
- private void ProcessTextBox()
- {
- SetSelectedDate();
- IsDropDownOpen = true;
- _calendar!.Focus();
- }
- private void SetSelectedDate()
- {
- if (_textBox != null)
- {
- if (!string.IsNullOrEmpty(_textBox.Text))
- {
- string s = _textBox.Text;
-
- if (SelectedDate != null)
- {
- // If the string value of the SelectedDate and the
- // TextBox string value are equal, we do not parse the
- // string again if we do an extra parse, we lose data in
- // M/d/yy format.
- // ex: SelectedDate = DateTime(1008,12,19) but when
- // "12/19/08" is parsed it is interpreted as
- // DateTime(2008,12,19)
- string? selectedDate = DateTimeToString(SelectedDate.Value);
- if (selectedDate == s)
- {
- return;
- }
- }
- DateTime? d = SetTextBoxValue(s);
-
- if (SelectedDate != d)
- {
- SelectedDate = d;
- }
- }
- else
- {
- if (SelectedDate != null)
- {
- SelectedDate = null;
- }
- }
- }
- else
- {
- DateTime? d = SetTextBoxValue(_defaultText);
-
- if (SelectedDate != d)
- {
- SelectedDate = d;
- }
- }
- }
- private DateTime? SetTextBoxValue(string s)
- {
- if (string.IsNullOrEmpty(s))
- {
- SetValue(TextProperty, s);
- return SelectedDate;
- }
- else
- {
- DateTime? d = ParseText(s);
- if (d != null)
- {
- SetValue(TextProperty, s);
- return d;
- }
- else
- {
- // If parse error: TextBox should have the latest valid
- // SelectedDate value:
- if (SelectedDate != null)
- {
- string? newtext = this.DateTimeToString(SelectedDate.Value);
- SetValue(TextProperty, newtext);
- return SelectedDate;
- }
- else
- {
- SetWaterMarkText();
- return null;
- }
- }
- }
- }
- private void SetWaterMarkText()
- {
- if (_textBox != null)
- {
- if (string.IsNullOrEmpty(Watermark) && !UseFloatingWatermark)
- {
- DateTimeFormatInfo dtfi = DateTimeHelper.GetCurrentDateFormat();
- Text = string.Empty;
- _defaultText = string.Empty;
- var watermarkFormat = "<{0}>";
- string watermarkText;
-
- switch (SelectedDateFormat)
- {
- case CalendarDatePickerFormat.Long:
- {
- watermarkText = string.Format(CultureInfo.CurrentCulture, watermarkFormat, dtfi.LongDatePattern.ToString());
- break;
- }
- case CalendarDatePickerFormat.Short:
- default:
- {
- watermarkText = string.Format(CultureInfo.CurrentCulture, watermarkFormat, dtfi.ShortDatePattern.ToString());
- break;
- }
- }
- _textBox.Watermark = watermarkText;
- }
- else
- {
- _textBox.ClearValue(TextBox.WatermarkProperty);
- }
- }
- }
-
- private static bool IsValidSelectedDateFormat(CalendarDatePickerFormat value)
- {
- return value == CalendarDatePickerFormat.Long
- || value == CalendarDatePickerFormat.Short
- || value == CalendarDatePickerFormat.Custom;
- }
- private static bool IsValidDateFormatString(string formatString)
- {
- return !string.IsNullOrWhiteSpace(formatString);
- }
- private static DateTime DiscardDayTime(DateTime d)
- {
- int year = d.Year;
- int month = d.Month;
- DateTime newD = new DateTime(year, month, 1, 0, 0, 0);
- return newD;
- }
- private static DateTime? DiscardTime(DateTime? d)
- {
- if (d == null)
- {
- return null;
- }
- else
- {
- DateTime discarded = (DateTime) d;
- int year = discarded.Year;
- int month = discarded.Month;
- int day = discarded.Day;
- DateTime newD = new DateTime(year, month, day, 0, 0, 0);
- return newD;
- }
- }
- }
-}
diff --git a/src/Avalonia.Controls/Calendar/CalendarItem.cs b/src/Avalonia.Controls/Calendar/CalendarItem.cs
index 616b9083ff..32fdaceacb 100644
--- a/src/Avalonia.Controls/Calendar/CalendarItem.cs
+++ b/src/Avalonia.Controls/Calendar/CalendarItem.cs
@@ -7,6 +7,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
+using Avalonia.Collections.Pooled;
using Avalonia.Controls.Metadata;
using Avalonia.Data;
using Avalonia.Input;
@@ -19,6 +20,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
{
@@ -167,13 +173,13 @@ namespace Avalonia.Controls.Primitives
if (MonthView != null)
{
var childCount = Calendar.RowsPerMonth + Calendar.RowsPerMonth * Calendar.ColumnsPerMonth;
- var children = new List(childCount);
+ using var children = new PooledList(childCount);
for (int i = 0; i < Calendar.RowsPerMonth; i++)
{
if (_dayTitleTemplate != null)
{
- var cell = _dayTitleTemplate.Build();
+ var cell = (Control) _dayTitleTemplate.Build();
cell.DataContext = string.Empty;
cell.SetValue(Grid.RowProperty, 0);
cell.SetValue(Grid.ColumnProperty, i);
@@ -181,11 +187,16 @@ namespace Avalonia.Controls.Primitives
}
}
+ EventHandler cellMouseLeftButtonDown = Cell_MouseLeftButtonDown;
+ EventHandler cellMouseLeftButtonUp = Cell_MouseLeftButtonUp;
+ EventHandler cellMouseEnter = Cell_MouseEnter;
+ EventHandler cellClick = Cell_Click;
+
for (int i = 1; i < Calendar.RowsPerMonth; i++)
{
for (int j = 0; j < Calendar.ColumnsPerMonth; j++)
{
- CalendarDayButton cell = new CalendarDayButton();
+ var cell = new CalendarDayButton();
if (Owner != null)
{
@@ -193,10 +204,10 @@ namespace Avalonia.Controls.Primitives
}
cell.SetValue(Grid.RowProperty, i);
cell.SetValue(Grid.ColumnProperty, j);
- cell.CalendarDayButtonMouseDown += Cell_MouseLeftButtonDown;
- cell.CalendarDayButtonMouseUp += Cell_MouseLeftButtonUp;
- cell.PointerEnter += Cell_MouseEnter;
- cell.Click += Cell_Click;
+ cell.CalendarDayButtonMouseDown += cellMouseLeftButtonDown;
+ cell.CalendarDayButtonMouseUp += cellMouseLeftButtonUp;
+ cell.PointerEnter += cellMouseEnter;
+ cell.Click += cellClick;
children.Add(cell);
}
}
@@ -209,12 +220,15 @@ namespace Avalonia.Controls.Primitives
var childCount = Calendar.RowsPerYear * Calendar.ColumnsPerYear;
var children = new List(childCount);
- CalendarButton month;
+ EventHandler monthCalendarButtonMouseDown = Month_CalendarButtonMouseDown;
+ EventHandler monthCalendarButtonMouseUp = Month_CalendarButtonMouseUp;
+ EventHandler monthMouseEnter = Month_MouseEnter;
+
for (int i = 0; i < Calendar.RowsPerYear; i++)
{
for (int j = 0; j < Calendar.ColumnsPerYear; j++)
{
- month = new CalendarButton();
+ var month = new CalendarButton();
if (Owner != null)
{
@@ -222,9 +236,9 @@ namespace Avalonia.Controls.Primitives
}
month.SetValue(Grid.RowProperty, i);
month.SetValue(Grid.ColumnProperty, j);
- month.CalendarLeftMouseButtonDown += Month_CalendarButtonMouseDown;
- month.CalendarLeftMouseButtonUp += Month_CalendarButtonMouseUp;
- month.PointerEnter += Month_MouseEnter;
+ month.CalendarLeftMouseButtonDown += monthCalendarButtonMouseDown;
+ month.CalendarLeftMouseButtonUp += monthCalendarButtonMouseUp;
+ month.PointerEnter += monthMouseEnter;
children.Add(month);
}
}
diff --git a/src/Avalonia.Controls/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/CalendarDatePicker/CalendarDatePicker.Properties.cs b/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.Properties.cs
new file mode 100644
index 0000000000..6c2356b411
--- /dev/null
+++ b/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.Properties.cs
@@ -0,0 +1,303 @@
+using System;
+using Avalonia.Controls.Primitives;
+using Avalonia.Data;
+using Avalonia.Layout;
+
+namespace Avalonia.Controls
+{
+ ///
+ public partial class CalendarDatePicker
+ {
+ ///
+ /// Defines the property.
+ ///
+ public static readonly DirectProperty DisplayDateProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(DisplayDate),
+ o => o.DisplayDate,
+ (o, v) => o.DisplayDate = v);
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly DirectProperty DisplayDateStartProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(DisplayDateStart),
+ o => o.DisplayDateStart,
+ (o, v) => o.DisplayDateStart = v);
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly DirectProperty DisplayDateEndProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(DisplayDateEnd),
+ o => o.DisplayDateEnd,
+ (o, v) => o.DisplayDateEnd = v);
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty FirstDayOfWeekProperty =
+ AvaloniaProperty.Register(nameof(FirstDayOfWeek));
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly DirectProperty IsDropDownOpenProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(IsDropDownOpen),
+ o => o.IsDropDownOpen,
+ (o, v) => o.IsDropDownOpen = v);
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty IsTodayHighlightedProperty =
+ AvaloniaProperty.Register(nameof(IsTodayHighlighted));
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly DirectProperty SelectedDateProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(SelectedDate),
+ o => o.SelectedDate,
+ (o, v) => o.SelectedDate = v,
+ enableDataValidation: true,
+ defaultBindingMode:BindingMode.TwoWay);
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty SelectedDateFormatProperty =
+ AvaloniaProperty.Register(
+ nameof(SelectedDateFormat),
+ defaultValue: CalendarDatePickerFormat.Short,
+ validate: IsValidSelectedDateFormat);
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty CustomDateFormatStringProperty =
+ AvaloniaProperty.Register(
+ nameof(CustomDateFormatString),
+ defaultValue: "d",
+ validate: IsValidDateFormatString);
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly DirectProperty TextProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(Text),
+ o => o.Text,
+ (o, v) => o.Text = v);
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty WatermarkProperty =
+ TextBox.WatermarkProperty.AddOwner();
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty UseFloatingWatermarkProperty =
+ TextBox.UseFloatingWatermarkProperty.AddOwner();
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty HorizontalContentAlignmentProperty =
+ ContentControl.HorizontalContentAlignmentProperty.AddOwner();
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty VerticalContentAlignmentProperty =
+ ContentControl.VerticalContentAlignmentProperty.AddOwner();
+
+ ///
+ /// Gets a collection of dates that are marked as not selectable.
+ ///
+ ///
+ /// A collection of dates that cannot be selected. The default value is
+ /// an empty collection.
+ ///
+ public CalendarBlackoutDatesCollection? BlackoutDates { get; private set; }
+
+ ///
+ /// Gets or sets the date to display.
+ ///
+ ///
+ /// The date to display. The default is .
+ ///
+ ///
+ /// The specified date is not in the range defined by
+ ///
+ /// and
+ /// .
+ ///
+ public DateTime DisplayDate
+ {
+ get => _displayDate;
+ set => SetAndRaise(DisplayDateProperty, ref _displayDate, value);
+ }
+
+ ///
+ /// Gets or sets the first date to be displayed.
+ ///
+ /// The first date to display.
+ public DateTime? DisplayDateStart
+ {
+ get => _displayDateStart;
+ set => SetAndRaise(DisplayDateStartProperty, ref _displayDateStart, value);
+ }
+
+ ///
+ /// Gets or sets the last date to be displayed.
+ ///
+ /// The last date to display.
+ public DateTime? DisplayDateEnd
+ {
+ get => _displayDateEnd;
+ set => SetAndRaise(DisplayDateEndProperty, ref _displayDateEnd, value);
+ }
+
+ ///
+ /// Gets or sets the day that is considered the beginning of the week.
+ ///
+ ///
+ /// A representing the beginning of
+ /// the week. The default is .
+ ///
+ public DayOfWeek FirstDayOfWeek
+ {
+ get => GetValue(FirstDayOfWeekProperty);
+ set => SetValue(FirstDayOfWeekProperty, value);
+ }
+
+ ///
+ /// Gets or sets a value indicating whether the drop-down
+ /// is open or closed.
+ ///
+ ///
+ /// True if the is
+ /// open; otherwise, false. The default is false.
+ ///
+ public bool IsDropDownOpen
+ {
+ get => _isDropDownOpen;
+ set => SetAndRaise(IsDropDownOpenProperty, ref _isDropDownOpen, value);
+ }
+
+ ///
+ /// Gets or sets a value indicating whether the current date will be
+ /// highlighted.
+ ///
+ ///
+ /// True if the current date is highlighted; otherwise, false. The
+ /// default is true.
+ ///
+ public bool IsTodayHighlighted
+ {
+ get => GetValue(IsTodayHighlightedProperty);
+ set => SetValue(IsTodayHighlightedProperty, value);
+ }
+
+ ///
+ /// Gets or sets the currently selected date.
+ ///
+ ///
+ /// The date currently selected. The default is null.
+ ///
+ ///
+ /// The specified date is not in the range defined by
+ ///
+ /// and
+ /// ,
+ /// or the specified date is in the
+ ///
+ /// collection.
+ ///
+ public DateTime? SelectedDate
+ {
+ get => _selectedDate;
+ set => SetAndRaise(SelectedDateProperty, ref _selectedDate, value);
+ }
+
+ ///
+ /// Gets or sets the format that is used to display the selected date.
+ ///
+ ///
+ /// The format that is used to display the selected date. The default is
+ /// .
+ ///
+ ///
+ /// An specified format is not valid.
+ ///
+ public CalendarDatePickerFormat SelectedDateFormat
+ {
+ get => GetValue(SelectedDateFormatProperty);
+ set => SetValue(SelectedDateFormatProperty, value);
+ }
+
+ public string CustomDateFormatString
+ {
+ get => GetValue(CustomDateFormatStringProperty);
+ set => SetValue(CustomDateFormatStringProperty, value);
+ }
+
+ ///
+ /// Gets or sets the text that is displayed by the .
+ ///
+ ///
+ /// The text displayed by the .
+ ///
+ ///
+ /// The text entered cannot be parsed to a valid date, and the exception
+ /// is not suppressed.
+ ///
+ ///
+ /// The text entered parses to a date that is not selectable.
+ ///
+ public string? Text
+ {
+ get => _text;
+ set => SetAndRaise(TextProperty, ref _text, value);
+ }
+
+ ///
+ public string? Watermark
+ {
+ get => GetValue(WatermarkProperty);
+ set => SetValue(WatermarkProperty, value);
+ }
+
+ ///
+ public bool UseFloatingWatermark
+ {
+ get => GetValue(UseFloatingWatermarkProperty);
+ set => SetValue(UseFloatingWatermarkProperty, value);
+ }
+
+ ///
+ /// Gets or sets the horizontal alignment of the content within the control.
+ ///
+ public HorizontalAlignment HorizontalContentAlignment
+ {
+ get => GetValue(HorizontalContentAlignmentProperty);
+ set => SetValue(HorizontalContentAlignmentProperty, value);
+ }
+
+ ///
+ /// Gets or sets the vertical alignment of the content within the control.
+ ///
+ public VerticalAlignment VerticalContentAlignment
+ {
+ get => GetValue(VerticalContentAlignmentProperty);
+ set => SetValue(VerticalContentAlignmentProperty, value);
+ }
+ }
+}
diff --git a/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.cs b/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.cs
new file mode 100644
index 0000000000..3d592e9ab5
--- /dev/null
+++ b/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.cs
@@ -0,0 +1,927 @@
+// (c) Copyright Microsoft Corporation.
+// This source is subject to the Microsoft Public License (Ms-PL).
+// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
+// All other rights reserved.
+
+using System;
+using System.Collections.ObjectModel;
+using System.Diagnostics;
+using System.Globalization;
+using System.Reactive.Disposables;
+using Avalonia.Controls.Metadata;
+using Avalonia.Controls.Primitives;
+using Avalonia.Data;
+using Avalonia.Input;
+using Avalonia.Interactivity;
+
+namespace Avalonia.Controls
+{
+ ///
+ /// A date selection control that allows the user to select dates from a drop down calendar.
+ ///
+ [TemplatePart(ElementButton, typeof(Button))]
+ [TemplatePart(ElementCalendar, typeof(Calendar))]
+ [TemplatePart(ElementPopup, typeof(Popup))]
+ [TemplatePart(ElementTextBox, typeof(TextBox))]
+ [PseudoClasses(pcFlyoutOpen, pcPressed)]
+ public partial class CalendarDatePicker : TemplatedControl
+ {
+ protected const string pcPressed = ":pressed";
+ protected const string pcFlyoutOpen = ":flyout-open";
+
+ private const string ElementTextBox = "PART_TextBox";
+ private const string ElementButton = "PART_Button";
+ private const string ElementPopup = "PART_Popup";
+ private const string ElementCalendar = "PART_Calendar";
+
+ private Calendar? _calendar;
+ private string _defaultText;
+ private Button? _dropDownButton;
+ private Popup? _popUp;
+ private TextBox? _textBox;
+ private IDisposable? _textBoxTextChangedSubscription;
+ private IDisposable? _buttonPointerPressedSubscription;
+
+ private DateTime? _onOpenSelectedDate;
+ private bool _settingSelectedDate;
+
+ private DateTime _displayDate;
+ private DateTime? _displayDateStart;
+ private DateTime? _displayDateEnd;
+ private bool _isDropDownOpen;
+ private DateTime? _selectedDate;
+ private string? _text;
+ private bool _suspendTextChangeHandler = false;
+ private bool _isPopupClosing = false;
+ private bool _ignoreButtonClick = false;
+ private bool _isFlyoutOpen = false;
+ private bool _isPressed = false;
+
+ ///
+ /// Occurs when the drop-down
+ /// is closed.
+ ///
+ public event EventHandler? CalendarClosed;
+
+ ///
+ /// Occurs when the drop-down
+ /// is opened.
+ ///
+ public event EventHandler? CalendarOpened;
+
+ ///
+ /// Occurs when
+ /// is assigned a value that cannot be interpreted as a date.
+ ///
+ public event EventHandler? DateValidationError;
+
+ ///
+ /// Occurs when the
+ ///
+ /// property is changed.
+ ///
+ public event EventHandler? SelectedDateChanged;
+
+ static CalendarDatePicker()
+ {
+ FocusableProperty.OverrideDefaultValue(true);
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public CalendarDatePicker()
+ {
+ FirstDayOfWeek = DateTimeHelper.GetCurrentDateFormat().FirstDayOfWeek;
+ _defaultText = string.Empty;
+ DisplayDate = DateTime.Today;
+ }
+
+ ///
+ /// Updates the visual state of the control by applying latest PseudoClasses.
+ ///
+ protected void UpdatePseudoClasses()
+ {
+ PseudoClasses.Set(pcFlyoutOpen, _isFlyoutOpen);
+ PseudoClasses.Set(pcPressed, _isPressed);
+ }
+
+ ///
+ protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
+ {
+ if (_calendar != null)
+ {
+ _calendar.DayButtonMouseUp -= Calendar_DayButtonMouseUp;
+ _calendar.DisplayDateChanged -= Calendar_DisplayDateChanged;
+ _calendar.SelectedDatesChanged -= Calendar_SelectedDatesChanged;
+ _calendar.PointerReleased -= Calendar_PointerReleased;
+ _calendar.KeyDown -= Calendar_KeyDown;
+ }
+ _calendar = e.NameScope.Find(ElementCalendar);
+ if (_calendar != null)
+ {
+ _calendar.SelectionMode = CalendarSelectionMode.SingleDate;
+
+ _calendar.DayButtonMouseUp += Calendar_DayButtonMouseUp;
+ _calendar.DisplayDateChanged += Calendar_DisplayDateChanged;
+ _calendar.SelectedDatesChanged += Calendar_SelectedDatesChanged;
+ _calendar.PointerReleased += Calendar_PointerReleased;
+ _calendar.KeyDown += Calendar_KeyDown;
+
+ var currentBlackoutDays = BlackoutDates;
+ BlackoutDates = _calendar.BlackoutDates;
+ if(currentBlackoutDays != null)
+ {
+ foreach (var range in currentBlackoutDays)
+ {
+ BlackoutDates.Add(range);
+ }
+ }
+ }
+
+ if (_popUp != null)
+ {
+ _popUp.Child = null;
+ _popUp.Closed -= PopUp_Closed;
+ }
+ _popUp = e.NameScope.Find(ElementPopup);
+ if(_popUp != null)
+ {
+ _popUp.Closed += PopUp_Closed;
+ if (IsDropDownOpen)
+ {
+ OpenDropDown();
+ }
+ }
+
+ if(_dropDownButton != null)
+ {
+ _dropDownButton.Click -= DropDownButton_Click;
+ _buttonPointerPressedSubscription?.Dispose();
+ }
+ _dropDownButton = e.NameScope.Find(ElementButton);
+ if(_dropDownButton != null)
+ {
+ _dropDownButton.Click += DropDownButton_Click;
+ _buttonPointerPressedSubscription = new CompositeDisposable(
+ _dropDownButton.AddDisposableHandler(PointerPressedEvent, DropDownButton_PointerPressed, handledEventsToo: true),
+ _dropDownButton.AddDisposableHandler(PointerReleasedEvent, DropDownButton_PointerReleased, handledEventsToo: true));
+ }
+
+ if (_textBox != null)
+ {
+ _textBox.KeyDown -= TextBox_KeyDown;
+ _textBox.GotFocus -= TextBox_GotFocus;
+ _textBoxTextChangedSubscription?.Dispose();
+ }
+ _textBox = e.NameScope.Find(ElementTextBox);
+
+ if(!SelectedDate.HasValue)
+ {
+ SetWaterMarkText();
+ }
+
+ if(_textBox != null)
+ {
+ _textBox.KeyDown += TextBox_KeyDown;
+ _textBox.GotFocus += TextBox_GotFocus;
+ _textBoxTextChangedSubscription = _textBox.GetObservable(TextBox.TextProperty).Subscribe(txt => TextBox_TextChanged());
+
+ if(SelectedDate.HasValue)
+ {
+ _textBox.Text = DateTimeToString(SelectedDate.Value);
+ }
+ else if(!String.IsNullOrEmpty(_defaultText))
+ {
+ _textBox.Text = _defaultText;
+ SetSelectedDate();
+ }
+ }
+
+ UpdatePseudoClasses();
+ }
+
+ ///
+ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+ {
+ // CustomDateFormatString
+ if (change.Property == CustomDateFormatStringProperty)
+ {
+ if (SelectedDateFormat == CalendarDatePickerFormat.Custom)
+ {
+ OnDateFormatChanged();
+ }
+ }
+ // IsDropDownOpen
+ else if (change.Property == IsDropDownOpenProperty)
+ {
+ var (oldValue, newValue) = change.GetOldAndNewValue();
+
+ if (_popUp != null && _popUp.Child != null)
+ {
+ if (newValue != oldValue)
+ {
+ if (_calendar!.DisplayMode != CalendarMode.Month)
+ {
+ _calendar.DisplayMode = CalendarMode.Month;
+ }
+
+ if (newValue)
+ {
+ OpenDropDown();
+ }
+ else
+ {
+ _popUp.IsOpen = false;
+ _isFlyoutOpen = _popUp.IsOpen;
+ _isPressed = false;
+
+ UpdatePseudoClasses();
+ OnCalendarClosed(new RoutedEventArgs());
+ }
+ }
+ }
+ }
+ // SelectedDate
+ else if (change.Property == SelectedDateProperty)
+ {
+ var (removedDate, addedDate) = change.GetOldAndNewValue();
+
+ if (SelectedDate != null)
+ {
+ DateTime day = SelectedDate.Value;
+
+ // When the SelectedDateProperty change is done from
+ // OnTextPropertyChanged method, two-way binding breaks if
+ // BeginInvoke is not used:
+ Threading.Dispatcher.UIThread.InvokeAsync(() =>
+ {
+ _settingSelectedDate = true;
+ Text = DateTimeToString(day);
+ _settingSelectedDate = false;
+ OnDateSelected(addedDate, removedDate);
+ });
+
+ // When DatePickerDisplayDateFlag is TRUE, the SelectedDate
+ // change is coming from the Calendar UI itself, so, we
+ // shouldn't change the DisplayDate since it will automatically
+ // be changed by the Calendar
+ if ((day.Month != DisplayDate.Month || day.Year != DisplayDate.Year) && (_calendar == null || !_calendar.CalendarDatePickerDisplayDateFlag))
+ {
+ DisplayDate = day;
+ }
+
+ if(_calendar != null)
+ {
+ _calendar.CalendarDatePickerDisplayDateFlag = false;
+ }
+ }
+ else
+ {
+ _settingSelectedDate = true;
+ SetWaterMarkText();
+ _settingSelectedDate = false;
+ OnDateSelected(addedDate, removedDate);
+ }
+ }
+ // SelectedDateFormat
+ else if (change.Property == SelectedDateFormatProperty)
+ {
+ OnDateFormatChanged();
+ }
+ // Text
+ else if (change.Property == TextProperty)
+ {
+ var (oldValue, newValue) = change.GetOldAndNewValue();
+
+ if (!_suspendTextChangeHandler)
+ {
+ if (newValue != null)
+ {
+ if (_textBox != null)
+ {
+ _textBox.Text = newValue;
+ }
+ else
+ {
+ _defaultText = newValue;
+ }
+
+ if (!_settingSelectedDate)
+ {
+ SetSelectedDate();
+ }
+ }
+ else
+ {
+ if (!_settingSelectedDate)
+ {
+ _settingSelectedDate = true;
+ SelectedDate = null;
+ _settingSelectedDate = false;
+ }
+ }
+ }
+ else
+ {
+ SetWaterMarkText();
+ }
+ }
+
+ base.OnPropertyChanged(change);
+ }
+
+ ///
+ protected override void UpdateDataValidation(AvaloniaProperty property, BindingValueType state, Exception? error)
+ {
+ if (property == SelectedDateProperty)
+ {
+ DataValidationErrors.SetError(this, error);
+ }
+
+ base.UpdateDataValidation(property, state, error);
+ }
+
+ ///
+ protected override void OnPointerPressed(PointerPressedEventArgs e)
+ {
+ base.OnPointerPressed(e);
+
+ if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
+ {
+ e.Handled = true;
+
+ _ignoreButtonClick = _isPopupClosing;
+
+ _isPressed = true;
+ UpdatePseudoClasses();
+ }
+ }
+
+ ///
+ protected override void OnPointerReleased(PointerReleasedEventArgs e)
+ {
+ base.OnPointerReleased(e);
+
+ if (_isPressed && e.InitialPressMouseButton == MouseButton.Left)
+ {
+ e.Handled = true;
+
+ if (!_ignoreButtonClick)
+ {
+ TogglePopUp();
+ }
+ else
+ {
+ _ignoreButtonClick = false;
+ }
+
+ _isPressed = false;
+ UpdatePseudoClasses();
+ }
+ }
+
+ ///
+ protected override void OnPointerCaptureLost(PointerCaptureLostEventArgs e)
+ {
+ base.OnPointerCaptureLost(e);
+
+ _isPressed = false;
+ UpdatePseudoClasses();
+ }
+
+ ///
+ protected override void OnPointerWheelChanged(PointerWheelEventArgs e)
+ {
+ base.OnPointerWheelChanged(e);
+ if (!e.Handled && SelectedDate.HasValue && _calendar != null)
+ {
+ DateTime selectedDate = this.SelectedDate.Value;
+ DateTime? newDate = DateTimeHelper.AddDays(selectedDate, e.Delta.Y > 0 ? -1 : 1);
+ if (newDate.HasValue && Calendar.IsValidDateSelection(_calendar, newDate.Value))
+ {
+ SelectedDate = newDate;
+ e.Handled = true;
+ }
+ }
+ }
+
+ ///
+ protected override void OnGotFocus(GotFocusEventArgs e)
+ {
+ base.OnGotFocus(e);
+ if(IsEnabled && _textBox != null && e.NavigationMethod == NavigationMethod.Tab)
+ {
+ _textBox.Focus();
+ var text = _textBox.Text;
+ if(!string.IsNullOrEmpty(text))
+ {
+ _textBox.SelectionStart = 0;
+ _textBox.SelectionEnd = text.Length;
+ }
+ }
+ }
+
+ ///
+ protected override void OnLostFocus(RoutedEventArgs e)
+ {
+ base.OnLostFocus(e);
+
+ _isPressed = false;
+ UpdatePseudoClasses();
+
+ SetSelectedDate();
+ }
+
+ ///
+ protected override void OnKeyUp(KeyEventArgs e)
+ {
+ var key = e.Key;
+
+ if ((key == Key.Space || key == Key.Enter) && IsEffectivelyEnabled) // Key.GamepadA is not currently supported
+ {
+ // Since the TextBox is used for direct date entry,
+ // it isn't supported to open the popup/flyout using these keys.
+ // Other controls open the popup/flyout here.
+ }
+ else if (key == Key.Down && e.KeyModifiers.HasAllFlags(KeyModifiers.Alt) && IsEffectivelyEnabled)
+ {
+ // It is only possible to open the popup using these keys.
+ // This is important as the down key is handled by calendar.
+ // If down also closed the popup, the date would move 1 week
+ // and then close the popup. This isn't user friendly at all.
+ // (calendar doesn't mark as handled either).
+ // The Escape key will still close the popup.
+ if (IsDropDownOpen == false)
+ {
+ e.Handled = true;
+
+ if (!_ignoreButtonClick)
+ {
+ TogglePopUp();
+ }
+ else
+ {
+ _ignoreButtonClick = false;
+ }
+
+ UpdatePseudoClasses();
+ }
+ }
+
+ base.OnKeyUp(e);
+ }
+
+ private void OnDateFormatChanged()
+ {
+ if (_textBox != null)
+ {
+ if (SelectedDate.HasValue)
+ {
+ Text = DateTimeToString(SelectedDate.Value);
+ }
+ else if (string.IsNullOrEmpty(_textBox.Text))
+ {
+ SetWaterMarkText();
+ }
+ else
+ {
+ DateTime? date = ParseText(_textBox.Text);
+
+ if (date != null)
+ {
+ string? s = DateTimeToString((DateTime)date);
+ Text = s;
+ }
+ }
+ }
+ }
+
+ ///
+ /// Raises the
+ ///
+ /// event.
+ ///
+ ///
+ /// A
+ ///
+ /// that contains the event data.
+ ///
+ protected virtual void OnDateValidationError(CalendarDatePickerDateValidationErrorEventArgs e)
+ {
+ DateValidationError?.Invoke(this, e);
+ }
+
+ private void OnDateSelected(DateTime? addedDate, DateTime? removedDate)
+ {
+ EventHandler? handler = this.SelectedDateChanged;
+ if (null != handler)
+ {
+ Collection addedItems = new Collection();
+ Collection removedItems = new Collection();
+
+ if (addedDate.HasValue)
+ {
+ addedItems.Add(addedDate.Value);
+ }
+
+ if (removedDate.HasValue)
+ {
+ removedItems.Add(removedDate.Value);
+ }
+
+ handler(this, new SelectionChangedEventArgs(SelectingItemsControl.SelectionChangedEvent, removedItems, addedItems));
+ }
+ }
+
+ private void OnCalendarClosed(EventArgs e)
+ {
+ CalendarClosed?.Invoke(this, e);
+ }
+
+ private void OnCalendarOpened(EventArgs e)
+ {
+ CalendarOpened?.Invoke(this, e);
+ }
+
+ private void Calendar_DayButtonMouseUp(object? sender, PointerReleasedEventArgs e)
+ {
+ Focus();
+ IsDropDownOpen = false;
+ }
+
+ private void Calendar_DisplayDateChanged(object? sender, CalendarDateChangedEventArgs e)
+ {
+ if (e.AddedDate != this.DisplayDate)
+ {
+ SetValue(DisplayDateProperty, (DateTime) e.AddedDate!);
+ }
+ }
+
+ private void Calendar_SelectedDatesChanged(object? sender, SelectionChangedEventArgs e)
+ {
+ Debug.Assert(e.AddedItems.Count < 2, "There should be less than 2 AddedItems!");
+
+ if (e.AddedItems.Count > 0 && SelectedDate.HasValue && DateTime.Compare((DateTime)e.AddedItems[0]!, SelectedDate.Value) != 0)
+ {
+ SelectedDate = (DateTime?)e.AddedItems[0];
+ }
+ else
+ {
+ if (e.AddedItems.Count == 0)
+ {
+ SelectedDate = null;
+ return;
+ }
+
+ if (!SelectedDate.HasValue)
+ {
+ if (e.AddedItems.Count > 0)
+ {
+ SelectedDate = (DateTime?)e.AddedItems[0];
+ }
+ }
+ }
+ }
+
+ private void Calendar_PointerReleased(object? sender, PointerReleasedEventArgs e)
+ {
+
+ if (e.InitialPressMouseButton == MouseButton.Left)
+ {
+ e.Handled = true;
+ }
+ }
+
+ private void Calendar_KeyDown(object? sender, KeyEventArgs e)
+ {
+ Calendar? c = sender as Calendar ?? throw new ArgumentException("Sender must be Calendar.", nameof(sender));
+
+ if (!e.Handled && (e.Key == Key.Enter || e.Key == Key.Space || e.Key == Key.Escape) && c.DisplayMode == CalendarMode.Month)
+ {
+ Focus();
+ IsDropDownOpen = false;
+
+ if (e.Key == Key.Escape)
+ {
+ SelectedDate = _onOpenSelectedDate;
+ }
+ }
+ }
+
+ private void TextBox_GotFocus(object? sender, RoutedEventArgs e)
+ {
+ IsDropDownOpen = false;
+ }
+
+ private void TextBox_KeyDown(object? sender, KeyEventArgs e)
+ {
+ if (!e.Handled)
+ {
+ e.Handled = ProcessDatePickerKey(e);
+ }
+ }
+
+ private void TextBox_TextChanged()
+ {
+ if (_textBox != null)
+ {
+ _suspendTextChangeHandler = true;
+ Text = _textBox.Text;
+ _suspendTextChangeHandler = false;
+ }
+ }
+
+ private void DropDownButton_PointerPressed(object? sender, PointerPressedEventArgs e)
+ {
+ _ignoreButtonClick = _isPopupClosing;
+
+ _isPressed = true;
+ UpdatePseudoClasses();
+ }
+
+ private void DropDownButton_PointerReleased(object? sender, PointerReleasedEventArgs e)
+ {
+ _isPressed = false;
+ UpdatePseudoClasses();
+ }
+
+ private void DropDownButton_Click(object? sender, RoutedEventArgs e)
+ {
+ if (!_ignoreButtonClick)
+ {
+ TogglePopUp();
+ }
+ else
+ {
+ _ignoreButtonClick = false;
+ }
+ }
+
+ private void PopUp_Closed(object? sender, EventArgs e)
+ {
+ IsDropDownOpen = false;
+
+ if(!_isPopupClosing)
+ {
+ _isPopupClosing = true;
+ Threading.Dispatcher.UIThread.InvokeAsync(() => _isPopupClosing = false);
+ }
+ }
+
+ ///
+ /// Toggles the property to open/close the calendar popup.
+ /// This will automatically adjust control focus as well.
+ ///
+ private void TogglePopUp()
+ {
+ if (IsDropDownOpen)
+ {
+ Focus();
+ IsDropDownOpen = false;
+ }
+ else
+ {
+ SetSelectedDate();
+ IsDropDownOpen = true;
+ _calendar!.Focus();
+ }
+ }
+
+ private void OpenDropDown()
+ {
+ if (_calendar != null)
+ {
+ _calendar.Focus();
+
+ // Open the PopUp
+ _onOpenSelectedDate = SelectedDate;
+ _popUp!.IsOpen = true;
+ _isFlyoutOpen = _popUp!.IsOpen;
+
+ UpdatePseudoClasses();
+ _calendar.ResetStates();
+ OnCalendarOpened(new RoutedEventArgs());
+ }
+ }
+
+ ///
+ /// Input text is parsed in the correct format and changed into a
+ /// DateTime object. If the text can not be parsed TextParseError Event
+ /// is thrown.
+ ///
+ /// Inherited code: Requires comment.
+ ///
+ /// IT SHOULD RETURN NULL IF THE STRING IS NOT VALID, RETURN THE
+ /// DATETIME VALUE IF IT IS VALID.
+ ///
+ private DateTime? ParseText(string text)
+ {
+ DateTime newSelectedDate;
+
+ // TryParse is not used in order to be able to pass the exception to
+ // the TextParseError event
+ try
+ {
+ newSelectedDate = DateTime.Parse(text, DateTimeHelper.GetCurrentDateFormat());
+
+ if (Calendar.IsValidDateSelection(this._calendar!, newSelectedDate))
+ {
+ return newSelectedDate;
+ }
+ else
+ {
+ var dateValidationError = new CalendarDatePickerDateValidationErrorEventArgs(new ArgumentOutOfRangeException(nameof(text), "SelectedDate value is not valid."), text);
+ OnDateValidationError(dateValidationError);
+
+ if (dateValidationError.ThrowException)
+ {
+ throw dateValidationError.Exception;
+ }
+ }
+ }
+ catch (FormatException ex)
+ {
+ CalendarDatePickerDateValidationErrorEventArgs textParseError = new CalendarDatePickerDateValidationErrorEventArgs(ex, text);
+ OnDateValidationError(textParseError);
+
+ if (textParseError.ThrowException)
+ {
+ throw textParseError.Exception;
+ }
+ }
+
+ return null;
+ }
+
+ private string? DateTimeToString(DateTime d)
+ {
+ DateTimeFormatInfo dtfi = DateTimeHelper.GetCurrentDateFormat();
+
+ switch (SelectedDateFormat)
+ {
+ case CalendarDatePickerFormat.Short:
+ return string.Format(CultureInfo.CurrentCulture, d.ToString(dtfi.ShortDatePattern, dtfi));
+ case CalendarDatePickerFormat.Long:
+ return string.Format(CultureInfo.CurrentCulture, d.ToString(dtfi.LongDatePattern, dtfi));
+ case CalendarDatePickerFormat.Custom:
+ return string.Format(CultureInfo.CurrentCulture, d.ToString(CustomDateFormatString, dtfi));
+ }
+
+ return null;
+ }
+
+ private bool ProcessDatePickerKey(KeyEventArgs e)
+ {
+ switch (e.Key)
+ {
+ case Key.Enter:
+ {
+ SetSelectedDate();
+ return true;
+ }
+ case Key.Down:
+ {
+ if ((e.KeyModifiers & KeyModifiers.Control) == KeyModifiers.Control)
+ {
+ TogglePopUp();
+ return true;
+ }
+ break;
+ }
+ }
+
+ return false;
+ }
+
+ private void SetSelectedDate()
+ {
+ if (_textBox != null)
+ {
+ if (!string.IsNullOrEmpty(_textBox.Text))
+ {
+ string s = _textBox.Text;
+
+ if (SelectedDate != null)
+ {
+ // If the string value of the SelectedDate and the
+ // TextBox string value are equal, we do not parse the
+ // string again if we do an extra parse, we lose data in
+ // M/d/yy format.
+ // ex: SelectedDate = DateTime(1008,12,19) but when
+ // "12/19/08" is parsed it is interpreted as
+ // DateTime(2008,12,19)
+ string? selectedDate = DateTimeToString(SelectedDate.Value);
+ if (selectedDate == s)
+ {
+ return;
+ }
+ }
+ DateTime? d = SetTextBoxValue(s);
+
+ if (SelectedDate != d)
+ {
+ SelectedDate = d;
+ }
+ }
+ else
+ {
+ if (SelectedDate != null)
+ {
+ SelectedDate = null;
+ }
+ }
+ }
+ else
+ {
+ DateTime? d = SetTextBoxValue(_defaultText);
+
+ if (SelectedDate != d)
+ {
+ SelectedDate = d;
+ }
+ }
+ }
+
+ private DateTime? SetTextBoxValue(string s)
+ {
+ if (string.IsNullOrEmpty(s))
+ {
+ SetValue(TextProperty, s);
+ return SelectedDate;
+ }
+ else
+ {
+ DateTime? d = ParseText(s);
+ if (d != null)
+ {
+ SetValue(TextProperty, s);
+ return d;
+ }
+ else
+ {
+ // If parse error: TextBox should have the latest valid
+ // SelectedDate value:
+ if (SelectedDate != null)
+ {
+ string? newtext = this.DateTimeToString(SelectedDate.Value);
+ SetValue(TextProperty, newtext);
+ return SelectedDate;
+ }
+ else
+ {
+ SetWaterMarkText();
+ return null;
+ }
+ }
+ }
+ }
+
+ private void SetWaterMarkText()
+ {
+ if (_textBox != null)
+ {
+ if (string.IsNullOrEmpty(Watermark) && !UseFloatingWatermark)
+ {
+ DateTimeFormatInfo dtfi = DateTimeHelper.GetCurrentDateFormat();
+ Text = string.Empty;
+ _defaultText = string.Empty;
+ var watermarkFormat = "<{0}>";
+ string watermarkText;
+
+ switch (SelectedDateFormat)
+ {
+ case CalendarDatePickerFormat.Long:
+ {
+ watermarkText = string.Format(CultureInfo.CurrentCulture, watermarkFormat, dtfi.LongDatePattern.ToString());
+ break;
+ }
+ case CalendarDatePickerFormat.Short:
+ default:
+ {
+ watermarkText = string.Format(CultureInfo.CurrentCulture, watermarkFormat, dtfi.ShortDatePattern.ToString());
+ break;
+ }
+ }
+ _textBox.Watermark = watermarkText;
+ }
+ else
+ {
+ _textBox.ClearValue(TextBox.WatermarkProperty);
+ }
+ }
+ }
+
+ private static bool IsValidSelectedDateFormat(CalendarDatePickerFormat value)
+ {
+ return value == CalendarDatePickerFormat.Long
+ || value == CalendarDatePickerFormat.Short
+ || value == CalendarDatePickerFormat.Custom;
+ }
+
+ private static bool IsValidDateFormatString(string formatString)
+ {
+ return !string.IsNullOrWhiteSpace(formatString);
+ }
+ }
+}
diff --git a/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePickerDateValidationErrorEventArgs.cs b/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePickerDateValidationErrorEventArgs.cs
new file mode 100644
index 0000000000..647910cb6b
--- /dev/null
+++ b/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePickerDateValidationErrorEventArgs.cs
@@ -0,0 +1,87 @@
+// (c) Copyright Microsoft Corporation.
+// This source is subject to the Microsoft Public License (Ms-PL).
+// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
+// All other rights reserved.
+
+using System;
+
+namespace Avalonia.Controls
+{
+ ///
+ /// Provides data for the
+ ///
+ /// event.
+ ///
+ public class CalendarDatePickerDateValidationErrorEventArgs : EventArgs
+ {
+ private bool _throwException;
+
+ ///
+ /// Initializes a new instance of the
+ ///
+ /// class.
+ ///
+ ///
+ /// The initial exception from the
+ ///
+ /// event.
+ ///
+ ///
+ /// The text that caused the
+ ///
+ /// event.
+ ///
+ public CalendarDatePickerDateValidationErrorEventArgs(Exception exception, string text)
+ {
+ Text = text;
+ Exception = exception;
+ }
+
+ ///
+ /// Gets the initial exception associated with the
+ ///
+ /// event.
+ ///
+ ///
+ /// The exception associated with the validation failure.
+ ///
+ public Exception Exception { get; private set; }
+
+ ///
+ /// Gets the text that caused the
+ ///
+ /// event.
+ ///
+ ///
+ /// The text that caused the validation failure.
+ ///
+ public string Text { get; private set; }
+
+ ///
+ /// Gets or sets a value indicating whether
+ ///
+ /// should be thrown.
+ ///
+ ///
+ /// True if the exception should be thrown; otherwise, false.
+ ///
+ ///
+ /// If set to true and
+ ///
+ /// is null.
+ ///
+ public bool ThrowException
+ {
+ get => _throwException;
+ set
+ {
+ if (value && Exception == null)
+ {
+ throw new ArgumentException("Cannot Throw Null Exception");
+ }
+
+ _throwException = value;
+ }
+ }
+ }
+}
diff --git a/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePickerFormat.cs b/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePickerFormat.cs
new file mode 100644
index 0000000000..4d96859d75
--- /dev/null
+++ b/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePickerFormat.cs
@@ -0,0 +1,31 @@
+// (c) Copyright Microsoft Corporation.
+// This source is subject to the Microsoft Public License (Ms-PL).
+// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
+// All other rights reserved.
+
+namespace Avalonia.Controls
+{
+ ///
+ /// Specifies date formats for a
+ /// .
+ ///
+ public enum CalendarDatePickerFormat
+ {
+ ///
+ /// Specifies that the date should be displayed using unabbreviated days
+ /// of the week and month names.
+ ///
+ Long = 0,
+
+ ///
+ /// Specifies that the date should be displayed using abbreviated days
+ /// of the week and month names.
+ ///
+ Short = 1,
+
+ ///
+ /// Specifies that the date should be displayed using a custom format string.
+ ///
+ Custom = 2
+ }
+}
diff --git a/src/Avalonia.Controls/Canvas.cs b/src/Avalonia.Controls/Canvas.cs
index fabf8978c7..adee7d4d90 100644
--- a/src/Avalonia.Controls/Canvas.cs
+++ b/src/Avalonia.Controls/Canvas.cs
@@ -1,4 +1,5 @@
using System;
+using System.Reactive.Concurrency;
using Avalonia.Input;
using Avalonia.Layout;
@@ -159,47 +160,57 @@ namespace Avalonia.Controls
}
///
- /// Arranges the control's children.
+ /// Arranges a single child.
///
- /// The size allocated to the control.
- /// The space taken.
- protected override Size ArrangeOverride(Size finalSize)
+ /// The child to arrange.
+ /// The size allocated to the canvas.
+ protected virtual void ArrangeChild(Control child, Size finalSize)
{
- foreach (Control child in Children)
- {
- double x = 0.0;
- double y = 0.0;
- double elementLeft = GetLeft(child);
+ double x = 0.0;
+ double y = 0.0;
+ double elementLeft = GetLeft(child);
- if (!double.IsNaN(elementLeft))
- {
- x = elementLeft;
- }
- else
+ if (!double.IsNaN(elementLeft))
+ {
+ x = elementLeft;
+ }
+ else
+ {
+ // Arrange with right.
+ double elementRight = GetRight(child);
+ if (!double.IsNaN(elementRight))
{
- // Arrange with right.
- double elementRight = GetRight(child);
- if (!double.IsNaN(elementRight))
- {
- x = finalSize.Width - child.DesiredSize.Width - elementRight;
- }
+ x = finalSize.Width - child.DesiredSize.Width - elementRight;
}
+ }
- double elementTop = GetTop(child);
- if (!double.IsNaN(elementTop) )
- {
- y = elementTop;
- }
- else
+ double elementTop = GetTop(child);
+ if (!double.IsNaN(elementTop))
+ {
+ y = elementTop;
+ }
+ else
+ {
+ double elementBottom = GetBottom(child);
+ if (!double.IsNaN(elementBottom))
{
- double elementBottom = GetBottom(child);
- if (!double.IsNaN(elementBottom))
- {
- y = finalSize.Height - child.DesiredSize.Height - elementBottom;
- }
+ y = finalSize.Height - child.DesiredSize.Height - elementBottom;
}
+ }
- child.Arrange(new Rect(new Point(x, y), child.DesiredSize));
+ child.Arrange(new Rect(new Point(x, y), child.DesiredSize));
+ }
+
+ ///
+ /// Arranges the control's children.
+ ///
+ /// The size allocated to the control.
+ /// The space taken.
+ protected override Size ArrangeOverride(Size finalSize)
+ {
+ foreach (Control child in Children)
+ {
+ ArrangeChild(child, finalSize);
}
return finalSize;
diff --git a/src/Avalonia.Controls/CheckBox.cs b/src/Avalonia.Controls/CheckBox.cs
index 05d49a44b1..238a21393f 100644
--- a/src/Avalonia.Controls/CheckBox.cs
+++ b/src/Avalonia.Controls/CheckBox.cs
@@ -1,3 +1,5 @@
+using Avalonia.Automation;
+using Avalonia.Automation.Peers;
using Avalonia.Controls.Primitives;
namespace Avalonia.Controls
@@ -7,5 +9,9 @@ namespace Avalonia.Controls
///
public class CheckBox : ToggleButton
{
+ static CheckBox()
+ {
+ AutomationProperties.ControlTypeOverrideProperty.OverrideDefaultValue(AutomationControlType.CheckBox);
+ }
}
}
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 72b09b7a3c..cbf9b35a05 100644
--- a/src/Avalonia.Controls/ComboBox.cs
+++ b/src/Avalonia.Controls/ComboBox.cs
@@ -1,5 +1,6 @@
using System;
using System.Linq;
+using Avalonia.Automation.Peers;
using System.Reactive.Disposables;
using Avalonia.Controls.Generators;
using Avalonia.Controls.Mixins;
@@ -12,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
{
///
@@ -181,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)
{
@@ -295,6 +317,11 @@ namespace Avalonia.Controls
_popup.Closed += PopupClosed;
}
+ protected override AutomationPeer OnCreateAutomationPeer()
+ {
+ return new ComboBoxAutomationPeer(this);
+ }
+
internal void ItemFocused(ComboBoxItem dropDownItem)
{
if (IsDropDownOpen && dropDownItem.IsFocused && dropDownItem.IsArrangeValid)
@@ -426,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/ComboBoxItem.cs b/src/Avalonia.Controls/ComboBoxItem.cs
index a0a1f2a4aa..83057d139f 100644
--- a/src/Avalonia.Controls/ComboBoxItem.cs
+++ b/src/Avalonia.Controls/ComboBoxItem.cs
@@ -1,5 +1,7 @@
using System;
using System.Reactive.Linq;
+using Avalonia.Automation;
+using Avalonia.Automation.Peers;
namespace Avalonia.Controls
{
@@ -13,5 +15,10 @@ namespace Avalonia.Controls
this.GetObservable(ComboBoxItem.IsFocusedProperty).Where(focused => focused)
.Subscribe(_ => (Parent as ComboBox)?.ItemFocused(this));
}
+
+ static ComboBoxItem()
+ {
+ AutomationProperties.ControlTypeOverrideProperty.OverrideDefaultValue(AutomationControlType.ComboBoxItem);
+ }
}
}
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/ContextMenu.cs b/src/Avalonia.Controls/ContextMenu.cs
index dff06a6369..2b122d4174 100644
--- a/src/Avalonia.Controls/ContextMenu.cs
+++ b/src/Avalonia.Controls/ContextMenu.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
+using Avalonia.Automation.Peers;
using System.Linq;
using Avalonia.Controls.Diagnostics;
using Avalonia.Controls.Generators;
@@ -13,6 +14,7 @@ using Avalonia.Input.Platform;
using Avalonia.Interactivity;
using Avalonia.Layout;
using Avalonia.Styling;
+using Avalonia.Automation;
namespace Avalonia.Controls
{
@@ -61,7 +63,7 @@ namespace Avalonia.Controls
/// Defines the property.
///
public static readonly StyledProperty PlacementRectProperty =
- AvaloniaProperty.Register(nameof(PlacementRect));
+ Popup.PlacementRectProperty.AddOwner();
///
/// Defines the property.
@@ -107,6 +109,8 @@ namespace Avalonia.Controls
ItemsPanelProperty.OverrideDefaultValue(DefaultPanel);
PlacementModeProperty.OverrideDefaultValue(PlacementMode.Pointer);
ContextMenuProperty.Changed.Subscribe(ContextMenuChanged);
+ AutomationProperties.AccessibilityViewProperty.OverrideDefaultValue(AccessibilityView.Control);
+ AutomationProperties.ControlTypeOverrideProperty.OverrideDefaultValue(AutomationControlType.Menu);
}
///
@@ -237,13 +241,13 @@ namespace Avalonia.Controls
}
}
- protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
if (change.Property == WindowManagerAddShadowHintProperty && _popup != null)
{
- _popup.WindowManagerAddShadowHint = change.NewValue.GetValueOrDefault();
+ _popup.WindowManagerAddShadowHint = change.GetNewValue();
}
}
@@ -347,6 +351,11 @@ namespace Avalonia.Controls
? PlacementMode.Bottom
: PlacementMode;
+ //Position of the line below is really important.
+ //All styles are being applied only when control has logical parent.
+ //Line below will add ContextMenu as child to the Popup and this will trigger styles and they would be applied.
+ //If you will move line below somewhere else it may cause that ContextMenu will behave differently from what you are expecting.
+ _popup.Child = this;
_popup.PlacementTarget = placementTarget;
_popup.HorizontalOffset = HorizontalOffset;
_popup.VerticalOffset = VerticalOffset;
@@ -355,7 +364,6 @@ namespace Avalonia.Controls
_popup.PlacementGravity = PlacementGravity;
_popup.PlacementRect = PlacementRect;
_popup.WindowManagerAddShadowHint = WindowManagerAddShadowHint;
- _popup.Child = this;
IsOpen = true;
_popup.IsOpen = true;
diff --git a/src/Avalonia.Controls/Control.cs b/src/Avalonia.Controls/Control.cs
index b9f3bd8890..d6a5fa0727 100644
--- a/src/Avalonia.Controls/Control.cs
+++ b/src/Avalonia.Controls/Control.cs
@@ -1,6 +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;
@@ -66,9 +67,10 @@ namespace Avalonia.Controls
///
public static readonly AttachedProperty FlowDirectionProperty =
AvaloniaProperty.RegisterAttached(nameof(FlowDirection), inherits: true);
-
+
private DataTemplates? _dataTemplates;
private IControl? _focusAdorner;
+ private AutomationPeer? _automationPeer;
///
/// Gets or sets the control's focus adorner.
@@ -135,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)
{
@@ -197,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)
{
@@ -242,6 +282,24 @@ namespace Avalonia.Controls
}
}
+ protected virtual AutomationPeer OnCreateAutomationPeer()
+ {
+ return new NoneAutomationPeer(this);
+ }
+
+ internal AutomationPeer GetOrCreateAutomationPeer()
+ {
+ VerifyAccess();
+
+ if (_automationPeer is object)
+ {
+ return _automationPeer;
+ }
+
+ _automationPeer = OnCreateAutomationPeer();
+ return _automationPeer;
+ }
+
protected override void OnPointerReleased(PointerReleasedEventArgs e)
{
base.OnPointerReleased(e);
@@ -289,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/Converters/CornerRadiusFilterConverter.cs b/src/Avalonia.Controls/Converters/CornerRadiusFilterConverter.cs
index 643c30178e..a91f143019 100644
--- a/src/Avalonia.Controls/Converters/CornerRadiusFilterConverter.cs
+++ b/src/Avalonia.Controls/Converters/CornerRadiusFilterConverter.cs
@@ -1,27 +1,32 @@
using System;
using System.Globalization;
-
using Avalonia.Data.Converters;
namespace Avalonia.Controls.Converters
{
///
/// Converts an existing CornerRadius struct to a new CornerRadius struct,
- /// with filters applied to extract only the specified fields, leaving the others set to 0.
+ /// with filters applied to extract only the specified corners, leaving the others set to 0.
///
public class CornerRadiusFilterConverter : IValueConverter
{
///
- /// Gets or sets the type of the filter applied to the .
+ /// Gets or sets the corners to filter by.
+ /// Only the specified corners will be included in the converted .
///
- public CornerRadiusFilterKinds Filter { get; set; }
+ public Corners Filter { get; set; }
///
- /// Gets or sets the scale multiplier applied to the .
+ /// Gets or sets the scale multiplier applied uniformly to each corner.
///
public double Scale { get; set; } = 1;
- public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
+ ///
+ public object? Convert(
+ object? value,
+ Type targetType,
+ object? parameter,
+ CultureInfo culture)
{
if (!(value is CornerRadius radius))
{
@@ -29,13 +34,18 @@ namespace Avalonia.Controls.Converters
}
return new CornerRadius(
- Filter.HasAllFlags(CornerRadiusFilterKinds.TopLeft) ? radius.TopLeft * Scale : 0,
- Filter.HasAllFlags(CornerRadiusFilterKinds.TopRight) ? radius.TopRight * Scale : 0,
- Filter.HasAllFlags(CornerRadiusFilterKinds.BottomRight) ? radius.BottomRight * Scale : 0,
- Filter.HasAllFlags(CornerRadiusFilterKinds.BottomLeft) ? radius.BottomLeft * Scale : 0);
+ Filter.HasAllFlags(Corners.TopLeft) ? radius.TopLeft * Scale : 0,
+ Filter.HasAllFlags(Corners.TopRight) ? radius.TopRight * Scale : 0,
+ Filter.HasAllFlags(Corners.BottomRight) ? radius.BottomRight * Scale : 0,
+ Filter.HasAllFlags(Corners.BottomLeft) ? radius.BottomLeft * Scale : 0);
}
- public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
+ ///
+ public object? ConvertBack(
+ object? value,
+ Type targetType,
+ object? parameter,
+ CultureInfo culture)
{
throw new NotImplementedException();
}
diff --git a/src/Avalonia.Controls/Converters/CornerRadiusFilterKind.cs b/src/Avalonia.Controls/Converters/CornerRadiusFilterKind.cs
deleted file mode 100644
index 6a9d0596be..0000000000
--- a/src/Avalonia.Controls/Converters/CornerRadiusFilterKind.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using System;
-
-namespace Avalonia.Controls.Converters
-{
- ///
- /// Defines constants that specify the filter type for a instance.
- ///
- [Flags]
- public enum CornerRadiusFilterKinds
- {
- ///
- /// No filter applied.
- ///
- None,
- ///
- /// Filters TopLeft value.
- ///
- TopLeft = 1,
- ///
- /// Filters TopRight value.
- ///
- TopRight = 2,
- ///
- /// Filters BottomLeft value.
- ///
- BottomLeft = 4,
- ///
- /// Filters BottomRight value.
- ///
- BottomRight = 8
- }
-}
diff --git a/src/Avalonia.Controls/Converters/CornerRadiusToDoubleConverter.cs b/src/Avalonia.Controls/Converters/CornerRadiusToDoubleConverter.cs
new file mode 100644
index 0000000000..6da15b61e6
--- /dev/null
+++ b/src/Avalonia.Controls/Converters/CornerRadiusToDoubleConverter.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Globalization;
+using Avalonia.Data.Converters;
+
+namespace Avalonia.Controls.Converters
+{
+ ///
+ /// Converts one corner of a to its double value.
+ ///
+ public class CornerRadiusToDoubleConverter : IValueConverter
+ {
+ ///
+ /// Gets or sets the specific corner of the to convert to double.
+ ///
+ public Corners Corner { get; set; }
+
+ ///
+ public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
+ {
+ if (!(value is CornerRadius cornerRadius))
+ {
+ return AvaloniaProperty.UnsetValue;
+ }
+
+ switch (Corner)
+ {
+ case Corners.TopLeft:
+ return cornerRadius.TopLeft;
+ case Corners.TopRight:
+ return cornerRadius.TopRight;
+ case Corners.BottomRight:
+ return cornerRadius.BottomRight;
+ case Corners.BottomLeft:
+ return cornerRadius.BottomLeft;
+ default:
+ return 0.0;
+ }
+ }
+
+ ///
+ public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/src/Avalonia.Controls/Converters/Corners.cs b/src/Avalonia.Controls/Converters/Corners.cs
new file mode 100644
index 0000000000..22ac4cb5f9
--- /dev/null
+++ b/src/Avalonia.Controls/Converters/Corners.cs
@@ -0,0 +1,36 @@
+using System;
+
+namespace Avalonia.Controls.Converters
+{
+ ///
+ /// Defines constants that specify one or more corners of a .
+ ///
+ [Flags]
+ public enum Corners
+ {
+ ///
+ /// No corner.
+ ///
+ None,
+
+ ///
+ /// The TopLeft corner.
+ ///
+ TopLeft = 1,
+
+ ///
+ /// The TopRight corner.
+ ///
+ TopRight = 2,
+
+ ///
+ /// The BottomLeft corner.
+ ///
+ BottomLeft = 4,
+
+ ///
+ /// The BottomRight corner.
+ ///
+ BottomRight = 8
+ }
+}
diff --git a/src/Avalonia.Controls/Converters/EnumValueEqualsConverter.cs b/src/Avalonia.Controls/Converters/EnumValueEqualsConverter.cs
new file mode 100644
index 0000000000..1a33a82ca4
--- /dev/null
+++ b/src/Avalonia.Controls/Converters/EnumValueEqualsConverter.cs
@@ -0,0 +1,54 @@
+using System;
+using System.Globalization;
+using Avalonia.Data.Converters;
+
+namespace Avalonia.Controls.Converters
+{
+ ///
+ /// Converter that checks if an enum value is equal to the given parameter enum value.
+ ///
+ public class EnumValueEqualsConverter : IValueConverter
+ {
+ ///
+ public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
+ {
+ // Note: Unlike string comparisons, null/empty is not supported
+ // Both 'value' and 'parameter' must exist and if both are missing they are not considered equal
+ if (value != null &&
+ parameter != null)
+ {
+ Type type = value.GetType();
+
+ if (type.IsEnum)
+ {
+ var valueStr = value?.ToString();
+ var paramStr = parameter?.ToString();
+
+ if (string.Equals(valueStr, paramStr, StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+ }
+
+ /*
+ // TODO: When .net Standard 2.0 is no longer supported the code can be changed to below
+ // This is a little more type safe
+ if (type.IsEnum &&
+ Enum.TryParse(type, value?.ToString(), true, out object? valueEnum) &&
+ Enum.TryParse(type, parameter?.ToString(), true, out object? paramEnum))
+ {
+ return valueEnum == paramEnum;
+ }
+ */
+ }
+
+ return false;
+ }
+
+ ///
+ public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
+ {
+ throw new System.NotImplementedException();
+ }
+ }
+}
diff --git a/src/Avalonia.Controls/Converters/MarginMultiplierConverter.cs b/src/Avalonia.Controls/Converters/MarginMultiplierConverter.cs
index b0c30ea11f..7931b63d8e 100644
--- a/src/Avalonia.Controls/Converters/MarginMultiplierConverter.cs
+++ b/src/Avalonia.Controls/Converters/MarginMultiplierConverter.cs
@@ -35,7 +35,6 @@ namespace Avalonia.Controls.Converters
Bottom ? Indent * thicknessDepth.Bottom : 0);
}
return new Thickness(0);
-
}
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
diff --git a/src/Avalonia.Controls/DateTimePickers/DatePicker.cs b/src/Avalonia.Controls/DateTimePickers/DatePicker.cs
index 8c6ac17280..f2b808fe0d 100644
--- a/src/Avalonia.Controls/DateTimePickers/DatePicker.cs
+++ b/src/Avalonia.Controls/DateTimePickers/DatePicker.cs
@@ -4,6 +4,7 @@ using Avalonia.Controls.Shapes;
using Avalonia.Controls.Templates;
using Avalonia.Data;
using Avalonia.Interactivity;
+using Avalonia.Layout;
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -13,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
{
@@ -398,18 +408,27 @@ namespace Avalonia.Controls
private void OnFlyoutButtonClicked(object? sender, RoutedEventArgs e)
{
if (_presenter == null)
- throw new InvalidOperationException("No DatePickerPresenter found");
+ throw new InvalidOperationException("No DatePickerPresenter found.");
+ if (_popup == null)
+ throw new InvalidOperationException("No Popup found.");
_presenter.Date = SelectedDate ?? DateTimeOffset.Now;
- _popup!.IsOpen = true;
+ _popup.PlacementMode = PlacementMode.AnchorAndGravity;
+ _popup.PlacementAnchor = Primitives.PopupPositioning.PopupAnchor.Bottom;
+ _popup.PlacementGravity = Primitives.PopupPositioning.PopupGravity.Bottom;
+ _popup.PlacementConstraintAdjustment = Primitives.PopupPositioning.PopupPositionerConstraintAdjustment.SlideY;
+ _popup.IsOpen = true;
+
+ // Overlay popup hosts won't get measured until the next layout pass, but we need the
+ // template to be applied to `_presenter` now. Detect this case and force a layout pass.
+ if (!_presenter.IsMeasureValid)
+ (VisualRoot as ILayoutRoot)?.LayoutManager?.ExecuteInitialLayoutPass();
var deltaY = _presenter.GetOffsetForPopup();
// The extra 5 px I think is related to default popup placement behavior
- _popup!.Host!.ConfigurePosition(_popup.PlacementTarget!, PlacementMode.AnchorAndGravity, new Point(0, deltaY + 5),
- Primitives.PopupPositioning.PopupAnchor.Bottom, Primitives.PopupPositioning.PopupGravity.Bottom,
- Primitives.PopupPositioning.PopupPositionerConstraintAdjustment.SlideY);
+ _popup.VerticalOffset = deltaY + 5;
}
protected virtual void OnSelectedDateChanged(object? sender, DatePickerSelectedValueChangedEventArgs e)
diff --git a/src/Avalonia.Controls/DateTimePickers/DatePickerPresenter.cs b/src/Avalonia.Controls/DateTimePickers/DatePickerPresenter.cs
index eac6d83074..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
{
///
@@ -537,8 +555,11 @@ namespace Avalonia.Controls
internal double GetOffsetForPopup()
{
+ if (_monthSelector is null)
+ return 0;
+
var acceptDismissButtonHeight = _acceptButton != null ? _acceptButton.Bounds.Height : 41;
- return -(MaxHeight - acceptDismissButtonHeight) / 2 - (_monthSelector!.ItemHeight / 2);
+ return -(MaxHeight - acceptDismissButtonHeight) / 2 - (_monthSelector.ItemHeight / 2);
}
}
}
diff --git a/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs b/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs
index 5948d81810..667f994a1d 100644
--- a/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs
+++ b/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs
@@ -1,7 +1,11 @@
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;
namespace Avalonia.Controls.Primitives
@@ -58,18 +62,18 @@ namespace Avalonia.Controls.Primitives
private Vector _offset;
private bool _hasInit;
private bool _suppressUpdateOffset;
- private ListBoxItem? _pressedItem;
+ private ScrollContentPresenter? _parentScroller;
public DateTimePickerPanel()
{
FormatDate = DateTime.Now;
- AddHandler(ListBoxItem.PointerPressedEvent, OnItemPointerDown, Avalonia.Interactivity.RoutingStrategies.Bubble);
- AddHandler(ListBoxItem.PointerReleasedEvent, OnItemPointerUp, Avalonia.Interactivity.RoutingStrategies.Bubble);
+ AddHandler(TappedEvent, OnItemTapped, RoutingStrategies.Bubble);
}
static DateTimePickerPanel()
{
FocusableProperty.OverrideDefaultValue(true);
+ BackgroundProperty.OverrideDefaultValue(Brushes.Transparent);
AffectsMeasure(ItemHeightProperty);
}
@@ -254,6 +258,8 @@ namespace Avalonia.Controls.Primitives
_suppressUpdateOffset = true;
SelectedValue = (int)newSel * Increment + MinimumValue;
_suppressUpdateOffset = false;
+
+ System.Diagnostics.Debug.WriteLine($"Offset: {_offset} ItemHeight: {ItemHeight}");
}
}
@@ -269,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;
@@ -340,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)
@@ -523,26 +543,13 @@ namespace Avalonia.Controls.Primitives
return newValue;
}
- private void OnItemPointerDown(object? sender, PointerPressedEventArgs e)
+ private void OnItemTapped(object? sender, TappedEventArgs e)
{
- if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed &&
- e.Source is IVisual source)
- {
- _pressedItem = GetItemFromSource(source);
- e.Handled = true;
- }
- }
-
- private void OnItemPointerUp(object? sender, PointerReleasedEventArgs e)
- {
- if (e.GetCurrentPoint(this).Properties.PointerUpdateKind == PointerUpdateKind.LeftButtonReleased &&
- _pressedItem != null &&
- e.Source is IVisual source &&
- GetItemFromSource(source) is ListBoxItem item &&
- item.Tag is int tag)
+ if (e.Source is IVisual source &&
+ GetItemFromSource(source) is ListBoxItem listBoxItem &&
+ listBoxItem.Tag is int tag)
{
SelectedValue = tag;
- _pressedItem = null;
e.Handled = true;
}
}
@@ -566,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 a0a27bd4ed..047667567d 100644
--- a/src/Avalonia.Controls/DateTimePickers/TimePicker.cs
+++ b/src/Avalonia.Controls/DateTimePickers/TimePicker.cs
@@ -3,6 +3,7 @@ using Avalonia.Controls.Primitives;
using Avalonia.Controls.Shapes;
using Avalonia.Controls.Templates;
using Avalonia.Data;
+using Avalonia.Layout;
using System;
using System.Globalization;
@@ -11,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
{
@@ -25,13 +38,13 @@ namespace Avalonia.Controls
/// Defines the property
///
public static readonly StyledProperty HeaderProperty =
- AvaloniaProperty.Register(nameof(Header));
+ AvaloniaProperty.Register(nameof(Header));
///
/// Defines the property
///
public static readonly StyledProperty HeaderTemplateProperty =
- AvaloniaProperty.Register(nameof(HeaderTemplate));
+ AvaloniaProperty.Register(nameof(HeaderTemplate));
///
/// Defines the property
@@ -254,16 +267,28 @@ namespace Avalonia.Controls
private void OnFlyoutButtonClicked(object? sender, Interactivity.RoutedEventArgs e)
{
- _presenter!.Time = SelectedTime ?? DateTime.Now.TimeOfDay;
+ if (_presenter == null)
+ throw new InvalidOperationException("No DatePickerPresenter found.");
+ if (_popup == null)
+ throw new InvalidOperationException("No Popup found.");
- _popup!.IsOpen = true;
+ _presenter.Time = SelectedTime ?? DateTime.Now.TimeOfDay;
+
+ _popup.PlacementMode = PlacementMode.AnchorAndGravity;
+ _popup.PlacementAnchor = Primitives.PopupPositioning.PopupAnchor.Bottom;
+ _popup.PlacementGravity = Primitives.PopupPositioning.PopupGravity.Bottom;
+ _popup.PlacementConstraintAdjustment = Primitives.PopupPositioning.PopupPositionerConstraintAdjustment.SlideY;
+ _popup.IsOpen = true;
+
+ // Overlay popup hosts won't get measured until the next layout pass, but we need the
+ // template to be applied to `_presenter` now. Detect this case and force a layout pass.
+ if (!_presenter.IsMeasureValid)
+ (VisualRoot as ILayoutRoot)?.LayoutManager?.ExecuteInitialLayoutPass();
var deltaY = _presenter.GetOffsetForPopup();
// The extra 5 px I think is related to default popup placement behavior
- _popup.Host!.ConfigurePosition(_popup.PlacementTarget!, PlacementMode.AnchorAndGravity, new Point(0, deltaY + 5),
- Primitives.PopupPositioning.PopupAnchor.Bottom, Primitives.PopupPositioning.PopupGravity.Bottom,
- Primitives.PopupPositioning.PopupPositionerConstraintAdjustment.SlideY);
+ _popup.VerticalOffset = deltaY + 5;
}
private void OnDismissPicker(object? sender, EventArgs e)
diff --git a/src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs b/src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs
index 55bc1af3a7..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
{
///
@@ -256,8 +271,11 @@ namespace Avalonia.Controls
internal double GetOffsetForPopup()
{
+ if (_hourSelector is null)
+ return 0;
+
var acceptDismissButtonHeight = _acceptButton != null ? _acceptButton.Bounds.Height : 41;
- return -(MaxHeight - acceptDismissButtonHeight) / 2 - (_hourSelector!.ItemHeight / 2);
+ return -(MaxHeight - acceptDismissButtonHeight) / 2 - (_hourSelector.ItemHeight / 2);
}
}
}
diff --git a/src/Avalonia.Controls/Diagnostics/IPopupHostProvider.cs b/src/Avalonia.Controls/Diagnostics/IPopupHostProvider.cs
index 45cd1d727e..64978248e5 100644
--- a/src/Avalonia.Controls/Diagnostics/IPopupHostProvider.cs
+++ b/src/Avalonia.Controls/Diagnostics/IPopupHostProvider.cs
@@ -1,11 +1,13 @@
using System;
using Avalonia.Controls.Primitives;
+using Avalonia.Metadata;
namespace Avalonia.Controls.Diagnostics
{
///
/// Diagnostics interface to retrieve an associated .
///
+ [NotClientImplementable]
public interface IPopupHostProvider
{
///
diff --git a/src/Avalonia.Controls/DockPanel.cs b/src/Avalonia.Controls/DockPanel.cs
index 8e23555c2d..3e3ed509b5 100644
--- a/src/Avalonia.Controls/DockPanel.cs
+++ b/src/Avalonia.Controls/DockPanel.cs
@@ -34,7 +34,7 @@ namespace Avalonia.Controls
///
public static readonly StyledProperty LastChildFillProperty =
AvaloniaProperty.Register(
- nameof(LastChildFillProperty),
+ nameof(LastChildFill),
defaultValue: true);
///
diff --git a/src/Avalonia.Controls/Documents/Bold.cs b/src/Avalonia.Controls/Documents/Bold.cs
new file mode 100644
index 0000000000..7d0a9130ae
--- /dev/null
+++ b/src/Avalonia.Controls/Documents/Bold.cs
@@ -0,0 +1,17 @@
+using Avalonia.Media;
+
+namespace Avalonia.Controls.Documents
+{
+ ///
+ /// Bold element - markup helper for indicating bolded content.
+ /// Equivalent to a Span with FontWeight property set to FontWeights.Bold.
+ /// Can contain other inline elements.
+ ///
+ public sealed class Bold : Span
+ {
+ static Bold()
+ {
+ FontWeightProperty.OverrideDefaultValue(FontWeight.Bold);
+ }
+ }
+}
diff --git a/src/Avalonia.Controls/Documents/IInlineHost.cs b/src/Avalonia.Controls/Documents/IInlineHost.cs
new file mode 100644
index 0000000000..da72c207be
--- /dev/null
+++ b/src/Avalonia.Controls/Documents/IInlineHost.cs
@@ -0,0 +1,11 @@
+using Avalonia.LogicalTree;
+
+namespace Avalonia.Controls.Documents
+{
+ internal interface IInlineHost : ILogical
+ {
+ void AddVisualChild(IControl child);
+
+ void Invalidate();
+ }
+}
diff --git a/src/Avalonia.Controls/Documents/Inline.cs b/src/Avalonia.Controls/Documents/Inline.cs
new file mode 100644
index 0000000000..b400625903
--- /dev/null
+++ b/src/Avalonia.Controls/Documents/Inline.cs
@@ -0,0 +1,70 @@
+using System.Collections.Generic;
+using System.Text;
+using Avalonia.Media;
+using Avalonia.Media.TextFormatting;
+
+namespace Avalonia.Controls.Documents
+{
+ ///
+ /// Inline element.
+ ///
+ public abstract class Inline : TextElement
+ {
+ ///
+ /// AvaloniaProperty for property.
+ ///
+ public static readonly StyledProperty TextDecorationsProperty =
+ AvaloniaProperty.Register(
+ nameof(TextDecorations));
+
+ ///
+ /// AvaloniaProperty for property.
+ ///
+ public static readonly StyledProperty BaselineAlignmentProperty =
+ AvaloniaProperty.Register(
+ nameof(BaselineAlignment),
+ BaselineAlignment.Baseline);
+
+ ///
+ /// The TextDecorations property specifies decorations that are added to the text of an element.
+ ///
+ public TextDecorationCollection TextDecorations
+ {
+ get { return GetValue(TextDecorationsProperty); }
+ set { SetValue(TextDecorationsProperty, value); }
+ }
+
+ ///
+ /// Describes how the baseline for a text-based element is positioned on the vertical axis,
+ /// relative to the established baseline for text.
+ ///
+ public BaselineAlignment BaselineAlignment
+ {
+ get { return GetValue(BaselineAlignmentProperty); }
+ set { SetValue(BaselineAlignmentProperty, value); }
+ }
+
+ internal abstract void BuildTextRun(IList textRuns);
+
+ internal abstract void AppendText(StringBuilder stringBuilder);
+
+ protected TextRunProperties CreateTextRunProperties()
+ {
+ return new GenericTextRunProperties(new Typeface(FontFamily, FontStyle, FontWeight), FontSize,
+ TextDecorations, Foreground, Background, BaselineAlignment);
+ }
+
+ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+ {
+ base.OnPropertyChanged(change);
+
+ switch (change.Property.Name)
+ {
+ case nameof(TextDecorations):
+ case nameof(BaselineAlignment):
+ InlineHost?.Invalidate();
+ break;
+ }
+ }
+ }
+}
diff --git a/src/Avalonia.Controls/Documents/InlineCollection.cs b/src/Avalonia.Controls/Documents/InlineCollection.cs
new file mode 100644
index 0000000000..a76222385e
--- /dev/null
+++ b/src/Avalonia.Controls/Documents/InlineCollection.cs
@@ -0,0 +1,149 @@
+using System;
+using System.Text;
+using Avalonia.Collections;
+using Avalonia.LogicalTree;
+using Avalonia.Metadata;
+
+namespace Avalonia.Controls.Documents
+{
+ ///
+ /// A collection of s.
+ ///
+ [WhitespaceSignificantCollection]
+ public class InlineCollection : AvaloniaList
+ {
+ private readonly IInlineHost? _host;
+ private string? _text = string.Empty;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public InlineCollection(ILogical parent) : this(parent, null) { }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ internal InlineCollection(ILogical parent, IInlineHost? host = null) : base(0)
+ {
+ _host = host;
+
+ ResetBehavior = ResetBehavior.Remove;
+
+ this.ForEachItem(
+ x =>
+ {
+ ((ISetLogicalParent)x).SetParent(parent);
+ x.InlineHost = host;
+ host?.Invalidate();
+ },
+ x =>
+ {
+ ((ISetLogicalParent)x).SetParent(null);
+ x.InlineHost = host;
+ host?.Invalidate();
+ },
+ () => throw new NotSupportedException());
+ }
+
+ public bool HasComplexContent => Count > 0;
+
+ ///
+ /// Gets or adds the text held by the inlines collection.
+ ///
+ /// Can be null for complex content.
+ ///
+ ///
+ public string? Text
+ {
+ get
+ {
+ if (!HasComplexContent)
+ {
+ return _text;
+ }
+
+ var builder = new StringBuilder();
+
+ foreach(var inline in this)
+ {
+ inline.AppendText(builder);
+ }
+
+ return builder.ToString();
+ }
+ set
+ {
+ if (HasComplexContent)
+ {
+ Add(new Run(value));
+ }
+ else
+ {
+ _text = value;
+ }
+ }
+ }
+
+ ///
+ /// Add a text segment to the collection.
+ ///
+ /// For non complex content this appends the text to the end of currently held text.
+ /// For complex content this adds a to the collection.
+ ///
+ ///
+ ///
+ public void Add(string text)
+ {
+ if (HasComplexContent)
+ {
+ Add(new Run(text));
+ }
+ else
+ {
+ _text += text;
+ }
+ }
+
+ public void Add(IControl child)
+ {
+ var implicitRun = new InlineUIContainer(child);
+
+ Add(implicitRun);
+ }
+
+ public override void Add(Inline item)
+ {
+ if (!HasComplexContent)
+ {
+ if (!string.IsNullOrEmpty(_text))
+ {
+ base.Add(new Run(_text));
+ }
+
+ _text = string.Empty;
+ }
+
+ base.Add(item);
+ }
+
+ ///
+ /// Raised when an inline in the collection changes.
+ ///
+ public event EventHandler? Invalidated;
+
+ ///
+ /// Raises the event.
+ ///
+ protected void Invalidate()
+ {
+ if(_host != null)
+ {
+ _host.Invalidate();
+ }
+
+ Invalidated?.Invoke(this, EventArgs.Empty);
+ }
+
+ private void Invalidate(object? sender, EventArgs e) => Invalidate();
+ }
+}
diff --git a/src/Avalonia.Controls/Documents/InlineUIContainer.cs b/src/Avalonia.Controls/Documents/InlineUIContainer.cs
new file mode 100644
index 0000000000..5f08c23099
--- /dev/null
+++ b/src/Avalonia.Controls/Documents/InlineUIContainer.cs
@@ -0,0 +1,125 @@
+using System.Collections.Generic;
+using System.Text;
+using Avalonia.Media;
+using Avalonia.Media.TextFormatting;
+using Avalonia.Metadata;
+using Avalonia.Utilities;
+
+namespace Avalonia.Controls.Documents
+{
+ ///
+ /// InlineUIContainer - a wrapper for embedded UIElements in text
+ /// flow content inline collections
+ ///
+ public class InlineUIContainer : Inline
+ {
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty ChildProperty =
+ AvaloniaProperty.Register(nameof(Child));
+
+ static InlineUIContainer()
+ {
+ BaselineAlignmentProperty.OverrideDefaultValue(BaselineAlignment.Top);
+ }
+
+ ///
+ /// Initializes a new instance of InlineUIContainer element.
+ ///
+ ///
+ /// The purpose of this element is to be a wrapper for UIElements
+ /// when they are embedded into text flow - as items of
+ /// InlineCollections.
+ ///
+ public InlineUIContainer()
+ {
+ }
+
+ ///
+ /// Initializes an InlineBox specifying its child UIElement
+ ///
+ ///
+ /// UIElement set as a child of this inline item
+ ///
+ public InlineUIContainer(IControl child)
+ {
+ Child = child;
+ }
+
+ ///
+ /// The content spanned by this TextElement.
+ ///
+ [Content]
+ public IControl Child
+ {
+ get => GetValue(ChildProperty);
+ set => SetValue(ChildProperty, value);
+ }
+
+ internal override void BuildTextRun(IList textRuns)
+ {
+ if(InlineHost == null)
+ {
+ return;
+ }
+
+ ((ISetLogicalParent)Child).SetParent(InlineHost);
+
+ InlineHost.AddVisualChild(Child);
+
+ textRuns.Add(new InlineRun(Child, CreateTextRunProperties()));
+ }
+
+ internal override void AppendText(StringBuilder stringBuilder)
+ {
+ }
+
+ private class InlineRun : DrawableTextRun
+ {
+ public InlineRun(IControl control, TextRunProperties properties)
+ {
+ Control = control;
+ Properties = properties;
+ }
+
+ public IControl Control { get; }
+
+ public override TextRunProperties? Properties { get; }
+
+ public override Size Size
+ {
+ get
+ {
+ if (!Control.IsMeasureValid)
+ {
+ Control.Measure(Size.Infinity);
+ }
+
+ return Control.DesiredSize;
+ }
+ }
+
+ public override double Baseline
+ {
+ get
+ {
+ double baseline = Size.Height;
+ double baselineOffsetValue = Control.GetValue(TextBlock.BaselineOffsetProperty);
+
+ if (!MathUtilities.IsZero(baselineOffsetValue))
+ {
+ baseline = baselineOffsetValue;
+ }
+
+ return -baseline;
+ }
+ }
+
+ public override void Draw(DrawingContext drawingContext, Point origin)
+ {
+ Control.Arrange(new Rect(origin, Size));
+ }
+ }
+ }
+}
diff --git a/src/Avalonia.Controls/Documents/Italic.cs b/src/Avalonia.Controls/Documents/Italic.cs
new file mode 100644
index 0000000000..e9f4698fc4
--- /dev/null
+++ b/src/Avalonia.Controls/Documents/Italic.cs
@@ -0,0 +1,17 @@
+using Avalonia.Media;
+
+namespace Avalonia.Controls.Documents
+{
+ ///
+ /// Italic element - markup helper for indicating italicized content.
+ /// Equivalent to a Span with FontStyle property set to FontStyles.Italic.
+ /// Can contain other inline elements.
+ ///
+ public sealed class Italic : Span
+ {
+ static Italic()
+ {
+ FontStyleProperty.OverrideDefaultValue(FontStyle.Italic);
+ }
+ }
+}
diff --git a/src/Avalonia.Controls/Documents/LineBreak.cs b/src/Avalonia.Controls/Documents/LineBreak.cs
new file mode 100644
index 0000000000..aeb81f7313
--- /dev/null
+++ b/src/Avalonia.Controls/Documents/LineBreak.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Avalonia.LogicalTree;
+using Avalonia.Media.TextFormatting;
+using Avalonia.Metadata;
+
+namespace Avalonia.Controls.Documents
+{
+ ///
+ /// LineBreak element that forces a line breaking.
+ ///
+ [TrimSurroundingWhitespace]
+ public class LineBreak : Inline
+ {
+ ///
+ /// Creates a new LineBreak instance.
+ ///
+ public LineBreak()
+ {
+ }
+
+ internal override void BuildTextRun(IList textRuns)
+ {
+ textRuns.Add(new TextEndOfLine());
+ }
+
+ internal override void AppendText(StringBuilder stringBuilder)
+ {
+ stringBuilder.Append(Environment.NewLine);
+ }
+ }
+}
+
diff --git a/src/Avalonia.Controls/Documents/Run.cs b/src/Avalonia.Controls/Documents/Run.cs
new file mode 100644
index 0000000000..2bd66b8a64
--- /dev/null
+++ b/src/Avalonia.Controls/Documents/Run.cs
@@ -0,0 +1,83 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Avalonia.Data;
+using Avalonia.Media.TextFormatting;
+using Avalonia.Metadata;
+
+namespace Avalonia.Controls.Documents
+{
+ ///
+ /// A terminal element in text flow hierarchy - contains a uniformatted run of unicode characters
+ ///
+ public class Run : Inline
+ {
+ ///
+ /// Initializes an instance of Run class.
+ ///
+ public Run()
+ {
+ }
+
+ ///
+ /// Initializes an instance of Run class specifying its text content.
+ ///
+ ///
+ /// Text content assigned to the Run.
+ ///
+ public Run(string? text)
+ {
+ Text = text;
+ }
+
+ ///
+ /// Dependency property backing Text.
+ ///
+ ///
+ /// Note that when a TextRange that intersects with this Run gets modified (e.g. by editing
+ /// a selection in RichTextBox), we will get two changes to this property since we delete
+ /// and then insert when setting the content of a TextRange.
+ ///
+ public static readonly StyledProperty TextProperty = AvaloniaProperty.Register (
+ nameof (Text), defaultBindingMode: BindingMode.TwoWay);
+
+ ///
+ /// The content spanned by this TextElement.
+ ///
+ [Content]
+ public string? Text {
+ get { return GetValue (TextProperty); }
+ set { SetValue (TextProperty, value); }
+ }
+
+ internal override void BuildTextRun(IList textRuns)
+ {
+ var text = (Text ?? "").AsMemory();
+
+ var textRunProperties = CreateTextRunProperties();
+
+ var textCharacters = new TextCharacters(text, textRunProperties);
+
+ textRuns.Add(textCharacters);
+ }
+
+ internal override void AppendText(StringBuilder stringBuilder)
+ {
+ var text = Text ?? "";
+
+ stringBuilder.Append(text);
+ }
+
+ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+ {
+ base.OnPropertyChanged(change);
+
+ switch (change.Property.Name)
+ {
+ case nameof(Text):
+ InlineHost?.Invalidate();
+ break;
+ }
+ }
+ }
+}
diff --git a/src/Avalonia.Controls/Documents/Span.cs b/src/Avalonia.Controls/Documents/Span.cs
new file mode 100644
index 0000000000..bd1b4fc5e1
--- /dev/null
+++ b/src/Avalonia.Controls/Documents/Span.cs
@@ -0,0 +1,75 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Avalonia.Media.TextFormatting;
+using Avalonia.Metadata;
+
+namespace Avalonia.Controls.Documents
+{
+ ///
+ /// Span element used for grouping other Inline elements.
+ ///
+ public class Span : Inline
+ {
+ ///
+ /// Defines the property.
+ ///
+ public static readonly DirectProperty InlinesProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(Inlines),
+ o => o.Inlines);
+
+ ///
+ /// Initializes a new instance of a Span element.
+ ///
+ public Span()
+ {
+ Inlines = new InlineCollection(this);
+ Inlines.Invalidated += (s, e) => InlineHost?.Invalidate();
+ }
+
+ ///
+ /// Gets or sets the inlines.
+ ///
+ [Content]
+ public InlineCollection Inlines { get; }
+
+ internal override void BuildTextRun(IList textRuns)
+ {
+ if (Inlines.HasComplexContent)
+ {
+ foreach (var inline in Inlines)
+ {
+ inline.BuildTextRun(textRuns);
+ }
+ }
+ else
+ {
+ if (Inlines.Text is string text)
+ {
+ var textRunProperties = CreateTextRunProperties();
+
+ var textCharacters = new TextCharacters(text.AsMemory(), textRunProperties);
+
+ textRuns.Add(textCharacters);
+ }
+ }
+ }
+
+ internal override void AppendText(StringBuilder stringBuilder)
+ {
+ if (Inlines.HasComplexContent)
+ {
+ foreach (var inline in Inlines)
+ {
+ inline.AppendText(stringBuilder);
+ }
+ }
+
+ if (Inlines.Text is string text)
+ {
+ stringBuilder.Append(text);
+ }
+ }
+ }
+}
diff --git a/src/Avalonia.Controls/Documents/TextElement.cs b/src/Avalonia.Controls/Documents/TextElement.cs
new file mode 100644
index 0000000000..f228519e60
--- /dev/null
+++ b/src/Avalonia.Controls/Documents/TextElement.cs
@@ -0,0 +1,273 @@
+using Avalonia.Media;
+
+namespace Avalonia.Controls.Documents
+{
+ ///
+ /// TextElement is an base class for content in text based controls.
+ /// TextElements span other content, applying property values or providing structural information.
+ ///
+ public abstract class TextElement : StyledElement
+ {
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty BackgroundProperty =
+ Border.BackgroundProperty.AddOwner();
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly AttachedProperty FontFamilyProperty =
+ AvaloniaProperty.RegisterAttached(
+ nameof(FontFamily),
+ defaultValue: FontFamily.Default,
+ inherits: true);
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly AttachedProperty FontSizeProperty =
+ AvaloniaProperty.RegisterAttached(
+ nameof(FontSize),
+ defaultValue: 12,
+ inherits: true);
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly AttachedProperty FontStyleProperty =
+ AvaloniaProperty.RegisterAttached(
+ nameof(FontStyle),
+ inherits: true);
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly AttachedProperty FontWeightProperty =
+ 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 =
+ AvaloniaProperty.RegisterAttached(
+ nameof(Foreground),
+ Brushes.Black,
+ inherits: true);
+
+ ///
+ /// Gets or sets a brush used to paint the control's background.
+ ///
+ public IBrush? Background
+ {
+ get { return GetValue(BackgroundProperty); }
+ set { SetValue(BackgroundProperty, value); }
+ }
+
+ ///
+ /// Gets or sets the font family.
+ ///
+ public FontFamily FontFamily
+ {
+ get { return GetValue(FontFamilyProperty); }
+ set { SetValue(FontFamilyProperty, value); }
+ }
+
+ ///
+ /// Gets or sets the font size.
+ ///
+ public double FontSize
+ {
+ get { return GetValue(FontSizeProperty); }
+ set { SetValue(FontSizeProperty, value); }
+ }
+
+ ///
+ /// Gets or sets the font style.
+ ///
+ public FontStyle FontStyle
+ {
+ get { return GetValue(FontStyleProperty); }
+ set { SetValue(FontStyleProperty, value); }
+ }
+
+ ///
+ /// Gets or sets the font weight.
+ ///
+ public FontWeight FontWeight
+ {
+ get { return GetValue(FontWeightProperty); }
+ 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.
+ ///
+ public IBrush? Foreground
+ {
+ 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);
+ }
+
+ internal IInlineHost? InlineHost { get; set; }
+
+ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+ {
+ base.OnPropertyChanged(change);
+
+ switch (change.Property.Name)
+ {
+ case nameof(Background):
+ case nameof(FontFamily):
+ case nameof(FontSize):
+ case nameof(FontStyle):
+ case nameof(FontWeight):
+ case nameof(FontStretch):
+ case nameof(Foreground):
+ InlineHost?.Invalidate();
+ break;
+ }
+ }
+ }
+}
diff --git a/src/Avalonia.Controls/Documents/Underline.cs b/src/Avalonia.Controls/Documents/Underline.cs
new file mode 100644
index 0000000000..fcd46c8439
--- /dev/null
+++ b/src/Avalonia.Controls/Documents/Underline.cs
@@ -0,0 +1,15 @@
+namespace Avalonia.Controls.Documents
+{
+ ///
+ /// Underline element - markup helper for indicating superscript content.
+ /// Equivalent to a Span with TextDecorations property set to TextDecorations.Underlined.
+ /// Can contain other inline elements.
+ ///
+ public sealed class Underline : Span
+ {
+ static Underline()
+ {
+ TextDecorationsProperty.OverrideDefaultValue(Media.TextDecorations.Underline);
+ }
+ }
+}
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/Embedding/Offscreen/OffscreenTopLevelImpl.cs b/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs
index 08d559a5c1..1c7bdb9b37 100644
--- a/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs
+++ b/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs
@@ -2,11 +2,13 @@
using System.Collections.Generic;
using Avalonia.Input;
using Avalonia.Input.Raw;
+using Avalonia.Metadata;
using Avalonia.Platform;
using Avalonia.Rendering;
namespace Avalonia.Controls.Embedding.Offscreen
{
+ [Unstable]
public abstract class OffscreenTopLevelImplBase : ITopLevelImpl
{
private double _scaling = 1;
diff --git a/src/Avalonia.Controls/Expander.cs b/src/Avalonia.Controls/Expander.cs
index 020b162864..3ba99d8a67 100644
--- a/src/Avalonia.Controls/Expander.cs
+++ b/src/Avalonia.Controls/Expander.cs
@@ -106,13 +106,13 @@ namespace Avalonia.Controls
}
}
- protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
if (change.Property == ExpandDirectionProperty)
{
- UpdatePseudoClasses(change.NewValue.GetValueOrDefault());
+ UpdatePseudoClasses(change.GetNewValue());
}
}
diff --git a/src/Avalonia.Controls/Flyouts/MenuFlyoutPresenter.cs b/src/Avalonia.Controls/Flyouts/MenuFlyoutPresenter.cs
index bcd859100a..6a7da87387 100644
--- a/src/Avalonia.Controls/Flyouts/MenuFlyoutPresenter.cs
+++ b/src/Avalonia.Controls/Flyouts/MenuFlyoutPresenter.cs
@@ -8,15 +8,6 @@ namespace Avalonia.Controls
{
public class MenuFlyoutPresenter : MenuBase
{
- public static readonly StyledProperty CornerRadiusProperty =
- Border.CornerRadiusProperty.AddOwner();
-
- public CornerRadius CornerRadius
- {
- get => GetValue(CornerRadiusProperty);
- set => SetValue(CornerRadiusProperty, value);
- }
-
public MenuFlyoutPresenter()
:base(new DefaultMenuInteractionHandler(true))
{
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/Generators/TreeContainerIndex.cs b/src/Avalonia.Controls/Generators/TreeContainerIndex.cs
index da13416700..eb60fca367 100644
--- a/src/Avalonia.Controls/Generators/TreeContainerIndex.cs
+++ b/src/Avalonia.Controls/Generators/TreeContainerIndex.cs
@@ -15,6 +15,7 @@ namespace Avalonia.Controls.Generators
///
public class TreeContainerIndex
{
+ private readonly Dictionary> _itemToContainerSet = new Dictionary>();
private readonly Dictionary _itemToContainer = new Dictionary();
private readonly Dictionary _containerToItem = new Dictionary();
@@ -45,14 +46,45 @@ namespace Avalonia.Controls.Generators
/// The item container.
public void Add(object item, IControl container)
{
- _itemToContainer.Add(item, container);
+ _itemToContainer[item] = container;
+ if (_itemToContainerSet.TryGetValue(item, out var set))
+ {
+ set.Add(container);
+ }
+ else
+ {
+ _itemToContainerSet.Add(item, new HashSet { container });
+ }
+
_containerToItem.Add(container, item);
Materialized?.Invoke(
- this,
+ this,
new ItemContainerEventArgs(new ItemContainerInfo(container, item, 0)));
}
+ ///
+ /// Removes a container from private collections.
+ ///
+ /// The item container.
+ /// The DataContext object
+ private void RemoveContainer(IControl container, object item)
+ {
+ if (_itemToContainerSet.TryGetValue(item, out var set))
+ {
+ set.Remove(container);
+ if (set.Count == 0)
+ {
+ _itemToContainerSet.Remove(item);
+ _itemToContainer.Remove(item);
+ }
+ else
+ {
+ _itemToContainer[item] = set.First();
+ }
+ }
+ }
+
///
/// Removes a container from the index.
///
@@ -61,10 +93,10 @@ namespace Avalonia.Controls.Generators
{
var item = _containerToItem[container];
_containerToItem.Remove(container);
- _itemToContainer.Remove(item);
+ RemoveContainer(container, item);
Dematerialized?.Invoke(
- this,
+ this,
new ItemContainerEventArgs(new ItemContainerInfo(container, item, 0)));
}
@@ -79,7 +111,7 @@ namespace Avalonia.Controls.Generators
{
var item = _containerToItem[container.ContainerControl];
_containerToItem.Remove(container.ContainerControl);
- _itemToContainer.Remove(item);
+ RemoveContainer(container.ContainerControl, item);
}
Dematerialized?.Invoke(
@@ -97,6 +129,14 @@ namespace Avalonia.Controls.Generators
if (item != null)
{
_itemToContainer.TryGetValue(item, out var result);
+ if (result == null)
+ {
+ _itemToContainerSet.TryGetValue(item, out var set);
+ if (set?.Count > 0)
+ {
+ return set.FirstOrDefault();
+ }
+ }
return result;
}
@@ -113,6 +153,10 @@ namespace Avalonia.Controls.Generators
if (container != null)
{
_containerToItem.TryGetValue(container, out var result);
+ if (result != null)
+ {
+ _itemToContainer[result] = container;
+ }
return 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/IContentControl.cs b/src/Avalonia.Controls/IContentControl.cs
index d28b0afb25..b4d8d0f574 100644
--- a/src/Avalonia.Controls/IContentControl.cs
+++ b/src/Avalonia.Controls/IContentControl.cs
@@ -1,5 +1,6 @@
using Avalonia.Controls.Templates;
using Avalonia.Layout;
+using Avalonia.Metadata;
namespace Avalonia.Controls
{
@@ -7,6 +8,7 @@ namespace Avalonia.Controls
/// Defines a control that displays according to a
/// .
///
+ [NotClientImplementable]
public interface IContentControl : IControl
{
///
diff --git a/src/Avalonia.Controls/IControl.cs b/src/Avalonia.Controls/IControl.cs
index b501bc15a7..3395fc1059 100644
--- a/src/Avalonia.Controls/IControl.cs
+++ b/src/Avalonia.Controls/IControl.cs
@@ -1,6 +1,7 @@
using Avalonia.Controls.Templates;
using Avalonia.Input;
using Avalonia.Layout;
+using Avalonia.Metadata;
using Avalonia.VisualTree;
namespace Avalonia.Controls
@@ -8,6 +9,7 @@ namespace Avalonia.Controls
///
/// Interface for Avalonia controls.
///
+ [NotClientImplementable]
public interface IControl : IVisual,
IDataTemplateHost,
ILayoutable,
diff --git a/src/Avalonia.Controls/IGlobalDataTemplates.cs b/src/Avalonia.Controls/IGlobalDataTemplates.cs
index 92dcd2c189..f4499ddb5e 100644
--- a/src/Avalonia.Controls/IGlobalDataTemplates.cs
+++ b/src/Avalonia.Controls/IGlobalDataTemplates.cs
@@ -1,10 +1,12 @@
using Avalonia.Controls.Templates;
+using Avalonia.Metadata;
namespace Avalonia.Controls
{
///
/// Defines the application-global data templates.
///
+ [NotClientImplementable]
public interface IGlobalDataTemplates : IDataTemplateHost
{
}
diff --git a/src/Avalonia.Controls/IMenu.cs b/src/Avalonia.Controls/IMenu.cs
index 0722a22f08..d90c5ea7a8 100644
--- a/src/Avalonia.Controls/IMenu.cs
+++ b/src/Avalonia.Controls/IMenu.cs
@@ -1,10 +1,12 @@
using Avalonia.Controls.Platform;
+using Avalonia.Metadata;
namespace Avalonia.Controls
{
///
/// Represents a or .
///
+ [NotClientImplementable]
public interface IMenu : IMenuElement
{
///
diff --git a/src/Avalonia.Controls/IMenuElement.cs b/src/Avalonia.Controls/IMenuElement.cs
index a3200d2b1b..c13c20b639 100644
--- a/src/Avalonia.Controls/IMenuElement.cs
+++ b/src/Avalonia.Controls/IMenuElement.cs
@@ -1,11 +1,13 @@
using System.Collections.Generic;
using Avalonia.Input;
+using Avalonia.Metadata;
namespace Avalonia.Controls
{
///
/// Represents an or .
///
+ [NotClientImplementable]
public interface IMenuElement : IControl
{
///
diff --git a/src/Avalonia.Controls/IMenuItem.cs b/src/Avalonia.Controls/IMenuItem.cs
index 35e36eb0f4..9d7ef3c18d 100644
--- a/src/Avalonia.Controls/IMenuItem.cs
+++ b/src/Avalonia.Controls/IMenuItem.cs
@@ -1,8 +1,11 @@
-namespace Avalonia.Controls
+using Avalonia.Metadata;
+
+namespace Avalonia.Controls
{
///
/// Represents a .
///
+ [NotClientImplementable]
public interface IMenuItem : IMenuElement
{
///
diff --git a/src/Avalonia.Controls/INativeMenuExporterEventsImplBridge.cs b/src/Avalonia.Controls/INativeMenuExporterEventsImplBridge.cs
index f492e6ca0f..29963e4821 100644
--- a/src/Avalonia.Controls/INativeMenuExporterEventsImplBridge.cs
+++ b/src/Avalonia.Controls/INativeMenuExporterEventsImplBridge.cs
@@ -1,5 +1,8 @@
+using Avalonia.Metadata;
+
namespace Avalonia.Controls
{
+ [Unstable]
public interface INativeMenuExporterEventsImplBridge
{
void RaiseNeedsUpdate ();
diff --git a/src/Avalonia.Controls/INativeMenuItemExporterEventsImplBridge.cs b/src/Avalonia.Controls/INativeMenuItemExporterEventsImplBridge.cs
index 6cb68d8ddd..a6c7489971 100644
--- a/src/Avalonia.Controls/INativeMenuItemExporterEventsImplBridge.cs
+++ b/src/Avalonia.Controls/INativeMenuItemExporterEventsImplBridge.cs
@@ -1,5 +1,8 @@
+using Avalonia.Metadata;
+
namespace Avalonia.Controls
{
+ [Unstable]
public interface INativeMenuItemExporterEventsImplBridge
{
void RaiseClicked ();
diff --git a/src/Avalonia.Controls/IPanel.cs b/src/Avalonia.Controls/IPanel.cs
index 7b9e2c2074..8f2564ec74 100644
--- a/src/Avalonia.Controls/IPanel.cs
+++ b/src/Avalonia.Controls/IPanel.cs
@@ -1,8 +1,11 @@
+using Avalonia.Metadata;
+
namespace Avalonia.Controls
{
///
/// Interface for controls that can contain multiple children.
///
+ [NotClientImplementable]
public interface IPanel : IControl
{
///
@@ -10,4 +13,4 @@ namespace Avalonia.Controls
///
Controls Children { get; }
}
-}
\ No newline at end of file
+}
diff --git a/src/Avalonia.Controls/IScrollable.cs b/src/Avalonia.Controls/IScrollable.cs
index 2a98b3910a..680088290c 100644
--- a/src/Avalonia.Controls/IScrollable.cs
+++ b/src/Avalonia.Controls/IScrollable.cs
@@ -1,4 +1,3 @@
-
namespace Avalonia.Controls.Primitives
{
///
diff --git a/src/Avalonia.Controls/Image.cs b/src/Avalonia.Controls/Image.cs
index c448729643..7408bff902 100644
--- a/src/Avalonia.Controls/Image.cs
+++ b/src/Avalonia.Controls/Image.cs
@@ -1,3 +1,5 @@
+using Avalonia.Automation;
+using Avalonia.Automation.Peers;
using Avalonia.Media;
using Avalonia.Media.Imaging;
using Avalonia.Metadata;
@@ -33,6 +35,7 @@ namespace Avalonia.Controls
{
AffectsRender(SourceProperty, StretchProperty, StretchDirectionProperty);
AffectsMeasure(SourceProperty, StretchProperty, StretchDirectionProperty);
+ AutomationProperties.ControlTypeOverrideProperty.OverrideDefaultValue(AutomationControlType.Image);
}
///
@@ -63,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 ed8f9efb2e..56b0014c05 100644
--- a/src/Avalonia.Controls/ItemsControl.cs
+++ b/src/Avalonia.Controls/ItemsControl.cs
@@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using Avalonia.Collections;
+using Avalonia.Automation.Peers;
using Avalonia.Controls.Generators;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Presenters;
@@ -165,7 +166,7 @@ namespace Avalonia.Controls
if (Presenter is IChildIndexProvider innerProvider)
{
innerProvider.ChildIndexChanged += PresenterChildIndexChanged;
- _childIndexChanged?.Invoke(this, new ChildIndexChangedEventArgs());
+ _childIndexChanged?.Invoke(this, ChildIndexChangedEventArgs.Empty);
}
}
@@ -335,13 +336,18 @@ namespace Avalonia.Controls
base.OnKeyDown(e);
}
- protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+ protected override AutomationPeer OnCreateAutomationPeer()
+ {
+ return new ItemsControlAutomationPeer(this);
+ }
+
+ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
if (change.Property == ItemCountProperty)
{
- UpdatePseudoClasses(change.NewValue.GetValueOrDefault());
+ UpdatePseudoClasses(change.GetNewValue());
}
}
@@ -502,7 +508,6 @@ namespace Avalonia.Controls
do
{
result = container.GetControl(direction, c, wrap);
- from = from ?? result;
if (result != null &&
result.Focusable &&
@@ -513,7 +518,7 @@ namespace Avalonia.Controls
}
c = result;
- } while (c != null && c != from);
+ } while (c != null && c != from && direction != NavigationDirection.First && direction != NavigationDirection.Last);
return null;
}
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/ListBoxItem.cs b/src/Avalonia.Controls/ListBoxItem.cs
index 4fe5f4de40..66a46cab4a 100644
--- a/src/Avalonia.Controls/ListBoxItem.cs
+++ b/src/Avalonia.Controls/ListBoxItem.cs
@@ -1,6 +1,6 @@
+using Avalonia.Automation.Peers;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Mixins;
-using Avalonia.Input;
namespace Avalonia.Controls
{
@@ -34,5 +34,10 @@ namespace Avalonia.Controls
get { return GetValue(IsSelectedProperty); }
set { SetValue(IsSelectedProperty, value); }
}
+
+ protected override AutomationPeer OnCreateAutomationPeer()
+ {
+ return new ListItemAutomationPeer(this);
+ }
}
}
diff --git a/src/Avalonia.Controls/MaskedTextBox.cs b/src/Avalonia.Controls/MaskedTextBox.cs
index ad64c61ebe..080326606e 100644
--- a/src/Avalonia.Controls/MaskedTextBox.cs
+++ b/src/Avalonia.Controls/MaskedTextBox.cs
@@ -32,7 +32,7 @@ namespace Avalonia.Controls
AvaloniaProperty.Register(nameof(Mask), string.Empty);
public static new readonly StyledProperty PasswordCharProperty =
- AvaloniaProperty.Register(nameof(PasswordChar), '\0');
+ AvaloniaProperty.Register(nameof(PasswordChar), '\0');
public static readonly StyledProperty PromptCharProperty =
AvaloniaProperty.Register(nameof(PromptChar), '_');
@@ -280,7 +280,7 @@ namespace Avalonia.Controls
base.OnLostFocus(e);
}
- protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
void UpdateMaskProvider()
{
diff --git a/src/Avalonia.Controls/Menu.cs b/src/Avalonia.Controls/Menu.cs
index cc89677f82..611811f170 100644
--- a/src/Avalonia.Controls/Menu.cs
+++ b/src/Avalonia.Controls/Menu.cs
@@ -1,3 +1,5 @@
+using Avalonia.Automation;
+using Avalonia.Automation.Peers;
using Avalonia.Controls.Platform;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
@@ -35,6 +37,8 @@ namespace Avalonia.Controls
static Menu()
{
ItemsPanelProperty.OverrideDefaultValue(typeof(Menu), DefaultPanel);
+ AutomationProperties.AccessibilityViewProperty.OverrideDefaultValue