diff --git a/.editorconfig b/.editorconfig
index a144ec8843..62a533e468 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -177,7 +177,9 @@ dotnet_diagnostic.CA1828.severity = warning
dotnet_diagnostic.CA1829.severity = warning
#CA1847: Use string.Contains(char) instead of string.Contains(string) with single characters
dotnet_diagnostic.CA1847.severity = warning
-#CACA2211:Non-constant fields should not be visible
+#CA1854: Prefer the IDictionary.TryGetValue(TKey, out TValue) method
+dotnet_diagnostic.CA1854.severity = warning
+#CA2211:Non-constant fields should not be visible
dotnet_diagnostic.CA2211.severity = error
# Wrapping preferences
@@ -186,6 +188,23 @@ csharp_wrap_before_ternary_opsigns = false
# Avalonia DevAnalyzer preferences
dotnet_diagnostic.AVADEV2001.severity = error
+# Avalonia PublicAnalyzer preferences
+dotnet_diagnostic.AVP1000.severity = error
+dotnet_diagnostic.AVP1001.severity = error
+dotnet_diagnostic.AVP1002.severity = error
+dotnet_diagnostic.AVP1010.severity = error
+dotnet_diagnostic.AVP1011.severity = error
+dotnet_diagnostic.AVP1012.severity = warning
+dotnet_diagnostic.AVP1013.severity = error
+dotnet_diagnostic.AVP1020.severity = error
+dotnet_diagnostic.AVP1021.severity = error
+dotnet_diagnostic.AVP1022.severity = error
+dotnet_diagnostic.AVP1030.severity = error
+dotnet_diagnostic.AVP1031.severity = error
+dotnet_diagnostic.AVP1032.severity = error
+dotnet_diagnostic.AVP1040.severity = error
+dotnet_diagnostic.AVA2001.severity = error
+
# Xaml files
[*.{xaml,axaml}]
indent_size = 2
diff --git a/.ncrunch/SafeAreaDemo.Android.v3.ncrunchproject b/.ncrunch/SafeAreaDemo.Android.v3.ncrunchproject
new file mode 100644
index 0000000000..319cd523ce
--- /dev/null
+++ b/.ncrunch/SafeAreaDemo.Android.v3.ncrunchproject
@@ -0,0 +1,5 @@
+
+
+ True
+
+
\ No newline at end of file
diff --git a/.ncrunch/SafeAreaDemo.Desktop.v3.ncrunchproject b/.ncrunch/SafeAreaDemo.Desktop.v3.ncrunchproject
new file mode 100644
index 0000000000..319cd523ce
--- /dev/null
+++ b/.ncrunch/SafeAreaDemo.Desktop.v3.ncrunchproject
@@ -0,0 +1,5 @@
+
+
+ True
+
+
\ No newline at end of file
diff --git a/.ncrunch/SafeAreaDemo.iOS.v3.ncrunchproject b/.ncrunch/SafeAreaDemo.iOS.v3.ncrunchproject
new file mode 100644
index 0000000000..319cd523ce
--- /dev/null
+++ b/.ncrunch/SafeAreaDemo.iOS.v3.ncrunchproject
@@ -0,0 +1,5 @@
+
+
+ True
+
+
\ No newline at end of file
diff --git a/.ncrunch/SafeAreaDemo.v3.ncrunchproject b/.ncrunch/SafeAreaDemo.v3.ncrunchproject
new file mode 100644
index 0000000000..319cd523ce
--- /dev/null
+++ b/.ncrunch/SafeAreaDemo.v3.ncrunchproject
@@ -0,0 +1,5 @@
+
+
+ True
+
+
\ No newline at end of file
diff --git a/.nuke/build.schema.json b/.nuke/build.schema.json
index 875161d336..d2f2ee36d5 100644
--- a/.nuke/build.schema.json
+++ b/.nuke/build.schema.json
@@ -101,10 +101,6 @@
"type": "boolean",
"description": "skip-tests"
},
- "Solution": {
- "type": "string",
- "description": "Path to a solution file that is automatically loaded. Default is Avalonia.sln"
- },
"Target": {
"type": "array",
"description": "List of targets to be invoked. Default is '{default_target}'",
diff --git a/Avalonia.Desktop.slnf b/Avalonia.Desktop.slnf
index 76620e8b93..92c924f107 100644
--- a/Avalonia.Desktop.slnf
+++ b/Avalonia.Desktop.slnf
@@ -3,6 +3,7 @@
"path": "Avalonia.sln",
"projects": [
"packages\\Avalonia\\Avalonia.csproj",
+ "samples\\AppWithoutLifetime\\AppWithoutLifetime.csproj",
"samples\\ControlCatalog.NetCore\\ControlCatalog.NetCore.csproj",
"samples\\ControlCatalog\\ControlCatalog.csproj",
"samples\\GpuInterop\\GpuInterop.csproj",
@@ -23,8 +24,6 @@
"src\\Avalonia.Dialogs\\Avalonia.Dialogs.csproj",
"src\\Avalonia.Fonts.Inter\\Avalonia.Fonts.Inter.csproj",
"src\\Avalonia.FreeDesktop\\Avalonia.FreeDesktop.csproj",
- "src\\Avalonia.Headless.Vnc\\Avalonia.Headless.Vnc.csproj",
- "src\\Avalonia.Headless\\Avalonia.Headless.csproj",
"src\\Avalonia.MicroCom\\Avalonia.MicroCom.csproj",
"src\\Avalonia.Native\\Avalonia.Native.csproj",
"src\\Avalonia.OpenGL\\Avalonia.OpenGL.csproj",
@@ -33,6 +32,8 @@
"src\\Avalonia.Themes.Fluent\\Avalonia.Themes.Fluent.csproj",
"src\\Avalonia.Themes.Simple\\Avalonia.Themes.Simple.csproj",
"src\\Avalonia.X11\\Avalonia.X11.csproj",
+ "src\\Headless\\Avalonia.Headless.Vnc\\Avalonia.Headless.Vnc.csproj",
+ "src\\Headless\\Avalonia.Headless\\Avalonia.Headless.csproj",
"src\\Linux\\Avalonia.LinuxFramebuffer\\Avalonia.LinuxFramebuffer.csproj",
"src\\Markup\\Avalonia.Markup.Xaml.Loader\\Avalonia.Markup.Xaml.Loader.csproj",
"src\\Markup\\Avalonia.Markup.Xaml\\Avalonia.Markup.Xaml.csproj",
@@ -65,4 +66,4 @@
"tests\\Avalonia.UnitTests\\Avalonia.UnitTests.csproj"
]
}
-}
\ No newline at end of file
+}
diff --git a/Avalonia.sln b/Avalonia.sln
index b21df07628..9670327d67 100644
--- a/Avalonia.sln
+++ b/Avalonia.sln
@@ -181,9 +181,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls.DataGrid.
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Themes.Fluent", "src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj", "{C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Headless", "src\Avalonia.Headless\Avalonia.Headless.csproj", "{8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Headless", "src\Headless\Avalonia.Headless\Avalonia.Headless.csproj", "{8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Headless.Vnc", "src\Avalonia.Headless.Vnc\Avalonia.Headless.Vnc.csproj", "{B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Headless.Vnc", "src\Headless\Avalonia.Headless.Vnc\Avalonia.Headless.Vnc.csproj", "{B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Markup.Xaml.Loader", "src\Markup\Avalonia.Markup.Xaml.Loader\Avalonia.Markup.Xaml.Loader.csproj", "{909A8CBD-7D0E-42FD-B841-022AD8925820}"
EndProject
@@ -233,7 +233,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReactiveUIDemo", "samples\R
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GpuInterop", "samples\GpuInterop\GpuInterop.csproj", "{C810060E-3809-4B74-A125-F11533AF9C1B}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Analyzers", "src\tools\PublicAnalyzers\Avalonia.Analyzers.csproj", "{C692FE73-43DB-49CE-87FC-F03ED61F25C9}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Analyzers", "src\tools\Avalonia.Analyzers\Avalonia.Analyzers.csproj", "{C692FE73-43DB-49CE-87FC-F03ED61F25C9}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{176582E8-46AF-416A-85C1-13A5C6744497}"
ProjectSection(SolutionItems) = preProject
@@ -244,13 +244,32 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls.ItemsRepe
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls.ItemsRepeater.UnitTests", "tests\Avalonia.Controls.ItemsRepeater.UnitTests\Avalonia.Controls.ItemsRepeater.UnitTests.csproj", "{F4E36AA8-814E-4704-BC07-291F70F45193}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Generators", "src\tools\Avalonia.Generators\Avalonia.Generators.csproj", "{DDA28789-C21A-4654-86CE-D01E81F095C5}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Generators", "src\tools\Avalonia.Generators\Avalonia.Generators.csproj", "{DDA28789-C21A-4654-86CE-D01E81F095C5}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Generators.Tests", "tests\Avalonia.Generators.Tests\Avalonia.Generators.Tests.csproj", "{2D7C812B-7E73-4252-8EFD-BC8A4D5CCB9F}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Fonts.Inter", "src\Avalonia.Fonts.Inter\Avalonia.Fonts.Inter.csproj", "{13F1135D-BA1A-435C-9C5B-A368D1D63DE4}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Fonts.Inter", "src\Avalonia.Fonts.Inter\Avalonia.Fonts.Inter.csproj", "{13F1135D-BA1A-435C-9C5B-A368D1D63DE4}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Generators.Sandbox", "samples\Generators.Sandbox\Generators.Sandbox.csproj", "{A82AD1BC-EBE6-4FC3-A13B-D52A50297533}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Generators.Sandbox", "samples\Generators.Sandbox\Generators.Sandbox.csproj", "{A82AD1BC-EBE6-4FC3-A13B-D52A50297533}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AppWithoutLifetime", "samples\AppWithoutLifetime\AppWithoutLifetime.csproj", "{F8928267-688E-4A51-989C-612A72446D33}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SafeAreaDemo", "samples\SafeAreaDemo\SafeAreaDemo.csproj", "{6B60A970-D5D2-49C2-8BAB-F9C7973B74B6}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SafeAreaDemo.Android", "samples\SafeAreaDemo.Android\SafeAreaDemo.Android.csproj", "{22E3BC08-EAF7-4889-BDC4-B4D3046C4E2D}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SafeAreaDemo.Desktop", "samples\SafeAreaDemo.Desktop\SafeAreaDemo.Desktop.csproj", "{4CDAD037-34A2-4CCF-A03A-C6C7B988A572}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SafeAreaDemo.iOS", "samples\SafeAreaDemo.iOS\SafeAreaDemo.iOS.csproj", "{FC956F9A-4C3A-4A1A-ACDD-BB54DCB661DD}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Headless", "Headless", "{FF237916-7150-496B-89ED-6CA3292896E7}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Headless.XUnit", "src\Headless\Avalonia.Headless.XUnit\Avalonia.Headless.XUnit.csproj", "{F47F8316-4D4B-4026-8EF3-16B2CFDA8119}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Headless.NUnit", "src\Headless\Avalonia.Headless.NUnit\Avalonia.Headless.NUnit.csproj", "{ED976634-B118-43F8-8B26-0279C7A7044F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Generators.Tests", "tests\Avalonia.Generators.Tests\Avalonia.Generators.Tests.csproj", "{4B8EBBEB-A1AD-49EC-8B69-B93ED15BFA64}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Headless.NUnit.UnitTests", "tests\Avalonia.Headless.NUnit.UnitTests\Avalonia.Headless.NUnit.UnitTests.csproj", "{2999D79E-3C20-4A90-B651-CA7E0AC92D35}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Headless.XUnit.UnitTests", "tests\Avalonia.Headless.XUnit.UnitTests\Avalonia.Headless.XUnit.UnitTests.csproj", "{F83FC908-A4E3-40DE-B4CF-A4BA1E92CDB3}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -583,18 +602,62 @@ Global
{DDA28789-C21A-4654-86CE-D01E81F095C5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DDA28789-C21A-4654-86CE-D01E81F095C5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DDA28789-C21A-4654-86CE-D01E81F095C5}.Release|Any CPU.Build.0 = Release|Any CPU
- {2D7C812B-7E73-4252-8EFD-BC8A4D5CCB9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {2D7C812B-7E73-4252-8EFD-BC8A4D5CCB9F}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {2D7C812B-7E73-4252-8EFD-BC8A4D5CCB9F}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {2D7C812B-7E73-4252-8EFD-BC8A4D5CCB9F}.Release|Any CPU.Build.0 = Release|Any CPU
{13F1135D-BA1A-435C-9C5B-A368D1D63DE4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{13F1135D-BA1A-435C-9C5B-A368D1D63DE4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{13F1135D-BA1A-435C-9C5B-A368D1D63DE4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{13F1135D-BA1A-435C-9C5B-A368D1D63DE4}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F47F8316-4D4B-4026-8EF3-16B2CFDA8119}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F47F8316-4D4B-4026-8EF3-16B2CFDA8119}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F47F8316-4D4B-4026-8EF3-16B2CFDA8119}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F47F8316-4D4B-4026-8EF3-16B2CFDA8119}.Release|Any CPU.Build.0 = Release|Any CPU
{A82AD1BC-EBE6-4FC3-A13B-D52A50297533}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A82AD1BC-EBE6-4FC3-A13B-D52A50297533}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A82AD1BC-EBE6-4FC3-A13B-D52A50297533}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A82AD1BC-EBE6-4FC3-A13B-D52A50297533}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F8928267-688E-4A51-989C-612A72446D33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F8928267-688E-4A51-989C-612A72446D33}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F8928267-688E-4A51-989C-612A72446D33}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F8928267-688E-4A51-989C-612A72446D33}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6B60A970-D5D2-49C2-8BAB-F9C7973B74B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6B60A970-D5D2-49C2-8BAB-F9C7973B74B6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6B60A970-D5D2-49C2-8BAB-F9C7973B74B6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6B60A970-D5D2-49C2-8BAB-F9C7973B74B6}.Release|Any CPU.Build.0 = Release|Any CPU
+ {22E3BC08-EAF7-4889-BDC4-B4D3046C4E2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {22E3BC08-EAF7-4889-BDC4-B4D3046C4E2D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {22E3BC08-EAF7-4889-BDC4-B4D3046C4E2D}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {22E3BC08-EAF7-4889-BDC4-B4D3046C4E2D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {22E3BC08-EAF7-4889-BDC4-B4D3046C4E2D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {22E3BC08-EAF7-4889-BDC4-B4D3046C4E2D}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ {4CDAD037-34A2-4CCF-A03A-C6C7B988A572}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4CDAD037-34A2-4CCF-A03A-C6C7B988A572}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4CDAD037-34A2-4CCF-A03A-C6C7B988A572}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4CDAD037-34A2-4CCF-A03A-C6C7B988A572}.Release|Any CPU.Build.0 = Release|Any CPU
+ {FC956F9A-4C3A-4A1A-ACDD-BB54DCB661DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FC956F9A-4C3A-4A1A-ACDD-BB54DCB661DD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FC956F9A-4C3A-4A1A-ACDD-BB54DCB661DD}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {FC956F9A-4C3A-4A1A-ACDD-BB54DCB661DD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FC956F9A-4C3A-4A1A-ACDD-BB54DCB661DD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {FC956F9A-4C3A-4A1A-ACDD-BB54DCB661DD}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ {ED976634-B118-43F8-8B26-0279C7A7044F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {ED976634-B118-43F8-8B26-0279C7A7044F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {ED976634-B118-43F8-8B26-0279C7A7044F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {ED976634-B118-43F8-8B26-0279C7A7044F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F47F8316-4D4B-4026-8EF3-16B2CFDA8119}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F47F8316-4D4B-4026-8EF3-16B2CFDA8119}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F47F8316-4D4B-4026-8EF3-16B2CFDA8119}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F47F8316-4D4B-4026-8EF3-16B2CFDA8119}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4B8EBBEB-A1AD-49EC-8B69-B93ED15BFA64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4B8EBBEB-A1AD-49EC-8B69-B93ED15BFA64}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4B8EBBEB-A1AD-49EC-8B69-B93ED15BFA64}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4B8EBBEB-A1AD-49EC-8B69-B93ED15BFA64}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2999D79E-3C20-4A90-B651-CA7E0AC92D35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2999D79E-3C20-4A90-B651-CA7E0AC92D35}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2999D79E-3C20-4A90-B651-CA7E0AC92D35}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2999D79E-3C20-4A90-B651-CA7E0AC92D35}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F83FC908-A4E3-40DE-B4CF-A4BA1E92CDB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F83FC908-A4E3-40DE-B4CF-A4BA1E92CDB3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F83FC908-A4E3-40DE-B4CF-A4BA1E92CDB3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F83FC908-A4E3-40DE-B4CF-A4BA1E92CDB3}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -639,6 +702,8 @@ Global
{AF915D5C-AB00-4EA0-B5E6-001F4AE84E68} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{4D36CEC8-53F2-40A5-9A37-79AAE356E2DA} = {86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B}
{351337F5-D66F-461B-A957-4EF60BDB4BA6} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
+ {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC} = {FF237916-7150-496B-89ED-6CA3292896E7}
+ {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E} = {FF237916-7150-496B-89ED-6CA3292896E7}
{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}
@@ -661,10 +726,22 @@ Global
{75C47156-C5D8-44BC-A5A7-E8657C2248D6} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{C810060E-3809-4B74-A125-F11533AF9C1B} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{C692FE73-43DB-49CE-87FC-F03ED61F25C9} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637}
- {DDA28789-C21A-4654-86CE-D01E81F095C5} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637}
- {2D7C812B-7E73-4252-8EFD-BC8A4D5CCB9F} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{F4E36AA8-814E-4704-BC07-291F70F45193} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
+ {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC} = {FF237916-7150-496B-89ED-6CA3292896E7}
+ {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E} = {FF237916-7150-496B-89ED-6CA3292896E7}
+ {F47F8316-4D4B-4026-8EF3-16B2CFDA8119} = {FF237916-7150-496B-89ED-6CA3292896E7}
+ {DDA28789-C21A-4654-86CE-D01E81F095C5} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637}
{A82AD1BC-EBE6-4FC3-A13B-D52A50297533} = {9B9E3891-2366-4253-A952-D08BCEB71098}
+ {F8928267-688E-4A51-989C-612A72446D33} = {9B9E3891-2366-4253-A952-D08BCEB71098}
+ {6B60A970-D5D2-49C2-8BAB-F9C7973B74B6} = {9B9E3891-2366-4253-A952-D08BCEB71098}
+ {22E3BC08-EAF7-4889-BDC4-B4D3046C4E2D} = {9B9E3891-2366-4253-A952-D08BCEB71098}
+ {4CDAD037-34A2-4CCF-A03A-C6C7B988A572} = {9B9E3891-2366-4253-A952-D08BCEB71098}
+ {FC956F9A-4C3A-4A1A-ACDD-BB54DCB661DD} = {9B9E3891-2366-4253-A952-D08BCEB71098}
+ {ED976634-B118-43F8-8B26-0279C7A7044F} = {FF237916-7150-496B-89ED-6CA3292896E7}
+ {F47F8316-4D4B-4026-8EF3-16B2CFDA8119} = {FF237916-7150-496B-89ED-6CA3292896E7}
+ {4B8EBBEB-A1AD-49EC-8B69-B93ED15BFA64} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
+ {2999D79E-3C20-4A90-B651-CA7E0AC92D35} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
+ {F83FC908-A4E3-40DE-B4CF-A4BA1E92CDB3} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A}
diff --git a/Directory.Build.targets b/Directory.Build.targets
index 73954c7f4d..e8d4baba11 100644
--- a/Directory.Build.targets
+++ b/Directory.Build.targets
@@ -1,5 +1,5 @@
-
+
$(DefineConstants);NET7SDK
diff --git a/build/DevAnalyzers.props b/build/DevAnalyzers.props
index 7d021d051f..dffd3098c3 100644
--- a/build/DevAnalyzers.props
+++ b/build/DevAnalyzers.props
@@ -5,7 +5,7 @@
ReferenceOutputAssembly="false"
OutputItemType="Analyzer"
SetTargetFramework="TargetFramework=netstandard2.0"/>
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dirs.proj b/dirs.proj
index f1eaae8a4a..d29aa61fcb 100644
--- a/dirs.proj
+++ b/dirs.proj
@@ -9,10 +9,11 @@
-
+
-
+
+
diff --git a/native/Avalonia.Native/src/OSX/AvnView.mm b/native/Avalonia.Native/src/OSX/AvnView.mm
index fdc144e3a5..86bacfb819 100644
--- a/native/Avalonia.Native/src/OSX/AvnView.mm
+++ b/native/Avalonia.Native/src/OSX/AvnView.mm
@@ -12,7 +12,6 @@
{
ComPtr _parent;
NSTrackingArea* _area;
- NSMutableAttributedString* _markedText;
bool _isLeftPressed, _isMiddlePressed, _isRightPressed, _isXButton1Pressed, _isXButton2Pressed;
AvnInputModifiers _modifierState;
NSEvent* _lastMouseDownEvent;
@@ -22,8 +21,9 @@
AvnPlatformResizeReason _resizeReason;
AvnAccessibilityElement* _accessibilityChild;
NSRect _cursorRect;
- NSMutableString* _text;
- NSRange _selection;
+ NSMutableAttributedString* _text;
+ NSRange _selectedRange;
+ NSRange _markedRange;
}
- (void)onClosed
@@ -59,6 +59,11 @@
[self registerForDraggedTypes: @[@"public.data", GetAvnCustomDataType()]];
_modifierState = AvnInputModifiersNone;
+
+ _text = [[NSMutableAttributedString alloc] initWithString:@""];
+ _markedRange = NSMakeRange(0, 0);
+ _selectedRange = NSMakeRange(0, 0);
+
return self;
}
@@ -270,7 +275,7 @@
delta.Y = [event deltaY];
}
- uint32 timestamp = static_cast([event timestamp] * 1000);
+ uint64_t timestamp = static_cast([event timestamp] * 1000);
auto modifiers = [self getModifiers:[event modifierFlags]];
if(type != Move ||
@@ -439,7 +444,7 @@
auto key = s_KeyMap[[event keyCode]];
- uint32_t timestamp = static_cast([event timestamp] * 1000);
+ uint64_t timestamp = static_cast([event timestamp] * 1000);
auto modifiers = [self getModifiers:[event modifierFlags]];
if(_parent != nullptr)
@@ -521,9 +526,13 @@
- (void)keyDown:(NSEvent *)event
{
- [self keyboardEvent:event withType:KeyDown];
- _lastKeyHandled = [[self inputContext] handleEvent:event];
- [super keyDown:event];
+ _lastKeyHandled = false;
+
+ [[self inputContext] handleEvent:event];
+
+ if(!_lastKeyHandled){
+ [self keyboardEvent:event withType:KeyDown];
+ }
}
- (void)keyUp:(NSEvent *)event
@@ -532,6 +541,10 @@
[super keyUp:event];
}
+- (void) doCommandBySelector:(SEL)selector{
+
+}
+
- (AvnInputModifiers)getModifiers:(NSEventModifierFlags)mod
{
unsigned int rv = 0;
@@ -561,50 +574,52 @@
- (BOOL)hasMarkedText
{
- return [_markedText length] > 0;
+ return _markedRange.length > 0;
}
- (NSRange)markedRange
{
- if([_markedText length] > 0)
- return NSMakeRange(0, [_markedText length] - 1);
- return NSMakeRange(NSNotFound, 0);
+ return _markedRange;
}
- (NSRange)selectedRange
{
- return _selection;
+ return _selectedRange;
}
- (void)setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
{
+ _lastKeyHandled = true;
+
+ NSString* markedText;
+
if([string isKindOfClass:[NSAttributedString class]])
{
- _markedText = [[NSMutableAttributedString alloc] initWithAttributedString:string];
+ markedText = [string string];
}
else
{
- _markedText = [[NSMutableAttributedString alloc] initWithString:string];
+ markedText = (NSString*) string;
}
- if(!_parent->InputMethod->IsActive()){
- return;
+ _markedRange = NSMakeRange(_selectedRange.location, [markedText length]);
+
+ if(_parent->InputMethod->IsActive()){
+ _parent->InputMethod->Client->SetPreeditText((char*)[markedText UTF8String]);
}
-
- _parent->InputMethod->Client->SetPreeditText((char*)[_markedText.string UTF8String]);
}
- (void)unmarkText
{
- [[_markedText mutableString] setString:@""];
+ if(_parent->InputMethod->IsActive()){
+ _parent->InputMethod->Client->SetPreeditText(nullptr);
+ }
- [[self inputContext] discardMarkedText];
+ _markedRange = NSMakeRange(_selectedRange.location, 0);
- if(!_parent->InputMethod->IsActive()){
- return;
+ if([self inputContext]) {
+ [[self inputContext] discardMarkedText];
}
-
- _parent->InputMethod->Client->SetPreeditText(nullptr);
}
- (NSArray *)validAttributesForMarkedText
@@ -614,19 +629,38 @@
- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)range actualRange:(NSRangePointer)actualRange
{
- return nullptr;
+ if(actualRange){
+ range = *actualRange;
+ }
+
+ NSAttributedString* subString = [_text attributedSubstringFromRange:range];
+
+ return subString;
}
- (void)insertText:(id)string replacementRange:(NSRange)replacementRange
{
- [self unmarkText];
-
- if(_parent != nullptr)
+ if(_parent == nullptr){
+ return;
+ }
+
+ NSString* text;
+
+ if([string isKindOfClass:[NSAttributedString class]])
{
- _lastKeyHandled = _parent->BaseEvents->RawTextInputEvent(0, [string UTF8String]);
+ text = [string string];
+ }
+ else
+ {
+ text = (NSString*) string;
}
- [[self inputContext] invalidateCharacterCoordinates];
+ [self unmarkText];
+
+ uint64_t timestamp = static_cast([NSDate timeIntervalSinceReferenceDate] * 1000);
+
+ _lastKeyHandled = _parent->BaseEvents->RawTextInputEvent(timestamp, [text UTF8String]);
+
}
- (NSUInteger)characterIndexForPoint:(NSPoint)point
@@ -746,15 +780,11 @@
}
- (void) setText:(NSString *)text{
- [_text setString:text];
-
- [[self inputContext] discardMarkedText];
+ [[_text mutableString] setString:text];
}
- (void) setSelection:(int)start :(int)end{
- _selection = NSMakeRange(start, end - start);
-
- [[self inputContext] invalidateCharacterCoordinates];
+ _selectedRange = NSMakeRange(start, end - start);
}
- (void) setCursorRect:(AvnRect)rect{
@@ -766,7 +796,9 @@
_cursorRect = windowRectOnScreen;
- [[self inputContext] invalidateCharacterCoordinates];
+ if([self inputContext]) {
+ [[self inputContext] invalidateCharacterCoordinates];
+ }
}
@end
diff --git a/native/Avalonia.Native/src/OSX/AvnWindow.mm b/native/Avalonia.Native/src/OSX/AvnWindow.mm
index 16e1486acc..ef50cdab84 100644
--- a/native/Avalonia.Native/src/OSX/AvnWindow.mm
+++ b/native/Avalonia.Native/src/OSX/AvnWindow.mm
@@ -460,7 +460,7 @@
auto point = [self translateLocalPoint:avnPoint];
AvnVector delta = { 0, 0 };
- _parent->BaseEvents->RawMouseEvent(NonClientLeftButtonDown, static_cast([event timestamp] * 1000), AvnInputModifiersNone, point, delta);
+ _parent->BaseEvents->RawMouseEvent(NonClientLeftButtonDown, static_cast([event timestamp] * 1000), AvnInputModifiersNone, point, delta);
}
if(!_isTransitioningToFullScreen)
diff --git a/nukebuild/Build.cs b/nukebuild/Build.cs
index 40232947d9..87e1e86bf9 100644
--- a/nukebuild/Build.cs
+++ b/nukebuild/Build.cs
@@ -35,8 +35,6 @@ using MicroCom.CodeGenerator;
partial class Build : NukeBuild
{
- [Solution("Avalonia.sln")] readonly Solution Solution;
-
BuildParameters Parameters { get; set; }
protected override void OnBuildInitialized()
{
@@ -143,10 +141,12 @@ partial class Build : NukeBuild
void RunCoreTest(string projectName)
{
Information($"Running tests from {projectName}");
- var project = Solution.GetProject(projectName).NotNull("project != null");
+ var project = RootDirectory.GlobFiles(@$"**\{projectName}.csproj").FirstOrDefault()
+ ?? throw new InvalidOperationException($"Project {projectName} doesn't exist");
+
// Nuke and MSBuild tools have build-in helpers to get target frameworks from the project.
// Unfortunately, it gets broken with every second SDK update, so we had to do it manually.
- var fileXml = XDocument.Parse(File.ReadAllText(project.Path));
+ var fileXml = XDocument.Parse(File.ReadAllText(project));
var targetFrameworks = fileXml.Descendants("TargetFrameworks")
.FirstOrDefault()?.Value.Split(';').Select(f => f.Trim());
if (targetFrameworks is null)
@@ -212,6 +212,8 @@ partial class Build : NukeBuild
RunCoreTest("Avalonia.Markup.Xaml.UnitTests");
RunCoreTest("Avalonia.Skia.UnitTests");
RunCoreTest("Avalonia.ReactiveUI.UnitTests");
+ RunCoreTest("Avalonia.Headless.NUnit.UnitTests");
+ RunCoreTest("Avalonia.Headless.XUnit.UnitTests");
});
Target RunRenderTests => _ => _
@@ -273,6 +275,8 @@ partial class Build : NukeBuild
if(!Numerge.NugetPackageMerger.Merge(Parameters.NugetIntermediateRoot, Parameters.NugetRoot, config,
new NumergeNukeLogger()))
throw new Exception("Package merge failed");
+ RefAssemblyGenerator.GenerateRefAsmsInPackage(Parameters.NugetRoot / "Avalonia." +
+ Parameters.Version + ".nupkg");
});
Target RunTests => _ => _
@@ -308,7 +312,7 @@ partial class Build : NukeBuild
public static int Main() =>
RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
- ? Execute(x => x.Package)
+ ? Execute(x => x.RunToolsTests)
: Execute(x => x.RunTests);
}
diff --git a/nukebuild/Helpers.cs b/nukebuild/Helpers.cs
new file mode 100644
index 0000000000..d8d06559bf
--- /dev/null
+++ b/nukebuild/Helpers.cs
@@ -0,0 +1,24 @@
+using System;
+using System.IO;
+using Nuke.Common.Utilities;
+
+class Helpers
+{
+ public static IDisposable UseTempDir(out string dir)
+ {
+ var path = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
+ Directory.CreateDirectory(path);
+ dir = path;
+ return DelegateDisposable.CreateBracket(null, () =>
+ {
+ try
+ {
+ Directory.Delete(path, true);
+ }
+ catch
+ {
+ // ignore
+ }
+ });
+ }
+}
diff --git a/nukebuild/RefAssemblyGenerator.cs b/nukebuild/RefAssemblyGenerator.cs
new file mode 100644
index 0000000000..f0d5c81a37
--- /dev/null
+++ b/nukebuild/RefAssemblyGenerator.cs
@@ -0,0 +1,177 @@
+using System.Collections.Generic;
+using System.IO;
+using System.IO.Compression;
+using System.Linq;
+using ILRepacking;
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+
+public class RefAssemblyGenerator
+{
+ class Resolver : DefaultAssemblyResolver, IAssemblyResolver
+ {
+ private readonly string _dir;
+ Dictionary _cache = new();
+
+ public Resolver(string dir)
+ {
+ _dir = dir;
+ }
+
+ public override AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters)
+ {
+ if (_cache.TryGetValue(name.Name, out var asm))
+ return asm;
+ var path = Path.Combine(_dir, name.Name + ".dll");
+ if (File.Exists(path))
+ return _cache[name.Name] = AssemblyDefinition.ReadAssembly(path, parameters);
+ return base.Resolve(name, parameters);
+ }
+ }
+
+ public static void PatchRefAssembly(string file)
+ {
+ var reader = typeof(RefAssemblyGenerator).Assembly.GetManifestResourceStream("avalonia.snk");
+ var snk = new byte[reader.Length];
+ reader.Read(snk, 0, snk.Length);
+
+ var def = AssemblyDefinition.ReadAssembly(file, new ReaderParameters
+ {
+ ReadWrite = true,
+ InMemory = true,
+ ReadSymbols = true,
+ SymbolReaderProvider = new DefaultSymbolReaderProvider(false),
+ AssemblyResolver = new Resolver(Path.GetDirectoryName(file))
+ });
+
+ var obsoleteAttribute = def.MainModule.ImportReference(new TypeReference("System", "ObsoleteAttribute", def.MainModule,
+ def.MainModule.TypeSystem.CoreLibrary));
+ var obsoleteCtor = def.MainModule.ImportReference(new MethodReference(".ctor",
+ def.MainModule.TypeSystem.Void, obsoleteAttribute)
+ {
+ Parameters = { new ParameterDefinition(def.MainModule.TypeSystem.String) }
+ });
+
+ foreach(var t in def.MainModule.Types)
+ ProcessType(t, obsoleteCtor);
+ def.Write(file, new WriterParameters()
+ {
+ StrongNameKeyBlob = snk,
+ WriteSymbols = def.MainModule.HasSymbols,
+ SymbolWriterProvider = new EmbeddedPortablePdbWriterProvider(),
+ DeterministicMvid = def.MainModule.HasSymbols
+ });
+ }
+
+ static void ProcessType(TypeDefinition type, MethodReference obsoleteCtor)
+ {
+ foreach (var nested in type.NestedTypes)
+ ProcessType(nested, obsoleteCtor);
+ if (type.IsInterface)
+ {
+ var hideMethods = type.Name.EndsWith("Impl")
+ || (type.HasCustomAttributes && type.CustomAttributes.Any(a =>
+ a.AttributeType.FullName == "Avalonia.Metadata.PrivateApiAttribute"));
+
+ var injectMethod = hideMethods
+ || type.CustomAttributes.Any(a =>
+ a.AttributeType.FullName == "Avalonia.Metadata.NotClientImplementableAttribute");
+
+ if (hideMethods)
+ {
+ foreach (var m in type.Methods)
+ {
+ var dflags = MethodAttributes.Public | MethodAttributes.Family | MethodAttributes.FamORAssem |
+ MethodAttributes.FamANDAssem | MethodAttributes.Assembly;
+ m.Attributes = ((m.Attributes | dflags) ^ dflags) | MethodAttributes.Assembly;
+ }
+ }
+
+ if(injectMethod)
+ {
+ type.Methods.Add(new MethodDefinition("NotClientImplementable",
+ MethodAttributes.Assembly
+ | MethodAttributes.Abstract
+ | MethodAttributes.NewSlot
+ | MethodAttributes.HideBySig, type.Module.TypeSystem.Void));
+ }
+
+ var forceUnstable = type.CustomAttributes.FirstOrDefault(a =>
+ a.AttributeType.FullName == "Avalonia.Metadata.UnstableAttribute");
+
+ foreach (var m in type.Methods)
+ MarkAsUnstable(m, obsoleteCtor, forceUnstable);
+ foreach (var m in type.Properties)
+ MarkAsUnstable(m, obsoleteCtor, forceUnstable);
+ foreach (var m in type.Events)
+ MarkAsUnstable(m, obsoleteCtor, forceUnstable);
+
+ }
+ }
+
+ static void MarkAsUnstable(IMemberDefinition def, MethodReference obsoleteCtor, ICustomAttribute unstableAttribute)
+ {
+ if (def.CustomAttributes.Any(a => a.AttributeType.FullName == "System.ObsoleteAttribute"))
+ return;
+
+ unstableAttribute = def.CustomAttributes.FirstOrDefault(a =>
+ a.AttributeType.FullName == "Avalonia.Metadata.UnstableAttribute") ?? unstableAttribute;
+
+ if (unstableAttribute is null)
+ return;
+
+ var message = unstableAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString();
+ if (string.IsNullOrEmpty(message))
+ {
+ message = "This is a part of unstable API and can be changed in minor releases. Consider replacing it with alternatives or reach out developers on GitHub.";
+ }
+
+ def.CustomAttributes.Add(new CustomAttribute(obsoleteCtor)
+ {
+ ConstructorArguments =
+ {
+ new CustomAttributeArgument(obsoleteCtor.Module.TypeSystem.String, message)
+ }
+ });
+ }
+
+ public static void GenerateRefAsmsInPackage(string packagePath)
+ {
+ using (var archive = new ZipArchive(File.Open(packagePath, FileMode.Open, FileAccess.ReadWrite),
+ ZipArchiveMode.Update))
+ {
+ foreach (var entry in archive.Entries.ToList())
+ {
+ if (entry.FullName.StartsWith("ref/"))
+ entry.Delete();
+ }
+
+ foreach (var entry in archive.Entries.ToList())
+ {
+ if (entry.FullName.StartsWith("lib/") && entry.Name.EndsWith(".xml"))
+ {
+ var newEntry = archive.CreateEntry("ref/" + entry.FullName.Substring(4),
+ CompressionLevel.Optimal);
+ using (var src = entry.Open())
+ using (var dst = newEntry.Open())
+ src.CopyTo(dst);
+ }
+ }
+
+ var libs = archive.Entries.Where(e => e.FullName.StartsWith("lib/") && e.FullName.EndsWith(".dll"))
+ .Select((e => new { s = e.FullName.Split('/'), e = e }))
+ .Select(e => new { Tfm = e.s[1], Name = e.s[2], Entry = e.e })
+ .GroupBy(x => x.Tfm);
+ foreach(var tfm in libs)
+ using (Helpers.UseTempDir(out var temp))
+ {
+ foreach (var l in tfm)
+ l.Entry.ExtractToFile(Path.Combine(temp, l.Name));
+ foreach (var l in tfm)
+ PatchRefAssembly(Path.Combine(temp, l.Name));
+ foreach (var l in tfm)
+ archive.CreateEntryFromFile(Path.Combine(temp, l.Name), $"ref/{l.Tfm}/{l.Name}");
+ }
+ }
+ }
+}
diff --git a/nukebuild/_build.csproj b/nukebuild/_build.csproj
index 13bac4b7db..8999b7ca76 100644
--- a/nukebuild/_build.csproj
+++ b/nukebuild/_build.csproj
@@ -15,7 +15,7 @@
-
+
@@ -31,18 +31,11 @@
-
-
-
-
-
-
-
+
+
-
-
-
+
diff --git a/nukebuild/numerge.config b/nukebuild/numerge.config
index 09f22ec527..71b77bee93 100644
--- a/nukebuild/numerge.config
+++ b/nukebuild/numerge.config
@@ -16,6 +16,11 @@
"Id": "Avalonia.Generators",
"IgnoreMissingFrameworkBinaries": true,
"DoNotMergeDependencies": true
+ },
+ {
+ "Id": "Avalonia.Analyzers",
+ "IgnoreMissingFrameworkBinaries": true,
+ "DoNotMergeDependencies": true
}
]
}
diff --git a/packages/Avalonia/Avalonia.csproj b/packages/Avalonia/Avalonia.csproj
index f21d6fefb4..412251bc9c 100644
--- a/packages/Avalonia/Avalonia.csproj
+++ b/packages/Avalonia/Avalonia.csproj
@@ -5,6 +5,7 @@
+
all
@@ -15,6 +16,10 @@
ReferenceOutputAssembly="false"
PrivateAssets="all"
OutputItemType="Analyzer" />
+
@@ -60,4 +65,20 @@
+
+
+
+ $(IntermediateOutputPath)/AvaloniaVersion.props
+
+
+
+
+ true
+ build
+
+
+
diff --git a/packages/Avalonia/Avalonia.props b/packages/Avalonia/Avalonia.props
index 2334aa91bc..a2da228887 100644
--- a/packages/Avalonia/Avalonia.props
+++ b/packages/Avalonia/Avalonia.props
@@ -4,9 +4,11 @@
$(MSBuildThisFileDirectory)\..\tools\net461\designer\Avalonia.Designer.HostApp.exe
$(MSBuildThisFileDirectory)\..\tools\netstandard2.0\Avalonia.Build.Tasks.dll
false
+ $(UsedAvaloniaProducts);AvaloniaUI
+
diff --git a/readme.md b/readme.md
index c8135080fe..6dd556bd0d 100644
--- a/readme.md
+++ b/readme.md
@@ -1,26 +1,43 @@
-[](https://avaloniaui.net/xpf)
+
+
[](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) 
-# ⚠️ **v11 Update - Pausing community contributions**
-
-for more information see [this](https://github.com/AvaloniaUI/Avalonia/discussions/10599) discussion.
+⚠️ **v11 Update - [Pausing community contributions](https://github.com/AvaloniaUI/Avalonia/discussions/10599)**
## 📖 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](https://avaloniaui.net) is a cross-platform UI framework for dotnet, providing a flexible styling system and supporting a wide range of platforms such as Windows, macOS, Linux, iOS, Android and WebAssembly. Avalonia is mature and production ready and is used by companies, including [Schneider Electric](https://avaloniaui.net/showcase#se), [Unity](https://avaloniaui.net/showcase#unity), [JetBrains](https://avaloniaui.net/showcase#rider) and [Github](https://avaloniaui.net/showcase#github).
+
+Considered by many to be the spiritual successor to WPF, Avalonia UI provides a familiar, modern development experience for XAML developers creating cross-platform applications. While Avalonia UI is [similar to WPF](https://docs.avaloniaui.net/misc/wpf), it isn't a 1:1 copy, and you'll find plenty of improvements.
-
+For those seeking a cross-platform WPF, we have created [Avalonia XPF](https://avaloniaui.net/xpf), enabling WPF applications to run on macOS and Linux with little to no code changes. Avalonia XPF is a commercial product and is licensed per-app, per-platform.
-To see the status of some of our features, please see our [Roadmap](https://github.com/AvaloniaUI/Avalonia/issues/2239). You can also see what [breaking changes](https://github.com/AvaloniaUI/Avalonia/issues/3538) we have planned and what our [past breaking changes](https://github.com/AvaloniaUI/Avalonia/wiki/Breaking-Changes) have been. [Awesome Avalonia](https://github.com/AvaloniaCommunity/awesome-avalonia) is community-curated list of awesome Avalonia UI tools, libraries, projects and resources. Go and see what people are building with Avalonia!
+#### Roadmap
+To see the status of some of our features, please see our [Roadmap](https://github.com/AvaloniaUI/Avalonia/issues/2239).
+
+#### Breaking Changes
+You can also see what [breaking changes](https://github.com/AvaloniaUI/Avalonia/issues/3538) we have planned and what our [past breaking changes](https://github.com/AvaloniaUI/Avalonia/wiki/Breaking-Changes) have been.
+
+#### Awesome Avalonia
+[Awesome Avalonia](https://github.com/AvaloniaCommunity/awesome-avalonia) is community-curated list of awesome Avalonia UI tools, libraries, projects and resources. Go and see what people are building with Avalonia!
## 🚀 Getting Started
+See our [Get Started](https://avaloniaui.net/GettingStarted) guide to begin developing apps with Avalonia UI.
+
+### Visual Studio
The Avalonia [Visual Studio Extension](https://marketplace.visualstudio.com/items?itemName=AvaloniaTeam.AvaloniaforVisualStudio) contains project and control templates that will help you get started, or you can use the .NET Core CLI. For a starter guide see our [documentation](https://docs.avaloniaui.net/docs/getting-started).
+### JetBrains Rider
+[JetBrains Rider](https://www.jetbrains.com/rider/whatsnew/?mkt_tok=eyJpIjoiTURBNU1HSmhNV0kwTUdFMiIsInQiOiJtNnU2VEc1TlNLa1ZRVkROYmdZYVpYREJsaU1qdUhmS3dxSzRHczdYWHl0RVlTNDMwSFwvNUs3VENTNVM0bVcyNFdaRmVYZzVWTTF1N3VrQWNGTkJreEhlam1hMlB4UVVWcHBGM1dNOUxoXC95YnRQdGgyUXl1YmZCM3h3d3BVWWdBIn0%3D#avalonia-support) now has official support for Avalonia.
+
+Code completion, inspections and refactorings are supported out of the box, for XAML previewer add `https://plugins.jetbrains.com/plugins/dev/14839` to plugin repositories and install [AvaloniaRider](https://github.com/ForNeVeR/AvaloniaRider) plugin.
+
+### Avalonia Packages
Avalonia is delivered via NuGet package manager. You can find the packages here: https://www.nuget.org/packages/Avalonia/
Use these commands in the Package Manager console to install Avalonia manually:
@@ -30,31 +47,26 @@ Install-Package Avalonia.Desktop
```
## Showcase
+[](https://avaloniaui.net/showcase)
-Examples of UIs built with Avalonia
-
-([Lunacy](https://icons8.com/lunacy))
-
-
-([PlasticSCM](https://www.plasticscm.com/))
-
-([WasabiWallet](https://www.wasabiwallet.io/))
-
-## JetBrains Rider
-
-[JetBrains Rider](https://www.jetbrains.com/rider/whatsnew/?mkt_tok=eyJpIjoiTURBNU1HSmhNV0kwTUdFMiIsInQiOiJtNnU2VEc1TlNLa1ZRVkROYmdZYVpYREJsaU1qdUhmS3dxSzRHczdYWHl0RVlTNDMwSFwvNUs3VENTNVM0bVcyNFdaRmVYZzVWTTF1N3VrQWNGTkJreEhlam1hMlB4UVVWcHBGM1dNOUxoXC95YnRQdGgyUXl1YmZCM3h3d3BVWWdBIn0%3D#avalonia-support) now has official support for Avalonia.
-
-Code completion, inspections and refactorings are supported out of the box, for XAML previewer add `https://plugins.jetbrains.com/plugins/dev/14839` to plugin repositories and install [AvaloniaRider](https://github.com/ForNeVeR/AvaloniaRider) plugin.
+See what others have built with Avalonia UI on our [Showcase](https://avaloniaui.net/Showcase). We welcome submissions!
## Bleeding Edge Builds
We also have a [nightly build](https://github.com/AvaloniaUI/Avalonia/wiki/Using-nightly-build-feed) which tracks the current state of master. Although these packages are less stable than the release on NuGet.org, you'll get all the latest features and bugfixes right away and many of our users actually prefer this feed!
-## Documentation
+## Learning
-Documentation can be found at https://docs.avaloniaui.net. We also have a [tutorial](https://docs.avaloniaui.net/docs/getting-started/programming-with-avalonia) over there for newcomers.
+### Documentation
+Documentation can be found at https://docs.avaloniaui.net.
+
+### Tutorials
+We also have a [tutorial](https://docs.avaloniaui.net/docs/getting-started/programming-with-avalonia) over there for newcomers.
+
+### Samples
+We have a [range of samples](https://github.com/AvaloniaUI/Avalonia.Samples) to help you get started.
## Building and Using
@@ -116,3 +128,8 @@ We have a range of [support plans available](https://avaloniaui.net/support) for
## .NET Foundation
This project is supported by the [.NET Foundation](https://dotnetfoundation.org).
+
+## Avalonia XPF
+Unleash the full potential of your existing WPF apps with our cross-platform UI framework, enabling WPF apps to run on macOS and Linux without requiring expensive and risky rewrites.
+
+[](https://avaloniaui.net/xpf)
diff --git a/samples/VirtualizationDemo/App.xaml b/samples/AppWithoutLifetime/App.axaml
similarity index 52%
rename from samples/VirtualizationDemo/App.xaml
rename to samples/AppWithoutLifetime/App.axaml
index eb5f0e4dca..5f86b8be93 100644
--- a/samples/VirtualizationDemo/App.xaml
+++ b/samples/AppWithoutLifetime/App.axaml
@@ -1,7 +1,7 @@
-
-
-
+ x:Class="AppWithoutLifetime.App">
+
+
+
diff --git a/samples/AppWithoutLifetime/App.axaml.cs b/samples/AppWithoutLifetime/App.axaml.cs
new file mode 100644
index 0000000000..9cc99929c4
--- /dev/null
+++ b/samples/AppWithoutLifetime/App.axaml.cs
@@ -0,0 +1,12 @@
+using Avalonia;
+using Avalonia.Markup.Xaml;
+
+namespace AppWithoutLifetime;
+
+public partial class App : Application
+{
+ public override void Initialize()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+}
diff --git a/samples/AppWithoutLifetime/AppWithoutLifetime.csproj b/samples/AppWithoutLifetime/AppWithoutLifetime.csproj
new file mode 100644
index 0000000000..fce12af298
--- /dev/null
+++ b/samples/AppWithoutLifetime/AppWithoutLifetime.csproj
@@ -0,0 +1,21 @@
+
+
+ WinExe
+ net6.0
+ enable
+ app.manifest
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/AppWithoutLifetime/MainWindow.axaml b/samples/AppWithoutLifetime/MainWindow.axaml
new file mode 100644
index 0000000000..3f31cb7fae
--- /dev/null
+++ b/samples/AppWithoutLifetime/MainWindow.axaml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
diff --git a/samples/AppWithoutLifetime/MainWindow.axaml.cs b/samples/AppWithoutLifetime/MainWindow.axaml.cs
new file mode 100644
index 0000000000..d27d5d5653
--- /dev/null
+++ b/samples/AppWithoutLifetime/MainWindow.axaml.cs
@@ -0,0 +1,30 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Interactivity;
+using Avalonia.Markup.Xaml;
+
+namespace AppWithoutLifetime;
+
+public partial class MainWindow : Window
+{
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+
+ protected override void OnLoaded()
+ {
+ this.AttachDevTools();
+ base.OnLoaded();
+ }
+
+ public void Open(object sender, RoutedEventArgs e)
+ {
+ new Sub().Show(this);
+ }
+}
diff --git a/samples/AppWithoutLifetime/Program.cs b/samples/AppWithoutLifetime/Program.cs
new file mode 100644
index 0000000000..9404b9f01a
--- /dev/null
+++ b/samples/AppWithoutLifetime/Program.cs
@@ -0,0 +1,28 @@
+using Avalonia;
+using Avalonia.Controls;
+using System;
+
+namespace AppWithoutLifetime;
+
+class Program
+{
+ // Initialization code. Don't use any Avalonia, third-party APIs or any
+ // SynchronizationContext-reliant code before AppMain is called: things aren't initialized
+ // yet and stuff might break.
+ [STAThread]
+ public static void Main(string[] args)
+ {
+ BuildAvaloniaApp().Start(AppMain, args);
+ }
+
+ private static void AppMain(Application app, string[] args)
+ {
+ app.Run(new MainWindow());
+ }
+
+ // Avalonia configuration, don't remove; also used by visual designer.
+ public static AppBuilder BuildAvaloniaApp()
+ => AppBuilder.Configure()
+ .UsePlatformDetect()
+ .LogToTrace();
+}
diff --git a/samples/AppWithoutLifetime/Sub.axaml b/samples/AppWithoutLifetime/Sub.axaml
new file mode 100644
index 0000000000..d0061c432c
--- /dev/null
+++ b/samples/AppWithoutLifetime/Sub.axaml
@@ -0,0 +1,9 @@
+
+ Welcome to Avalonia Sub!
+
diff --git a/samples/AppWithoutLifetime/Sub.axaml.cs b/samples/AppWithoutLifetime/Sub.axaml.cs
new file mode 100644
index 0000000000..50c770b3a2
--- /dev/null
+++ b/samples/AppWithoutLifetime/Sub.axaml.cs
@@ -0,0 +1,24 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace AppWithoutLifetime;
+
+public partial class Sub : Window
+{
+ public Sub()
+ {
+ InitializeComponent();
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+
+ protected override void OnLoaded()
+ {
+ this.AttachDevTools();
+ base.OnLoaded();
+ }
+}
diff --git a/samples/AppWithoutLifetime/app.manifest b/samples/AppWithoutLifetime/app.manifest
new file mode 100644
index 0000000000..d653ddd4b4
--- /dev/null
+++ b/samples/AppWithoutLifetime/app.manifest
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
index e465e9caf3..877d475fb6 100644
--- a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
+++ b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
@@ -26,7 +26,7 @@
-
+
diff --git a/samples/ControlCatalog.NetCore/Properties/launchSettings.json b/samples/ControlCatalog.NetCore/Properties/launchSettings.json
index 5964ca320e..11feb94bb3 100644
--- a/samples/ControlCatalog.NetCore/Properties/launchSettings.json
+++ b/samples/ControlCatalog.NetCore/Properties/launchSettings.json
@@ -6,6 +6,10 @@
"Dxgi": {
"commandName": "Project",
"commandLineArgs": "--dxgi"
+ },
+ "VNC": {
+ "commandName": "Project",
+ "commandLineArgs": "--vnc"
}
}
-}
\ No newline at end of file
+}
diff --git a/samples/ControlCatalog/App.xaml b/samples/ControlCatalog/App.xaml
index 3b847adcbb..64bf3e53b3 100644
--- a/samples/ControlCatalog/App.xaml
+++ b/samples/ControlCatalog/App.xaml
@@ -26,8 +26,6 @@
#FFFFFFFF
- #FF0078D7
- #FF005A9E
diff --git a/samples/ControlCatalog/MainView.xaml.cs b/samples/ControlCatalog/MainView.xaml.cs
index 9c439c874f..9c511f9eb0 100644
--- a/samples/ControlCatalog/MainView.xaml.cs
+++ b/samples/ControlCatalog/MainView.xaml.cs
@@ -19,13 +19,9 @@ namespace ControlCatalog
{
public class MainView : UserControl
{
- private readonly IPlatformSettings _platformSettings;
-
public MainView()
{
AvaloniaXamlLoader.Load(this);
- _platformSettings = AvaloniaLocator.Current.GetRequiredService();
- PlatformSettingsOnColorValuesChanged(_platformSettings, _platformSettings.GetColorValues());
var sideBar = this.Get("Sidebar");
@@ -141,50 +137,6 @@ namespace ControlCatalog
ViewModel.IsSystemBarVisible = insets.IsSystemBarVisible ?? true;
};
}
-
- _platformSettings.ColorValuesChanged += PlatformSettingsOnColorValuesChanged;
- PlatformSettingsOnColorValuesChanged(_platformSettings, _platformSettings.GetColorValues());
- }
-
- protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
- {
- base.OnDetachedFromLogicalTree(e);
-
- _platformSettings.ColorValuesChanged -= PlatformSettingsOnColorValuesChanged;
- }
-
- private void PlatformSettingsOnColorValuesChanged(object? sender, PlatformColorValues e)
- {
- Application.Current!.Resources["SystemAccentColor"] = e.AccentColor1;
- Application.Current.Resources["SystemAccentColorDark1"] = ChangeColorLuminosity(e.AccentColor1, -0.3);
- Application.Current.Resources["SystemAccentColorDark2"] = ChangeColorLuminosity(e.AccentColor1, -0.5);
- Application.Current.Resources["SystemAccentColorDark3"] = ChangeColorLuminosity(e.AccentColor1, -0.7);
- Application.Current.Resources["SystemAccentColorLight1"] = ChangeColorLuminosity(e.AccentColor1, 0.3);
- Application.Current.Resources["SystemAccentColorLight2"] = ChangeColorLuminosity(e.AccentColor1, 0.5);
- Application.Current.Resources["SystemAccentColorLight3"] = ChangeColorLuminosity(e.AccentColor1, 0.7);
-
- static Color ChangeColorLuminosity(Color color, double luminosityFactor)
- {
- var red = (double)color.R;
- var green = (double)color.G;
- var blue = (double)color.B;
-
- if (luminosityFactor < 0)
- {
- luminosityFactor = 1 + luminosityFactor;
- red *= luminosityFactor;
- green *= luminosityFactor;
- blue *= luminosityFactor;
- }
- else if (luminosityFactor >= 0)
- {
- red = (255 - red) * luminosityFactor + red;
- green = (255 - green) * luminosityFactor + green;
- blue = (255 - blue) * luminosityFactor + blue;
- }
-
- return new Color(color.A, (byte)red, (byte)green, (byte)blue);
- }
}
}
}
diff --git a/samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs b/samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs
index ae7e43f511..81d379ce05 100644
--- a/samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs
+++ b/samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs
@@ -114,7 +114,7 @@ namespace ControlCatalog.Pages
foreach (AutoCompleteBox box in GetAllAutoCompleteBox().Where(x => x.Name != "CustomAutocompleteBox"))
{
- box.Items = States;
+ box.ItemsSource = States;
}
var converter = new FuncMultiValueConverter(parts =>
@@ -132,7 +132,7 @@ namespace ControlCatalog.Pages
asyncBox.AsyncPopulator = PopulateAsync;
var customAutocompleteBox = this.Get("CustomAutocompleteBox");
- customAutocompleteBox.Items = Sentences.SelectMany(x => x);
+ customAutocompleteBox.ItemsSource = Sentences.SelectMany(x => x);
customAutocompleteBox.TextFilter = LastWordContains;
customAutocompleteBox.TextSelector = AppendWord;
}
diff --git a/samples/ControlCatalog/Pages/ComboBoxPage.xaml b/samples/ControlCatalog/Pages/ComboBoxPage.xaml
index 748a46c447..f3f6cfe0af 100644
--- a/samples/ControlCatalog/Pages/ComboBoxPage.xaml
+++ b/samples/ControlCatalog/Pages/ComboBoxPage.xaml
@@ -98,6 +98,21 @@
Inline Item 3
Inline Item 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
WrapSelection
diff --git a/samples/ControlCatalog/Pages/ContextFlyoutPage.xaml b/samples/ControlCatalog/Pages/ContextFlyoutPage.xaml
index cb294442d2..56f8547740 100644
--- a/samples/ControlCatalog/Pages/ContextFlyoutPage.xaml
+++ b/samples/ControlCatalog/Pages/ContextFlyoutPage.xaml
@@ -67,7 +67,7 @@
-
+
diff --git a/samples/ControlCatalog/Pages/CustomDrawingExampleControl.cs b/samples/ControlCatalog/Pages/CustomDrawingExampleControl.cs
index 549cf3d740..782435ae06 100644
--- a/samples/ControlCatalog/Pages/CustomDrawingExampleControl.cs
+++ b/samples/ControlCatalog/Pages/CustomDrawingExampleControl.cs
@@ -19,18 +19,16 @@ namespace ControlCatalog.Pages
public static readonly StyledProperty ScaleProperty = AvaloniaProperty.Register(nameof(Scale), 1.0d);
public double Scale { get => GetValue(ScaleProperty); set => SetValue(ScaleProperty, value); }
- public static readonly StyledProperty RotationProperty = AvaloniaProperty.Register(nameof(Rotation));
+ public static readonly StyledProperty RotationProperty = AvaloniaProperty.Register(nameof(Rotation),
+ coerce: (_, val) => val % (Math.PI * 2));
+
///
/// Rotation, measured in Radians!
///
public double Rotation
{
get => GetValue(RotationProperty);
- set
- {
- double valueToUse = value % (Math.PI * 2);
- SetValue(RotationProperty, valueToUse);
- }
+ set => SetValue(RotationProperty, value);
}
public static readonly StyledProperty ViewportCenterYProperty = AvaloniaProperty.Register(nameof(ViewportCenterY), 0.0d);
@@ -213,5 +211,6 @@ namespace ControlCatalog.Pages
return workingPoint;
}
+
}
}
diff --git a/samples/ControlCatalog/Pages/DataGridPage.xaml b/samples/ControlCatalog/Pages/DataGridPage.xaml
index c39e9f0a81..fb097da71b 100644
--- a/samples/ControlCatalog/Pages/DataGridPage.xaml
+++ b/samples/ControlCatalog/Pages/DataGridPage.xaml
@@ -94,7 +94,7 @@
+ ItemsSource="{Binding DataGrid3Source}">
diff --git a/samples/ControlCatalog/Pages/DataGridPage.xaml.cs b/samples/ControlCatalog/Pages/DataGridPage.xaml.cs
index b0c3e3a553..617b0db5ed 100644
--- a/samples/ControlCatalog/Pages/DataGridPage.xaml.cs
+++ b/samples/ControlCatalog/Pages/DataGridPage.xaml.cs
@@ -36,7 +36,7 @@ namespace ControlCatalog.Pages
collectionView1.SortDescriptions.Add(dataGridSortDescription);
}
};
- dg1.Items = collectionView1;
+ dg1.ItemsSource = collectionView1;
var dg2 = this.Get("dataGridGrouping");
dg2.IsReadOnly = true;
@@ -44,7 +44,7 @@ namespace ControlCatalog.Pages
var collectionView2 = new DataGridCollectionView(Countries.All);
collectionView2.GroupDescriptions.Add(new DataGridPathGroupDescription("Region"));
- dg2.Items = collectionView2;
+ dg2.ItemsSource = collectionView2;
var dg3 = this.Get("dataGridEdit");
dg3.IsReadOnly = false;
diff --git a/samples/ControlCatalog/Pages/DialogsPage.xaml b/samples/ControlCatalog/Pages/DialogsPage.xaml
index 90c717e7ed..980b210aaa 100644
--- a/samples/ControlCatalog/Pages/DialogsPage.xaml
+++ b/samples/ControlCatalog/Pages/DialogsPage.xaml
@@ -45,7 +45,7 @@
-
+
Desktop
Documents
@@ -54,7 +54,7 @@
Videos
Music
-
+
-
diff --git a/samples/ControlCatalog/Pages/TextBlockPage.xaml b/samples/ControlCatalog/Pages/TextBlockPage.xaml
index 6511e2136a..70f83f6799 100644
--- a/samples/ControlCatalog/Pages/TextBlockPage.xaml
+++ b/samples/ControlCatalog/Pages/TextBlockPage.xaml
@@ -118,7 +118,7 @@
-
+
This is a
TextBlock
with several
diff --git a/samples/ControlCatalog/ViewModels/ComboBoxPageViewModel.cs b/samples/ControlCatalog/ViewModels/ComboBoxPageViewModel.cs
index d3e4ea7c31..6ab7bb02e3 100644
--- a/samples/ControlCatalog/ViewModels/ComboBoxPageViewModel.cs
+++ b/samples/ControlCatalog/ViewModels/ComboBoxPageViewModel.cs
@@ -16,5 +16,20 @@ namespace ControlCatalog.ViewModels
get => _wrapSelection;
set => this.RaiseAndSetIfChanged(ref _wrapSelection, value);
}
+
+ public ObservableCollection Values { get; set; } = new ObservableCollection
+ {
+ new IdAndName(){ Id = "Id 1", Name = "Name 1" },
+ new IdAndName(){ Id = "Id 2", Name = "Name 2" },
+ new IdAndName(){ Id = "Id 3", Name = "Name 3" },
+ new IdAndName(){ Id = "Id 4", Name = "Name 4" },
+ new IdAndName(){ Id = "Id 5", Name = "Name 5" },
+ };
+ }
+
+ public class IdAndName
+ {
+ public string Id { get; set; }
+ public string Name { get; set; }
}
}
diff --git a/samples/IntegrationTestApp/IntegrationTestApp.csproj b/samples/IntegrationTestApp/IntegrationTestApp.csproj
index 1356eeb526..398743a353 100644
--- a/samples/IntegrationTestApp/IntegrationTestApp.csproj
+++ b/samples/IntegrationTestApp/IntegrationTestApp.csproj
@@ -3,6 +3,7 @@
WinExe
net7.0
enable
+ $(NoWarn);AVP1012
diff --git a/samples/IntegrationTestApp/MainWindow.axaml b/samples/IntegrationTestApp/MainWindow.axaml
index 95378ed717..d8d9678a2d 100644
--- a/samples/IntegrationTestApp/MainWindow.axaml
+++ b/samples/IntegrationTestApp/MainWindow.axaml
@@ -170,10 +170,21 @@
-
-
+
+
+
+
+
+
+
+
+
-
+
+
diff --git a/samples/IntegrationTestApp/MainWindow.axaml.cs b/samples/IntegrationTestApp/MainWindow.axaml.cs
index c9c7939c1c..7130b3602a 100644
--- a/samples/IntegrationTestApp/MainWindow.axaml.cs
+++ b/samples/IntegrationTestApp/MainWindow.axaml.cs
@@ -202,7 +202,7 @@ namespace IntegrationTestApp
{
var lifetime = (ClassicDesktopStyleApplicationLifetime)Application.Current!.ApplicationLifetime!;
- foreach (var window in lifetime.Windows)
+ foreach (var window in lifetime.Windows.ToArray())
{
window.Activate();
}
@@ -212,7 +212,7 @@ namespace IntegrationTestApp
{
var lifetime = (ClassicDesktopStyleApplicationLifetime)Application.Current!.ApplicationLifetime!;
- foreach (var window in lifetime.Windows)
+ foreach (var window in lifetime.Windows.ToArray())
{
window.Show();
if (window.WindowState == WindowState.Minimized)
@@ -270,6 +270,8 @@ namespace IntegrationTestApp
this.Get("BasicListBox").SelectedIndex = -1;
if (source?.Name == "MenuClickedMenuItemReset")
this.Get("ClickedMenuItem").Text = "None";
+ if (source?.Name == "ResetSliders")
+ this.Get("HorizontalSlider").Value = 50;
if (source?.Name == "ShowTransparentWindow")
ShowTransparentWindow();
if (source?.Name == "ShowTransparentPopup")
diff --git a/samples/MobileSandbox.Desktop/MobileSandbox.Desktop.csproj b/samples/MobileSandbox.Desktop/MobileSandbox.Desktop.csproj
index a24e55de81..31a6b05175 100644
--- a/samples/MobileSandbox.Desktop/MobileSandbox.Desktop.csproj
+++ b/samples/MobileSandbox.Desktop/MobileSandbox.Desktop.csproj
@@ -19,7 +19,7 @@
-
+
diff --git a/samples/RenderDemo/Pages/AnimationsPage.xaml b/samples/RenderDemo/Pages/AnimationsPage.xaml
index 3f89a9d5f7..d764af8978 100644
--- a/samples/RenderDemo/Pages/AnimationsPage.xaml
+++ b/samples/RenderDemo/Pages/AnimationsPage.xaml
@@ -308,6 +308,41 @@
+
+
@@ -332,6 +367,11 @@
+
+
+ Drop
+ Shadow
+
diff --git a/samples/RenderDemo/Pages/CustomSkiaPage.cs b/samples/RenderDemo/Pages/CustomSkiaPage.cs
index 4a3e20ff5b..2d64ba8f7b 100644
--- a/samples/RenderDemo/Pages/CustomSkiaPage.cs
+++ b/samples/RenderDemo/Pages/CustomSkiaPage.cs
@@ -44,9 +44,9 @@ namespace RenderDemo.Pages
public bool HitTest(Point p) => false;
public bool Equals(ICustomDrawOperation other) => false;
static Stopwatch St = Stopwatch.StartNew();
- public void Render(IDrawingContextImpl context)
+ public void Render(ImmediateDrawingContext context)
{
- var leaseFeature = context.GetFeature();
+ var leaseFeature = context.TryGetFeature();
if (leaseFeature == null)
context.DrawGlyphRun(Brushes.Black, _noSkia.PlatformImpl);
else
diff --git a/samples/RenderDemo/Pages/WriteableBitmapPage.cs b/samples/RenderDemo/Pages/WriteableBitmapPage.cs
index 850e398a93..a13a625d14 100644
--- a/samples/RenderDemo/Pages/WriteableBitmapPage.cs
+++ b/samples/RenderDemo/Pages/WriteableBitmapPage.cs
@@ -59,7 +59,7 @@ namespace RenderDemo.Pages
color = new Color(fillAlpha, r, g, b);
}
- data[y * fb.Size.Width + x] = (int) color.ToUint32();
+ data[y * fb.Size.Width + x] = (int) color.ToUInt32();
}
}
diff --git a/samples/SafeAreaDemo.Android/Icon.png b/samples/SafeAreaDemo.Android/Icon.png
new file mode 100644
index 0000000000..41a2a618fb
Binary files /dev/null and b/samples/SafeAreaDemo.Android/Icon.png differ
diff --git a/samples/SafeAreaDemo.Android/MainActivity.cs b/samples/SafeAreaDemo.Android/MainActivity.cs
new file mode 100644
index 0000000000..b0f0a6e419
--- /dev/null
+++ b/samples/SafeAreaDemo.Android/MainActivity.cs
@@ -0,0 +1,11 @@
+using Android.App;
+using Android.Content.PM;
+using Avalonia.Android;
+
+namespace SafeAreaDemo.Android
+{
+ [Activity(Label = "SafeAreaDemo.Android", Theme = "@style/MyTheme.NoActionBar", Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleTop, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize | ConfigChanges.UiMode)]
+ public class MainActivity : AvaloniaMainActivity
+ {
+ }
+}
diff --git a/samples/SafeAreaDemo.Android/Properties/AndroidManifest.xml b/samples/SafeAreaDemo.Android/Properties/AndroidManifest.xml
new file mode 100644
index 0000000000..b6a5777e03
--- /dev/null
+++ b/samples/SafeAreaDemo.Android/Properties/AndroidManifest.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/samples/SafeAreaDemo.Android/Resources/drawable/splash_screen.xml b/samples/SafeAreaDemo.Android/Resources/drawable/splash_screen.xml
new file mode 100644
index 0000000000..2e920b4b3b
--- /dev/null
+++ b/samples/SafeAreaDemo.Android/Resources/drawable/splash_screen.xml
@@ -0,0 +1,13 @@
+
+
+
+ -
+
+
+
+
+
+
diff --git a/samples/SafeAreaDemo.Android/Resources/values/colors.xml b/samples/SafeAreaDemo.Android/Resources/values/colors.xml
new file mode 100644
index 0000000000..59279d5d32
--- /dev/null
+++ b/samples/SafeAreaDemo.Android/Resources/values/colors.xml
@@ -0,0 +1,4 @@
+
+
+ #FFFFFF
+
diff --git a/samples/SafeAreaDemo.Android/Resources/values/styles.xml b/samples/SafeAreaDemo.Android/Resources/values/styles.xml
new file mode 100644
index 0000000000..2759d2904a
--- /dev/null
+++ b/samples/SafeAreaDemo.Android/Resources/values/styles.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/SafeAreaDemo.Android/SafeAreaDemo.Android.csproj b/samples/SafeAreaDemo.Android/SafeAreaDemo.Android.csproj
new file mode 100644
index 0000000000..f5d2af79d0
--- /dev/null
+++ b/samples/SafeAreaDemo.Android/SafeAreaDemo.Android.csproj
@@ -0,0 +1,24 @@
+
+
+ Exe
+ net7.0-android
+ 21
+ enable
+ com.avalonia.safeareademo
+ 1
+ 1.0
+ apk
+ False
+
+
+
+
+ Resources\drawable\Icon.png
+
+
+
+
+
+
+
+
diff --git a/samples/SafeAreaDemo.Android/SplashActivity.cs b/samples/SafeAreaDemo.Android/SplashActivity.cs
new file mode 100644
index 0000000000..621ad1c675
--- /dev/null
+++ b/samples/SafeAreaDemo.Android/SplashActivity.cs
@@ -0,0 +1,30 @@
+using Android.App;
+using Android.Content;
+using Android.OS;
+using Avalonia;
+using Avalonia.Android;
+using Application = Android.App.Application;
+
+namespace SafeAreaDemo.Android
+{
+ [Activity(Theme = "@style/MyTheme.Splash", MainLauncher = true, NoHistory = true)]
+ public class SplashActivity : AvaloniaSplashActivity
+ {
+ protected override AppBuilder CustomizeAppBuilder(AppBuilder builder)
+ {
+ return base.CustomizeAppBuilder(builder);
+ }
+
+ protected override void OnCreate(Bundle? savedInstanceState)
+ {
+ base.OnCreate(savedInstanceState);
+ }
+
+ protected override void OnResume()
+ {
+ base.OnResume();
+
+ StartActivity(new Intent(Application.Context, typeof(MainActivity)));
+ }
+ }
+}
diff --git a/samples/SafeAreaDemo.Desktop/Program.cs b/samples/SafeAreaDemo.Desktop/Program.cs
new file mode 100644
index 0000000000..b07682e8c8
--- /dev/null
+++ b/samples/SafeAreaDemo.Desktop/Program.cs
@@ -0,0 +1,21 @@
+using Avalonia;
+using System;
+
+namespace SafeAreaDemo.Desktop
+{
+ internal class Program
+ {
+ // Initialization code. Don't use any Avalonia, third-party APIs or any
+ // SynchronizationContext-reliant code before AppMain is called: things aren't initialized
+ // yet and stuff might break.
+ [STAThread]
+ public static void Main(string[] args) => BuildAvaloniaApp()
+ .StartWithClassicDesktopLifetime(args);
+
+ // Avalonia configuration, don't remove; also used by visual designer.
+ public static AppBuilder BuildAvaloniaApp()
+ => AppBuilder.Configure()
+ .UsePlatformDetect()
+ .LogToTrace();
+ }
+}
diff --git a/samples/SafeAreaDemo.Desktop/SafeAreaDemo.Desktop.csproj b/samples/SafeAreaDemo.Desktop/SafeAreaDemo.Desktop.csproj
new file mode 100644
index 0000000000..a3b020d531
--- /dev/null
+++ b/samples/SafeAreaDemo.Desktop/SafeAreaDemo.Desktop.csproj
@@ -0,0 +1,24 @@
+
+
+ WinExe
+
+ net7.0
+ enable
+ true
+
+
+
+ app.manifest
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/SafeAreaDemo.Desktop/app.manifest b/samples/SafeAreaDemo.Desktop/app.manifest
new file mode 100644
index 0000000000..f0a4b00175
--- /dev/null
+++ b/samples/SafeAreaDemo.Desktop/app.manifest
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/SafeAreaDemo.iOS/AppDelegate.cs b/samples/SafeAreaDemo.iOS/AppDelegate.cs
new file mode 100644
index 0000000000..6990435d78
--- /dev/null
+++ b/samples/SafeAreaDemo.iOS/AppDelegate.cs
@@ -0,0 +1,17 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.iOS;
+using Avalonia.Media;
+using Foundation;
+using UIKit;
+
+namespace SafeAreaDemo.iOS
+{
+ // The UIApplicationDelegate for the application. This class is responsible for launching the
+ // User Interface of the application, as well as listening (and optionally responding) to
+ // application events from iOS.
+ [Register("AppDelegate")]
+ public partial class AppDelegate : AvaloniaAppDelegate
+ {
+ }
+}
diff --git a/samples/SafeAreaDemo.iOS/Entitlements.plist b/samples/SafeAreaDemo.iOS/Entitlements.plist
new file mode 100644
index 0000000000..0c67376eba
--- /dev/null
+++ b/samples/SafeAreaDemo.iOS/Entitlements.plist
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/samples/SafeAreaDemo.iOS/Info.plist b/samples/SafeAreaDemo.iOS/Info.plist
new file mode 100644
index 0000000000..ec04bd5a87
--- /dev/null
+++ b/samples/SafeAreaDemo.iOS/Info.plist
@@ -0,0 +1,47 @@
+
+
+
+
+ CFBundleDisplayName
+ SafeAreaDemo
+ CFBundleIdentifier
+ companyName.SafeAreaDemo
+ CFBundleShortVersionString
+ 1.0
+ CFBundleVersion
+ 1.0
+ LSRequiresIPhoneOS
+
+ MinimumOSVersion
+ 10.0
+ UIDeviceFamily
+
+ 1
+ 2
+
+ UILaunchStoryboardName
+ LaunchScreen
+ UIRequiredDeviceCapabilities
+
+ armv7
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UIStatusBarHidden
+
+ UIViewControllerBasedStatusBarAppearance
+
+
+
diff --git a/samples/SafeAreaDemo.iOS/Main.cs b/samples/SafeAreaDemo.iOS/Main.cs
new file mode 100644
index 0000000000..1c76dc6bc4
--- /dev/null
+++ b/samples/SafeAreaDemo.iOS/Main.cs
@@ -0,0 +1,15 @@
+using UIKit;
+
+namespace SafeAreaDemo.iOS
+{
+ public class Application
+ {
+ // This is the main entry point of the application.
+ static void Main(string[] args)
+ {
+ // if you want to use a different Application Delegate class from "AppDelegate"
+ // you can specify it here.
+ UIApplication.Main(args, null, typeof(AppDelegate));
+ }
+ }
+}
\ No newline at end of file
diff --git a/samples/SafeAreaDemo.iOS/Resources/LaunchScreen.xib b/samples/SafeAreaDemo.iOS/Resources/LaunchScreen.xib
new file mode 100644
index 0000000000..c6dd636c46
--- /dev/null
+++ b/samples/SafeAreaDemo.iOS/Resources/LaunchScreen.xib
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/SafeAreaDemo.iOS/SafeAreaDemo.iOS.csproj b/samples/SafeAreaDemo.iOS/SafeAreaDemo.iOS.csproj
new file mode 100644
index 0000000000..71365fe07d
--- /dev/null
+++ b/samples/SafeAreaDemo.iOS/SafeAreaDemo.iOS.csproj
@@ -0,0 +1,18 @@
+
+
+ Exe
+ net7.0-ios
+ 10.0
+ manual
+ enable
+ iossimulator-x64
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/SafeAreaDemo/App.xaml b/samples/SafeAreaDemo/App.xaml
new file mode 100644
index 0000000000..f5ffbdb32a
--- /dev/null
+++ b/samples/SafeAreaDemo/App.xaml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/SafeAreaDemo/App.xaml.cs b/samples/SafeAreaDemo/App.xaml.cs
new file mode 100644
index 0000000000..e23cb0e04a
--- /dev/null
+++ b/samples/SafeAreaDemo/App.xaml.cs
@@ -0,0 +1,36 @@
+using Avalonia;
+using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Markup.Xaml;
+using SafeAreaDemo.ViewModels;
+using SafeAreaDemo.Views;
+
+namespace SafeAreaDemo
+{
+ public partial class App : Application
+ {
+ public override void Initialize()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+
+ public override void OnFrameworkInitializationCompleted()
+ {
+ if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+ {
+ desktop.MainWindow = new MainWindow
+ {
+ DataContext = new MainViewModel()
+ };
+ }
+ else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewPlatform)
+ {
+ singleViewPlatform.MainView = new MainView
+ {
+ DataContext = new MainViewModel()
+ };
+ }
+
+ base.OnFrameworkInitializationCompleted();
+ }
+ }
+}
\ No newline at end of file
diff --git a/samples/SafeAreaDemo/Assets/avalonia-logo.ico b/samples/SafeAreaDemo/Assets/avalonia-logo.ico
new file mode 100644
index 0000000000..da8d49ff9b
Binary files /dev/null and b/samples/SafeAreaDemo/Assets/avalonia-logo.ico differ
diff --git a/samples/SafeAreaDemo/SafeAreaDemo.csproj b/samples/SafeAreaDemo/SafeAreaDemo.csproj
new file mode 100644
index 0000000000..20bc5ec8fe
--- /dev/null
+++ b/samples/SafeAreaDemo/SafeAreaDemo.csproj
@@ -0,0 +1,27 @@
+
+
+ net7.0
+ enable
+ latest
+ true
+
+
+
+
+
+ %(Filename)
+
+
+ Designer
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/SafeAreaDemo/ViewLocator.cs b/samples/SafeAreaDemo/ViewLocator.cs
new file mode 100644
index 0000000000..4f71fdbe9c
--- /dev/null
+++ b/samples/SafeAreaDemo/ViewLocator.cs
@@ -0,0 +1,31 @@
+using System;
+using Avalonia.Controls;
+using Avalonia.Controls.Templates;
+using MiniMvvm;
+
+namespace SafeAreaDemo
+{
+ public class ViewLocator : IDataTemplate
+ {
+ public Control? Build(object? data)
+ {
+ if (data is null)
+ return null;
+
+ var name = data.GetType().FullName!.Replace("ViewModel", "View");
+ var type = Type.GetType(name);
+
+ if (type != null)
+ {
+ return (Control)Activator.CreateInstance(type)!;
+ }
+
+ return new TextBlock { Text = name };
+ }
+
+ public bool Match(object? data)
+ {
+ return data is ViewModelBase;
+ }
+ }
+}
diff --git a/samples/SafeAreaDemo/ViewModels/MainViewModel.cs b/samples/SafeAreaDemo/ViewModels/MainViewModel.cs
new file mode 100644
index 0000000000..fe58567171
--- /dev/null
+++ b/samples/SafeAreaDemo/ViewModels/MainViewModel.cs
@@ -0,0 +1,112 @@
+using Avalonia;
+using Avalonia.Controls.Platform;
+using MiniMvvm;
+
+namespace SafeAreaDemo.ViewModels
+{
+ public class MainViewModel : ViewModelBase
+ {
+ private bool _useSafeArea = true;
+ private bool _fullscreen;
+ private IInsetsManager? _insetsManager;
+ private bool _hideSystemBars;
+
+ public Thickness SafeAreaPadding
+ {
+ get
+ {
+ return _insetsManager?.SafeAreaPadding ?? default;
+ }
+ }
+
+ public Thickness ViewPadding
+ {
+ get
+ {
+ return _useSafeArea ? SafeAreaPadding : default;
+ }
+ }
+
+ public bool UseSafeArea
+ {
+ get => _useSafeArea;
+ set
+ {
+ _useSafeArea = value;
+
+ this.RaisePropertyChanged();
+
+ RaiseSafeAreaChanged();
+ }
+ }
+
+ public bool Fullscreen
+ {
+ get => _fullscreen;
+ set
+ {
+ _fullscreen = value;
+
+ if (_insetsManager != null)
+ {
+ _insetsManager.DisplayEdgeToEdge = value;
+ }
+
+ this.RaisePropertyChanged();
+
+ RaiseSafeAreaChanged();
+ }
+ }
+
+ public bool HideSystemBars
+ {
+ get => _hideSystemBars;
+ set
+ {
+ _hideSystemBars = value;
+
+ if (_insetsManager != null)
+ {
+ _insetsManager.IsSystemBarVisible = !value;
+ }
+
+ this.RaisePropertyChanged();
+
+ RaiseSafeAreaChanged();
+ }
+ }
+
+ internal IInsetsManager? InsetsManager
+ {
+ get => _insetsManager;
+ set
+ {
+ if (_insetsManager != null)
+ {
+ _insetsManager.SafeAreaChanged -= InsetsManager_SafeAreaChanged;
+ }
+
+ _insetsManager = value;
+
+ if (_insetsManager != null)
+ {
+ _insetsManager.SafeAreaChanged += InsetsManager_SafeAreaChanged;
+
+ _insetsManager.DisplayEdgeToEdge = _fullscreen;
+ _insetsManager.IsSystemBarVisible = !_hideSystemBars;
+ }
+ }
+ }
+
+ private void InsetsManager_SafeAreaChanged(object? sender, SafeAreaChangedArgs e)
+ {
+ RaiseSafeAreaChanged();
+ }
+
+ private void RaiseSafeAreaChanged()
+ {
+ this.RaisePropertyChanged(nameof(SafeAreaPadding));
+ this.RaisePropertyChanged(nameof(ViewPadding));
+ }
+ }
+}
diff --git a/samples/SafeAreaDemo/Views/MainView.xaml b/samples/SafeAreaDemo/Views/MainView.xaml
new file mode 100644
index 0000000000..a8f7c2e735
--- /dev/null
+++ b/samples/SafeAreaDemo/Views/MainView.xaml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Fullscreen
+ Use Safe Area
+ Hide System Bars
+
+
+
+
+
+
+
diff --git a/samples/SafeAreaDemo/Views/MainView.xaml.cs b/samples/SafeAreaDemo/Views/MainView.xaml.cs
new file mode 100644
index 0000000000..2b651225e7
--- /dev/null
+++ b/samples/SafeAreaDemo/Views/MainView.xaml.cs
@@ -0,0 +1,25 @@
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+using SafeAreaDemo.ViewModels;
+
+namespace SafeAreaDemo.Views
+{
+ public partial class MainView : UserControl
+ {
+ public MainView()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+
+ protected override void OnLoaded()
+ {
+ base.OnLoaded();
+
+ var insetsManager = TopLevel.GetTopLevel(this)?.InsetsManager;
+ if (insetsManager != null && DataContext is MainViewModel viewModel)
+ {
+ viewModel.InsetsManager = insetsManager;
+ }
+ }
+ }
+}
diff --git a/samples/SafeAreaDemo/Views/MainWindow.xaml b/samples/SafeAreaDemo/Views/MainWindow.xaml
new file mode 100644
index 0000000000..ccd3028bb9
--- /dev/null
+++ b/samples/SafeAreaDemo/Views/MainWindow.xaml
@@ -0,0 +1,12 @@
+
+
+
diff --git a/samples/SafeAreaDemo/Views/MainWindow.xaml.cs b/samples/SafeAreaDemo/Views/MainWindow.xaml.cs
new file mode 100644
index 0000000000..de8f2b05ca
--- /dev/null
+++ b/samples/SafeAreaDemo/Views/MainWindow.xaml.cs
@@ -0,0 +1,13 @@
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace SafeAreaDemo.Views
+{
+ public partial class MainWindow : Window
+ {
+ public MainWindow()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+ }
+}
diff --git a/samples/Sandbox/MainWindow.axaml.cs b/samples/Sandbox/MainWindow.axaml.cs
index 23d45edf6a..e3dda25b29 100644
--- a/samples/Sandbox/MainWindow.axaml.cs
+++ b/samples/Sandbox/MainWindow.axaml.cs
@@ -1,22 +1,17 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Presenters;
+using Avalonia.Input.TextInput;
using Avalonia.Markup.Xaml;
using Avalonia.Win32.WinRT.Composition;
namespace Sandbox
{
- public class MainWindow : Window
+ public partial class MainWindow : Window
{
public MainWindow()
{
- this.InitializeComponent();
- this.AttachDevTools();
- }
-
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
+ InitializeComponent();
}
}
}
diff --git a/samples/Sandbox/Sandbox.csproj b/samples/Sandbox/Sandbox.csproj
index f23e391a2a..d2e66988e0 100644
--- a/samples/Sandbox/Sandbox.csproj
+++ b/samples/Sandbox/Sandbox.csproj
@@ -4,6 +4,7 @@
WinExe
net6.0
true
+ true
@@ -17,4 +18,5 @@
+
diff --git a/samples/VirtualizationDemo/App.axaml b/samples/VirtualizationDemo/App.axaml
new file mode 100644
index 0000000000..f5f06ffb6a
--- /dev/null
+++ b/samples/VirtualizationDemo/App.axaml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/VirtualizationDemo/App.axaml.cs b/samples/VirtualizationDemo/App.axaml.cs
new file mode 100644
index 0000000000..5ac5c9a92b
--- /dev/null
+++ b/samples/VirtualizationDemo/App.axaml.cs
@@ -0,0 +1,20 @@
+using Avalonia;
+using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Markup.Xaml;
+
+namespace VirtualizationDemo;
+
+public partial class App : Application
+{
+ public override void Initialize()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+
+ public override void OnFrameworkInitializationCompleted()
+ {
+ if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+ desktop.MainWindow = new MainWindow();
+ base.OnFrameworkInitializationCompleted();
+ }
+}
diff --git a/samples/VirtualizationDemo/App.xaml.cs b/samples/VirtualizationDemo/App.xaml.cs
deleted file mode 100644
index 81b80c1f40..0000000000
--- a/samples/VirtualizationDemo/App.xaml.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using Avalonia;
-using Avalonia.Controls.ApplicationLifetimes;
-using Avalonia.Markup.Xaml;
-
-namespace VirtualizationDemo
-{
- public class App : Application
- {
- public override void Initialize()
- {
- AvaloniaXamlLoader.Load(this);
- }
-
- public override void OnFrameworkInitializationCompleted()
- {
- if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
- desktop.MainWindow = new MainWindow();
- base.OnFrameworkInitializationCompleted();
- }
- }
-}
diff --git a/samples/VirtualizationDemo/Assets/chat.json b/samples/VirtualizationDemo/Assets/chat.json
new file mode 100644
index 0000000000..cc628b534a
--- /dev/null
+++ b/samples/VirtualizationDemo/Assets/chat.json
@@ -0,0 +1,190 @@
+{
+ "chat": [
+ {
+ "sender": "Alice",
+ "message": "Hey Bob! How was your weekend?",
+ "timestamp": "2023-04-01T10:00:00"
+ },
+ {
+ "sender": "Bob",
+ "message": "It was great, thanks for asking. I went on a camping trip with some friends. How about you?",
+ "timestamp": "2023-04-01T10:01:00"
+ },
+ {
+ "sender": "Alice",
+ "message": "My weekend was pretty chill. I just stayed home and caught up on some TV shows.",
+ "timestamp": "2023-04-01T10:03:00"
+ },
+ {
+ "sender": "Bob",
+ "message": "That sounds relaxing. What shows did you watch?",
+ "timestamp": "2023-04-01T10:05:00"
+ },
+ {
+ "sender": "Alice",
+ "message": "I watched the new season of 'Stranger Things' and started watching 'Ozark'. Have you seen them?",
+ "timestamp": "2023-04-01T10:07:00"
+ },
+ {
+ "sender": "Bob",
+ "message": "Yeah, I've seen both of those. They're really good! What do you think of them so far?",
+ "timestamp": "2023-04-01T10:10:00"
+ },
+ {
+ "sender": "Alice",
+ "message": "I'm really enjoying 'Stranger Things', but 'Ozark' is a bit darker than I expected. I'm only a few episodes in though, so we'll see how it goes.",
+ "timestamp": "2023-04-01T10:12:00"
+ },
+ {
+ "sender": "Bob",
+ "message": "Yeah, 'Ozark' can be intense at times, but it's really well done. Keep watching, it gets even better.",
+ "timestamp": "2023-04-01T10:15:00"
+ },
+ {
+ "sender": "Alice",
+ "message": "Thanks for the recommendation, I'll definitely keep watching. So, how's work been for you lately?",
+ "timestamp": "2023-04-01T10:20:00"
+ },
+ {
+ "sender": "Bob",
+ "message": "It's been pretty busy, but I'm managing. How about you?",
+ "timestamp": "2023-04-01T10:22:00"
+ },
+ {
+ "sender": "Alice",
+ "message": "Same here, things have been pretty hectic. But it keeps us on our toes, right?",
+ "timestamp": "2023-04-01T10:25:00"
+ },
+ {
+ "sender": "Bob",
+ "message": "Absolutely. Hey, have you heard about the new project we're starting next week?",
+ "timestamp": "2023-04-01T10:30:00"
+ },
+ {
+ "sender": "Alice",
+ "message": "No, I haven't. What's it about?",
+ "timestamp": "2023-04-01T10:32:00"
+ },
+ {
+ "sender": "Bob",
+ "message": "It's a big project for a new client, and it's going to require a lot of extra hours from all of us. But the pay is going to be great,so it's definitely worth the extra effort. I'll fill you in on the details later, but for now, let's just enjoy our coffee break, shall we?",
+ "timestamp": "2023-04-01T10:35:00"
+ },
+ {
+ "sender": "Alice",
+ "message": "Sounds good to me. I could use a break right about now.",
+ "timestamp": "2023-04-01T10:40:00"
+ },
+ {
+ "sender": "Bob",
+ "message": "Me too. So, have you tried the new caf� down the street yet?",
+ "timestamp": "2023-04-01T10:45:00"
+ },
+ {
+ "sender": "Alice",
+ "message": "No, I haven't. Is it any good?",
+ "timestamp": "2023-04-01T10:47:00"
+ },
+ {
+ "sender": "Bob",
+ "message": "It's really good! They have the best croissants I've ever tasted.",
+ "timestamp": "2023-04-01T10:50:00"
+ },
+ {
+ "sender": "Alice",
+ "message": "Hmm, I'll have to try it out sometime. Do they have any vegan options?",
+ "timestamp": "2023-04-01T10:52:00"
+ },
+ {
+ "sender": "Bob",
+ "message": "I'm not sure, but I think they do. You should ask them the next time you go there.",
+ "timestamp": "2023-04-01T10:55:00"
+ },
+ {
+ "sender": "Alice",
+ "message": "Thanks for the suggestion. I'm always looking for good vegan options around here.",
+ "timestamp": "2023-04-01T11:00:00"
+ },
+ {
+ "sender": "Bob",
+ "message": "No problem. So, have you made any plans for the weekend yet?",
+ "timestamp": "2023-04-01T11:05:00"
+ },
+ {
+ "sender": "Alice",
+ "message": "Not yet. I was thinking of maybe going for a hike or something. What about you?",
+ "timestamp": "2023-04-01T11:07:00"
+ },
+ {
+ "sender": "Bob",
+ "message": "I haven't made any plans either. Maybe we could do something together?",
+ "timestamp": "2023-04-01T11:10:00"
+ },
+ {
+ "sender": "Alice",
+ "message": "That sounds like a great idea! Let's plan on it.",
+ "timestamp": "2023-04-01T11:12:00"
+ },
+ {
+ "sender": "Bob",
+ "message": "Awesome. I'll check out some hiking trails and let you know which ones look good.",
+ "timestamp": "2023-04-01T11:15:00"
+ },
+ {
+ "sender": "Alice",
+ "message": "Sounds good. I can't wait!",
+ "timestamp": "2023-04-01T11:20:00"
+ },
+ {
+ "sender": "John",
+ "message": "Hey Lisa, how was your day?",
+ "timestamp": "2023-04-01T18:00:00"
+ },
+ {
+ "sender": "Lisa",
+ "message": "It was good, thanks for asking. How about you?",
+ "timestamp": "2023-04-01T18:05:00"
+ },
+ {
+ "sender": "John",
+ "message": "Eh, it was alright. Work was pretty busy, but nothing too crazy.",
+ "timestamp": "2023-04-01T18:10:00"
+ },
+ {
+ "sender": "Lisa",
+ "message": "Yeah, I know what you mean. My boss has been on my case lately about meeting our deadlines.",
+ "timestamp": "2023-04-01T18:15:00"
+ },
+ {
+ "sender": "John",
+ "message": "That sucks. Are you feeling stressed out?",
+ "timestamp": "2023-04-01T18:20:00"
+ },
+ {
+ "sender": "Lisa",
+ "message": "A little bit, yeah. But I'm trying to stay positive and focus on getting my work done.",
+ "timestamp": "2023-04-01T18:25:00"
+ },
+ {
+ "sender": "John",
+ "message": "That's a good attitude to have. Have you tried doing some meditation or other relaxation techniques?",
+ "timestamp": "2023-04-01T18:30:00"
+ },
+ {
+ "sender": "Lisa",
+ "message": "I haven't, but I've been thinking about it. Do you have any suggestions?",
+ "timestamp": "2023-04-01T18:35:00"
+ },
+ {
+ "sender": "John",
+ "message": "Sure, I could send you some links to guided meditations that I've found helpful. And there are also some great apps out there that can help you with relaxation.",
+ "timestamp": "2023-04-01T18:40:00"
+ },
+ {
+ "sender": "Lisa",
+ "message": "That would be awesome, thanks so much!",
+ "timestamp": "2023-04-01T18:45:00"
+ }
+ ]
+}
+
diff --git a/samples/VirtualizationDemo/MainWindow.axaml b/samples/VirtualizationDemo/MainWindow.axaml
new file mode 100644
index 0000000000..04e75450bf
--- /dev/null
+++ b/samples/VirtualizationDemo/MainWindow.axaml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/VirtualizationDemo/MainWindow.axaml.cs b/samples/VirtualizationDemo/MainWindow.axaml.cs
new file mode 100644
index 0000000000..533dc00aa1
--- /dev/null
+++ b/samples/VirtualizationDemo/MainWindow.axaml.cs
@@ -0,0 +1,15 @@
+using Avalonia;
+using Avalonia.Controls;
+using VirtualizationDemo.ViewModels;
+
+namespace VirtualizationDemo;
+
+public partial class MainWindow : Window
+{
+ public MainWindow()
+ {
+ InitializeComponent();
+ this.AttachDevTools();
+ DataContext = new MainWindowViewModel();
+ }
+}
diff --git a/samples/VirtualizationDemo/MainWindow.xaml b/samples/VirtualizationDemo/MainWindow.xaml
deleted file mode 100644
index 3aee63c246..0000000000
--- a/samples/VirtualizationDemo/MainWindow.xaml
+++ /dev/null
@@ -1,64 +0,0 @@
-
-
-
-
-
-
-
-
- Horiz. ScrollBar
-
- Vert. ScrollBar
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/samples/VirtualizationDemo/MainWindow.xaml.cs b/samples/VirtualizationDemo/MainWindow.xaml.cs
deleted file mode 100644
index cea200dcec..0000000000
--- a/samples/VirtualizationDemo/MainWindow.xaml.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using Avalonia;
-using Avalonia.Controls;
-using Avalonia.Markup.Xaml;
-using VirtualizationDemo.ViewModels;
-
-namespace VirtualizationDemo
-{
- public class MainWindow : Window
- {
- public MainWindow()
- {
- this.InitializeComponent();
- this.AttachDevTools();
- DataContext = new MainWindowViewModel();
- }
-
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
- }
-}
diff --git a/samples/VirtualizationDemo/Models/Chat.cs b/samples/VirtualizationDemo/Models/Chat.cs
new file mode 100644
index 0000000000..813e8650f5
--- /dev/null
+++ b/samples/VirtualizationDemo/Models/Chat.cs
@@ -0,0 +1,23 @@
+using System;
+using System.IO;
+using System.Text.Json;
+
+namespace VirtualizationDemo.Models;
+
+public class ChatFile
+{
+ public ChatMessage[]? Chat { get; set; }
+
+ public static ChatFile Load(string path)
+ {
+ var options = new JsonSerializerOptions
+ {
+ PropertyNameCaseInsensitive = true
+ };
+
+ using var s = File.OpenRead(path);
+ return JsonSerializer.Deserialize(s, options)!;
+ }
+}
+
+public record ChatMessage(string Sender, string Message, DateTimeOffset Timestamp);
diff --git a/samples/VirtualizationDemo/Program.cs b/samples/VirtualizationDemo/Program.cs
index febda46450..87212b6daa 100644
--- a/samples/VirtualizationDemo/Program.cs
+++ b/samples/VirtualizationDemo/Program.cs
@@ -1,15 +1,14 @@
using Avalonia;
-namespace VirtualizationDemo
+namespace VirtualizationDemo;
+
+class Program
{
- class Program
- {
- public static AppBuilder BuildAvaloniaApp()
- => AppBuilder.Configure()
- .UsePlatformDetect()
- .LogToTrace();
+ public static AppBuilder BuildAvaloniaApp()
+ => AppBuilder.Configure()
+ .UsePlatformDetect()
+ .LogToTrace();
- public static int Main(string[] args)
- => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
- }
+ public static int Main(string[] args)
+ => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
}
diff --git a/samples/VirtualizationDemo/ViewModels/ChatPageViewModel.cs b/samples/VirtualizationDemo/ViewModels/ChatPageViewModel.cs
new file mode 100644
index 0000000000..c0abe62bd5
--- /dev/null
+++ b/samples/VirtualizationDemo/ViewModels/ChatPageViewModel.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.ObjectModel;
+using System.IO;
+using VirtualizationDemo.Models;
+
+namespace VirtualizationDemo.ViewModels;
+
+public class ChatPageViewModel
+{
+ public ChatPageViewModel()
+ {
+ var chat = ChatFile.Load(Path.Combine("Assets", "chat.json"));
+ Messages = new(chat.Chat ?? Array.Empty());
+ }
+
+ public ObservableCollection Messages { get; }
+}
diff --git a/samples/VirtualizationDemo/ViewModels/ExpanderItemViewModel.cs b/samples/VirtualizationDemo/ViewModels/ExpanderItemViewModel.cs
new file mode 100644
index 0000000000..a17fc2d303
--- /dev/null
+++ b/samples/VirtualizationDemo/ViewModels/ExpanderItemViewModel.cs
@@ -0,0 +1,21 @@
+using MiniMvvm;
+
+namespace VirtualizationDemo.ViewModels;
+
+public class ExpanderItemViewModel : ViewModelBase
+{
+ private string? _header;
+ private bool _isExpanded;
+
+ public string? Header
+ {
+ get => _header;
+ set => RaiseAndSetIfChanged(ref _header, value);
+ }
+
+ public bool IsExpanded
+ {
+ get => _isExpanded;
+ set => RaiseAndSetIfChanged(ref _isExpanded, value);
+ }
+}
diff --git a/samples/VirtualizationDemo/ViewModels/ExpanderPageViewModel.cs b/samples/VirtualizationDemo/ViewModels/ExpanderPageViewModel.cs
new file mode 100644
index 0000000000..f2807a803b
--- /dev/null
+++ b/samples/VirtualizationDemo/ViewModels/ExpanderPageViewModel.cs
@@ -0,0 +1,17 @@
+using System.Collections.ObjectModel;
+using System.Linq;
+
+namespace VirtualizationDemo.ViewModels;
+
+internal class ExpanderPageViewModel
+{
+ public ExpanderPageViewModel()
+ {
+ Items = new(Enumerable.Range(0, 100).Select(x => new ExpanderItemViewModel
+ {
+ Header = $"Item {x}",
+ }));
+ }
+
+ public ObservableCollection Items { get; set; }
+}
diff --git a/samples/VirtualizationDemo/ViewModels/ItemViewModel.cs b/samples/VirtualizationDemo/ViewModels/ItemViewModel.cs
deleted file mode 100644
index 9ba505ffe5..0000000000
--- a/samples/VirtualizationDemo/ViewModels/ItemViewModel.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-using System;
-using MiniMvvm;
-
-namespace VirtualizationDemo.ViewModels
-{
- internal class ItemViewModel : ViewModelBase
- {
- private string _prefix;
- private int _index;
- private double _height = double.NaN;
-
- public ItemViewModel(int index, string prefix = "Item")
- {
- _prefix = prefix;
- _index = index;
- }
-
- public string Header => $"{_prefix} {_index}";
-
- public double Height
- {
- get => _height;
- set => this.RaiseAndSetIfChanged(ref _height, value);
- }
- }
-}
diff --git a/samples/VirtualizationDemo/ViewModels/MainWindowViewModel.cs b/samples/VirtualizationDemo/ViewModels/MainWindowViewModel.cs
index 96dbbc1a83..478e40187e 100644
--- a/samples/VirtualizationDemo/ViewModels/MainWindowViewModel.cs
+++ b/samples/VirtualizationDemo/ViewModels/MainWindowViewModel.cs
@@ -1,160 +1,10 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Reactive;
-using Avalonia.Collections;
-using Avalonia.Controls;
-using Avalonia.Controls.Primitives;
-using Avalonia.Layout;
-using Avalonia.Controls.Selection;
-using MiniMvvm;
+using MiniMvvm;
-namespace VirtualizationDemo.ViewModels
-{
- internal class MainWindowViewModel : ViewModelBase
- {
- private int _itemCount = 200;
- private string _newItemString = "New Item";
- private int _newItemIndex;
- private AvaloniaList _items;
- private string _prefix = "Item";
- private ScrollBarVisibility _horizontalScrollBarVisibility = ScrollBarVisibility.Auto;
- private ScrollBarVisibility _verticalScrollBarVisibility = ScrollBarVisibility.Auto;
- private Orientation _orientation = Orientation.Vertical;
-
- public MainWindowViewModel()
- {
- this.WhenAnyValue(x => x.ItemCount).Subscribe(ResizeItems);
- RecreateCommand = MiniCommand.Create(() => Recreate());
-
- AddItemCommand = MiniCommand.Create(() => AddItem());
-
- RemoveItemCommand = MiniCommand.Create(() => Remove());
-
- SelectFirstCommand = MiniCommand.Create(() => SelectItem(0));
-
- SelectLastCommand = MiniCommand.Create(() => SelectItem(Items.Count - 1));
- }
-
- public string NewItemString
- {
- get { return _newItemString; }
- set { this.RaiseAndSetIfChanged(ref _newItemString, value); }
- }
-
- public int ItemCount
- {
- get { return _itemCount; }
- set { this.RaiseAndSetIfChanged(ref _itemCount, value); }
- }
-
- public SelectionModel Selection { get; } = new SelectionModel();
-
- public AvaloniaList Items
- {
- get { return _items; }
- private set { this.RaiseAndSetIfChanged(ref _items, value); }
- }
-
- public Orientation Orientation
- {
- get { return _orientation; }
- set { this.RaiseAndSetIfChanged(ref _orientation, value); }
- }
-
- public IEnumerable Orientations =>
- Enum.GetValues(typeof(Orientation)).Cast();
-
- public ScrollBarVisibility HorizontalScrollBarVisibility
- {
- get { return _horizontalScrollBarVisibility; }
- set { this.RaiseAndSetIfChanged(ref _horizontalScrollBarVisibility, value); }
- }
+namespace VirtualizationDemo.ViewModels;
- public ScrollBarVisibility VerticalScrollBarVisibility
- {
- get { return _verticalScrollBarVisibility; }
- set { this.RaiseAndSetIfChanged(ref _verticalScrollBarVisibility, value); }
- }
-
- public IEnumerable ScrollBarVisibilities =>
- Enum.GetValues(typeof(ScrollBarVisibility)).Cast();
-
- public MiniCommand AddItemCommand { get; private set; }
- public MiniCommand RecreateCommand { get; private set; }
- public MiniCommand RemoveItemCommand { get; private set; }
- public MiniCommand SelectFirstCommand { get; private set; }
- public MiniCommand SelectLastCommand { get; private set; }
-
- public void RandomizeSize()
- {
- var random = new Random();
-
- foreach (var i in Items)
- {
- i.Height = random.Next(240) + 10;
- }
- }
-
- public void ResetSize()
- {
- foreach (var i in Items)
- {
- i.Height = double.NaN;
- }
- }
-
- private void ResizeItems(int count)
- {
- if (Items == null)
- {
- var items = Enumerable.Range(0, count)
- .Select(x => new ItemViewModel(x));
- Items = new AvaloniaList(items);
- }
- else if (count > Items.Count)
- {
- var items = Enumerable.Range(Items.Count, count - Items.Count)
- .Select(x => new ItemViewModel(x));
- Items.AddRange(items);
- }
- else if (count < Items.Count)
- {
- Items.RemoveRange(count, Items.Count - count);
- }
- }
-
- private void AddItem()
- {
- var index = Items.Count;
-
- if (Selection.SelectedItems.Count > 0)
- {
- index = Selection.SelectedIndex;
- }
-
- Items.Insert(index, new ItemViewModel(_newItemIndex++, NewItemString));
- }
-
- private void Remove()
- {
- if (Selection.SelectedItems.Count > 0)
- {
- Items.RemoveAll(Selection.SelectedItems.ToList());
- }
- }
-
- private void Recreate()
- {
- _prefix = _prefix == "Item" ? "Recreated" : "Item";
- var items = Enumerable.Range(0, _itemCount)
- .Select(x => new ItemViewModel(x, _prefix));
- Items = new AvaloniaList(items);
- }
-
- private void SelectItem(int index)
- {
- Selection.SelectedIndex = index;
- }
- }
+internal class MainWindowViewModel : ViewModelBase
+{
+ public PlaygroundPageViewModel Playground { get; } = new();
+ public ChatPageViewModel Chat { get; } = new();
+ public ExpanderPageViewModel Expanders { get; } = new();
}
diff --git a/samples/VirtualizationDemo/ViewModels/PlaygroundItemViewModel.cs b/samples/VirtualizationDemo/ViewModels/PlaygroundItemViewModel.cs
new file mode 100644
index 0000000000..584ef4600b
--- /dev/null
+++ b/samples/VirtualizationDemo/ViewModels/PlaygroundItemViewModel.cs
@@ -0,0 +1,17 @@
+using MiniMvvm;
+
+namespace VirtualizationDemo.ViewModels;
+
+public class PlaygroundItemViewModel : ViewModelBase
+{
+ private string? _header;
+
+ public PlaygroundItemViewModel(int index) => Header = $"Item {index}";
+ public PlaygroundItemViewModel(string? header) => Header = header;
+
+ public string? Header
+ {
+ get => _header;
+ set => RaiseAndSetIfChanged(ref _header, value);
+ }
+}
diff --git a/samples/VirtualizationDemo/ViewModels/PlaygroundPageViewModel.cs b/samples/VirtualizationDemo/ViewModels/PlaygroundPageViewModel.cs
new file mode 100644
index 0000000000..98ab91b0a6
--- /dev/null
+++ b/samples/VirtualizationDemo/ViewModels/PlaygroundPageViewModel.cs
@@ -0,0 +1,95 @@
+using System;
+using System.Collections.ObjectModel;
+using System.Linq;
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Controls.Selection;
+using MiniMvvm;
+
+namespace VirtualizationDemo.ViewModels;
+
+public class PlaygroundPageViewModel : ViewModelBase
+{
+ private SelectionMode _selectionMode = SelectionMode.Multiple;
+ private int _scrollToIndex = 500;
+ private string? _newItemHeader = "New Item 1";
+
+ public PlaygroundPageViewModel()
+ {
+ Items = new(Enumerable.Range(0, 1000).Select(x => new PlaygroundItemViewModel(x)));
+ Selection = new();
+ }
+
+ public ObservableCollection Items { get; }
+
+ public bool Multiple
+ {
+ get => _selectionMode.HasAnyFlag(SelectionMode.Multiple);
+ set => SetSelectionMode(SelectionMode.Multiple, value);
+ }
+
+ public bool Toggle
+ {
+ get => _selectionMode.HasAnyFlag(SelectionMode.Toggle);
+ set => SetSelectionMode(SelectionMode.Toggle, value);
+ }
+
+ public bool AlwaysSelected
+ {
+ get => _selectionMode.HasAnyFlag(SelectionMode.AlwaysSelected);
+ set => SetSelectionMode(SelectionMode.AlwaysSelected, value);
+ }
+
+ public SelectionModel Selection { get; }
+
+ public SelectionMode SelectionMode
+ {
+ get => _selectionMode;
+ set => RaiseAndSetIfChanged(ref _selectionMode, value);
+ }
+
+ public int ScrollToIndex
+ {
+ get => _scrollToIndex;
+ set => RaiseAndSetIfChanged(ref _scrollToIndex, value);
+ }
+
+ public string? NewItemHeader
+ {
+ get => _newItemHeader;
+ set => RaiseAndSetIfChanged(ref _newItemHeader, value);
+ }
+
+ public void ExecuteScrollToIndex()
+ {
+ Selection.Select(ScrollToIndex);
+ }
+
+ public void RandomizeScrollToIndex()
+ {
+ var rnd = new Random();
+ ScrollToIndex = rnd.Next(Items.Count);
+ }
+
+ public void AddAtSelectedIndex()
+ {
+ if (Selection.SelectedIndex == -1)
+ return;
+ Items.Insert(Selection.SelectedIndex, new(NewItemHeader));
+ }
+
+ public void DeleteSelectedItem()
+ {
+ var count = Selection.Count;
+ for (var i = count - 1; i >= 0; i--)
+ Items.RemoveAt(Selection.SelectedIndexes[i]);
+ }
+
+ private void SetSelectionMode(SelectionMode mode, bool value)
+ {
+ if (value)
+ SelectionMode |= mode;
+ else
+ SelectionMode &= ~mode;
+ }
+}
diff --git a/samples/VirtualizationDemo/Views/ChatPageView.axaml b/samples/VirtualizationDemo/Views/ChatPageView.axaml
new file mode 100644
index 0000000000..fc182f15ae
--- /dev/null
+++ b/samples/VirtualizationDemo/Views/ChatPageView.axaml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/VirtualizationDemo/Views/ChatPageView.axaml.cs b/samples/VirtualizationDemo/Views/ChatPageView.axaml.cs
new file mode 100644
index 0000000000..b5c90db69c
--- /dev/null
+++ b/samples/VirtualizationDemo/Views/ChatPageView.axaml.cs
@@ -0,0 +1,11 @@
+using Avalonia.Controls;
+
+namespace VirtualizationDemo.Views;
+
+public partial class ChatPageView : UserControl
+{
+ public ChatPageView()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/samples/VirtualizationDemo/Views/ExpanderPageView.axaml b/samples/VirtualizationDemo/Views/ExpanderPageView.axaml
new file mode 100644
index 0000000000..972d885229
--- /dev/null
+++ b/samples/VirtualizationDemo/Views/ExpanderPageView.axaml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/VirtualizationDemo/Views/ExpanderPageView.axaml.cs b/samples/VirtualizationDemo/Views/ExpanderPageView.axaml.cs
new file mode 100644
index 0000000000..df3689cf24
--- /dev/null
+++ b/samples/VirtualizationDemo/Views/ExpanderPageView.axaml.cs
@@ -0,0 +1,13 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace VirtualizationDemo.Views;
+
+public partial class ExpanderPageView : UserControl
+{
+ public ExpanderPageView()
+ {
+ InitializeComponent();
+ }
+}
\ No newline at end of file
diff --git a/samples/VirtualizationDemo/Views/PlaygroundPageView.axaml b/samples/VirtualizationDemo/Views/PlaygroundPageView.axaml
new file mode 100644
index 0000000000..52bc6fd27a
--- /dev/null
+++ b/samples/VirtualizationDemo/Views/PlaygroundPageView.axaml
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+ Multiple
+ Toggle
+ AlwaysSelected
+ AutoScrollToSelectedItem
+ WrapSelection
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/VirtualizationDemo/Views/PlaygroundPageView.axaml.cs b/samples/VirtualizationDemo/Views/PlaygroundPageView.axaml.cs
new file mode 100644
index 0000000000..5282475778
--- /dev/null
+++ b/samples/VirtualizationDemo/Views/PlaygroundPageView.axaml.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Linq;
+using System.Threading;
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+using Avalonia.Threading;
+
+namespace VirtualizationDemo.Views;
+
+public partial class PlaygroundPageView : UserControl
+{
+ private DispatcherTimer _timer;
+
+ public PlaygroundPageView()
+ {
+ InitializeComponent();
+
+ _timer = new DispatcherTimer
+ {
+ Interval = TimeSpan.FromMilliseconds(500),
+ };
+
+ _timer.Tick += TimerTick;
+ }
+
+ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
+ {
+ base.OnAttachedToVisualTree(e);
+ _timer.Start();
+ }
+
+ protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
+ {
+ base.OnDetachedFromVisualTree(e);
+ _timer.Stop();
+ }
+
+ private void TimerTick(object? sender, EventArgs e)
+ {
+ var message = $"Realized {list.GetRealizedContainers().Count()} of {list.ItemsPanelRoot?.Children.Count}";
+ itemCount.Text = message;
+ }
+}
diff --git a/samples/VirtualizationDemo/VirtualizationDemo.csproj b/samples/VirtualizationDemo/VirtualizationDemo.csproj
index 81b30c6cbe..3ac7aab589 100644
--- a/samples/VirtualizationDemo/VirtualizationDemo.csproj
+++ b/samples/VirtualizationDemo/VirtualizationDemo.csproj
@@ -1,19 +1,24 @@
- Exe
+ WinExe
net6.0
+ true
+
+
+
-
-
+
+
+
+
+ PreserveNewest
+
-
-
-
-
-
+
+
diff --git a/src/Android/Avalonia.Android/Platform/AndroidInsetsManager.cs b/src/Android/Avalonia.Android/Platform/AndroidInsetsManager.cs
index 549815a036..251a177432 100644
--- a/src/Android/Avalonia.Android/Platform/AndroidInsetsManager.cs
+++ b/src/Android/Avalonia.Android/Platform/AndroidInsetsManager.cs
@@ -81,7 +81,7 @@ namespace Avalonia.Android.Platform
var renderScaling = _topLevel.RenderScaling;
var inset = insets.GetInsets(
- (DisplayEdgeToEdge ?
+ (_displayEdgeToEdge ?
WindowInsetsCompat.Type.StatusBars() | WindowInsetsCompat.Type.NavigationBars() |
WindowInsetsCompat.Type.DisplayCutout() :
0) | WindowInsetsCompat.Type.Ime());
@@ -91,8 +91,8 @@ namespace Avalonia.Android.Platform
return new Thickness(inset.Left / renderScaling,
inset.Top / renderScaling,
inset.Right / renderScaling,
- (imeInset.Bottom > 0 && ((_usesLegacyLayouts && !DisplayEdgeToEdge) || !_usesLegacyLayouts) ?
- imeInset.Bottom - navBarInset.Bottom :
+ (imeInset.Bottom > 0 && ((_usesLegacyLayouts && !_displayEdgeToEdge) || !_usesLegacyLayouts) ?
+ imeInset.Bottom - (_displayEdgeToEdge ? 0 : navBarInset.Bottom) :
inset.Bottom) / renderScaling);
}
diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
index 126c488d59..fae1aacf61 100644
--- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
+++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
@@ -91,7 +91,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
public Action Paint { get; set; }
- public Action Resized { get; set; }
+ public Action Resized { get; set; }
public Action ScalingChanged { get; set; }
@@ -156,12 +156,12 @@ namespace Avalonia.Android.Platform.SkiaPlatform
protected virtual void OnResized(Size size)
{
- Resized?.Invoke(size, PlatformResizeReason.Unspecified);
+ Resized?.Invoke(size, WindowResizeReason.Unspecified);
}
internal void Resize(Size size)
{
- Resized?.Invoke(size, PlatformResizeReason.Layout);
+ Resized?.Invoke(size, WindowResizeReason.Layout);
}
class ViewImpl : InvalidationAwareSurfaceView, ISurfaceHolderCallback, IInitEditorInfo
diff --git a/src/Avalonia.Base/Animation/Animation.cs b/src/Avalonia.Base/Animation/Animation.cs
index d62acc0d52..dd99c40cd3 100644
--- a/src/Avalonia.Base/Animation/Animation.cs
+++ b/src/Avalonia.Base/Animation/Animation.cs
@@ -200,7 +200,7 @@ namespace Avalonia.Animation
///
/// The animation setter.
/// The property animator value.
- public static void SetAnimator(IAnimationSetter setter,
+ public static void SetAnimator(IAnimationSetter setter,
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicMethods)]
Type value)
{
@@ -319,7 +319,7 @@ namespace Avalonia.Animation
if (animators.Count == 1)
{
var subscription = animators[0].Apply(this, control, clock, match, onComplete);
-
+
if (subscription is not null)
{
subscriptions.Add(subscription);
@@ -348,9 +348,11 @@ namespace Avalonia.Animation
if (onComplete != null)
{
- Task.WhenAll(completionTasks!).ContinueWith(
- (_, state) => ((Action)state!).Invoke(),
- onComplete);
+ Task.WhenAll(completionTasks!)
+ .ContinueWith((_, state) => ((Action)state!).Invoke()
+ , onComplete
+ , TaskScheduler.FromCurrentSynchronizationContext()
+ );
}
}
return new CompositeDisposable(subscriptions);
diff --git a/src/Avalonia.Base/Avalonia.Base.csproj b/src/Avalonia.Base/Avalonia.Base.csproj
index 639c27bf03..eafff3b780 100644
--- a/src/Avalonia.Base/Avalonia.Base.csproj
+++ b/src/Avalonia.Base/Avalonia.Base.csproj
@@ -22,6 +22,7 @@
+
diff --git a/src/Avalonia.Base/ClassBindingManager.cs b/src/Avalonia.Base/ClassBindingManager.cs
index a9726cb86e..55f3a7892a 100644
--- a/src/Avalonia.Base/ClassBindingManager.cs
+++ b/src/Avalonia.Base/ClassBindingManager.cs
@@ -17,6 +17,8 @@ namespace Avalonia
return target.Bind(prop, source, anchor);
}
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1001:The same AvaloniaProperty should not be registered twice",
+ Justification = "Classes.attr binding feature is implemented using intermediate avalonia properties for each class")]
private static AvaloniaProperty RegisterClassProxyProperty(string className)
{
var prop = AvaloniaProperty.Register("__AvaloniaReserved::Classes::" + className);
diff --git a/src/Avalonia.Base/Collections/AvaloniaDictionaryExtensions.cs b/src/Avalonia.Base/Collections/AvaloniaDictionaryExtensions.cs
index e350a019d4..8c731c188f 100644
--- a/src/Avalonia.Base/Collections/AvaloniaDictionaryExtensions.cs
+++ b/src/Avalonia.Base/Collections/AvaloniaDictionaryExtensions.cs
@@ -35,7 +35,7 @@ namespace Avalonia.Collections
/// Indicates if a weak subscription should be used to track changes to the collection.
///
/// A disposable used to terminate the subscription.
- internal static IDisposable ForEachItem(
+ public static IDisposable ForEachItem(
this IAvaloniaReadOnlyDictionary collection,
Action added,
Action removed,
diff --git a/src/Avalonia.Base/Controls/IResourceDictionary.cs b/src/Avalonia.Base/Controls/IResourceDictionary.cs
index 2bd1f65638..6712498bf4 100644
--- a/src/Avalonia.Base/Controls/IResourceDictionary.cs
+++ b/src/Avalonia.Base/Controls/IResourceDictionary.cs
@@ -18,6 +18,6 @@ namespace Avalonia.Controls
///
/// Gets a collection of merged resource dictionaries that are specifically keyed and composed to address theme scenarios.
///
- IDictionary ThemeDictionaries { get; }
+ IDictionary ThemeDictionaries { get; }
}
}
diff --git a/src/Avalonia.Base/Controls/IThemeVariantProvider.cs b/src/Avalonia.Base/Controls/IThemeVariantProvider.cs
new file mode 100644
index 0000000000..03a7fb1206
--- /dev/null
+++ b/src/Avalonia.Base/Controls/IThemeVariantProvider.cs
@@ -0,0 +1,21 @@
+using Avalonia.Metadata;
+using Avalonia.Styling;
+
+namespace Avalonia.Controls;
+
+///
+/// Resource provider with theme variant awareness.
+/// Can be used with .
+///
+///
+/// This is a helper interface for the XAML compiler to make Key property accessibly by the markup extensions.
+/// Which means, it can only be used with ResourceDictionaries and markup extensions in the XAML code.
+///
+[Unstable("This XAML-only API might be removed in the future minor updates.")]
+public interface IThemeVariantProvider : IResourceProvider
+{
+ ///
+ /// Key property set by the compiler.
+ ///
+ ThemeVariant? Key { get; set; }
+}
diff --git a/src/Avalonia.Base/Controls/ResourceDictionary.cs b/src/Avalonia.Base/Controls/ResourceDictionary.cs
index 231a19baab..b928cf0672 100644
--- a/src/Avalonia.Base/Controls/ResourceDictionary.cs
+++ b/src/Avalonia.Base/Controls/ResourceDictionary.cs
@@ -13,13 +13,13 @@ namespace Avalonia.Controls
///
/// An indexed dictionary of resources.
///
- public class ResourceDictionary : IResourceDictionary
+ public class ResourceDictionary : IResourceDictionary, IThemeVariantProvider
{
private object? lastDeferredItemKey;
private Dictionary
diff --git a/src/Avalonia.Build.Tasks/SpanCompat.cs b/src/Avalonia.Build.Tasks/SpanCompat.cs
index be59ff8b6c..00892d56e6 100644
--- a/src/Avalonia.Build.Tasks/SpanCompat.cs
+++ b/src/Avalonia.Build.Tasks/SpanCompat.cs
@@ -85,31 +85,7 @@ namespace System
{
return TrimStart().TrimEnd();
}
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public bool TryParseUInt(NumberStyles style, IFormatProvider provider, out uint value)
- {
- return uint.TryParse(ToString(), style, provider, out value);
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public bool TryParseInt(out int value)
- {
- return int.TryParse(ToString(), out value);
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public bool TryParseDouble(NumberStyles style, IFormatProvider provider, out double value)
- {
- return double.TryParse(ToString(), style, provider, out value);
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public bool TryParseByte(NumberStyles style, IFormatProvider provider, out byte value)
- {
- return byte.TryParse(ToString(), style, provider, out value);
- }
-
+
public override string ToString() => _length == 0 ? string.Empty : _s.Substring(_start, _length);
internal int IndexOf(ReadOnlySpan v, StringComparison ordinal, int start = 0)
diff --git a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
index a394d47904..d71070e818 100644
--- a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
+++ b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
@@ -149,7 +149,7 @@ namespace Avalonia.Build.Tasks
{
var ctor = asm.MainModule.ImportReference(typeSystem.GetTypeReference(asmMetadata).Resolve()
.GetConstructors().First(c => c.Parameters.Count == 2).Resolve());
- var strType = asm.MainModule.ImportReference(typeof(string));
+ var strType = asm.MainModule.TypeSystem.String;
var arg1 = new CustomAttributeArgument(strType, "AvaloniaUseCompiledBindingsByDefault");
var arg2 = new CustomAttributeArgument(strType, defaultCompileBindings.ToString());
asm.CustomAttributes.Add(new CustomAttribute(ctor) { ConstructorArguments = { arg1, arg2 } });
diff --git a/src/Avalonia.Controls.DataGrid/DataGrid.cs b/src/Avalonia.Controls.DataGrid/DataGrid.cs
index 944e7b737a..a55a47fa53 100644
--- a/src/Avalonia.Controls.DataGrid/DataGrid.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGrid.cs
@@ -152,8 +152,6 @@ namespace Avalonia.Controls
private double _verticalOffset;
private byte _verticalScrollChangesIgnored;
- private IEnumerable _items;
-
public event EventHandler HorizontalScroll;
public event EventHandler VerticalScroll;
@@ -652,21 +650,18 @@ namespace Avalonia.Controls
}
///
- /// Identifies the ItemsSource dependency property.
+ /// Identifies the ItemsSource property.
///
- public static readonly DirectProperty ItemsProperty =
- AvaloniaProperty.RegisterDirect(
- nameof(Items),
- o => o.Items,
- (o, v) => o.Items = v);
+ public static readonly StyledProperty ItemsSourceProperty =
+ AvaloniaProperty.Register(nameof(ItemsSource));
///
/// Gets or sets a collection that is used to generate the content of the control.
///
- public IEnumerable Items
+ public IEnumerable ItemsSource
{
- get { return _items; }
- set { SetAndRaise(ItemsProperty, ref _items, value); }
+ get => GetValue(ItemsSourceProperty);
+ set => SetValue(ItemsSourceProperty, value);
}
public static readonly StyledProperty AreRowDetailsFrozenProperty =
@@ -713,7 +708,7 @@ namespace Avalonia.Controls
HorizontalScrollBarVisibilityProperty,
VerticalScrollBarVisibilityProperty);
- ItemsProperty.Changed.AddClassHandler((x, e) => x.OnItemsPropertyChanged(e));
+ ItemsSourceProperty.Changed.AddClassHandler((x, e) => x.OnItemsSourcePropertyChanged(e));
CanUserResizeColumnsProperty.Changed.AddClassHandler((x, e) => x.OnCanUserResizeColumnsChanged(e));
ColumnWidthProperty.Changed.AddClassHandler((x, e) => x.OnColumnWidthChanged(e));
FrozenColumnCountProperty.Changed.AddClassHandler((x, e) => x.OnFrozenColumnCountChanged(e));
@@ -734,6 +729,8 @@ namespace Avalonia.Controls
RowDetailsTemplateProperty.Changed.AddClassHandler((x, e) => x.OnRowDetailsTemplateChanged(e));
RowDetailsVisibilityModeProperty.Changed.AddClassHandler((x, e) => x.OnRowDetailsVisibilityModeChanged(e));
AutoGenerateColumnsProperty.Changed.AddClassHandler((x, e) => x.OnAutoGenerateColumnsChanged(e));
+
+ FocusableProperty.OverrideDefaultValue(true);
}
///
@@ -816,10 +813,10 @@ namespace Avalonia.Controls
}
///
- /// ItemsProperty property changed handler.
+ /// ItemsSourceProperty property changed handler.
///
/// The event arguments.
- private void OnItemsPropertyChanged(AvaloniaPropertyChangedEventArgs e)
+ private void OnItemsSourcePropertyChanged(AvaloniaPropertyChangedEventArgs e)
{
if (!_areHandlersSuspended)
{
@@ -830,7 +827,7 @@ namespace Avalonia.Controls
if (LoadingOrUnloadingRow)
{
- SetValueNoCallback(ItemsProperty, oldValue);
+ SetValueNoCallback(ItemsSourceProperty, oldValue);
throw DataGridError.DataGrid.CannotChangeItemsWhenLoadingRows();
}
@@ -1855,7 +1852,7 @@ namespace Avalonia.Controls
{
get
{
- if (CurrentSlot == -1 || Items == null || RowGroupHeadersTable.Contains(CurrentSlot))
+ if (CurrentSlot == -1 || ItemsSource == null || RowGroupHeadersTable.Contains(CurrentSlot))
{
return null;
}
@@ -2483,7 +2480,7 @@ namespace Avalonia.Controls
if (_hScrollBar != null)
{
- //_hScrollBar.IsTabStop = false;
+ _hScrollBar.IsTabStop = false;
_hScrollBar.Maximum = 0.0;
_hScrollBar.Orientation = Orientation.Horizontal;
_hScrollBar.IsVisible = false;
@@ -2499,7 +2496,7 @@ namespace Avalonia.Controls
if (_vScrollBar != null)
{
- //_vScrollBar.IsTabStop = false;
+ _vScrollBar.IsTabStop = false;
_vScrollBar.Maximum = 0.0;
_vScrollBar.Orientation = Orientation.Vertical;
_vScrollBar.IsVisible = false;
@@ -3739,7 +3736,7 @@ namespace Avalonia.Controls
if (sender is Control editingElement)
{
editingElement.LostFocus -= EditingElement_LostFocus;
- if (EditingRow != null && EditingColumnIndex != -1)
+ if (EditingRow != null && _editingColumnIndex != -1)
{
FocusEditingCell(true);
}
@@ -3962,6 +3959,7 @@ namespace Avalonia.Controls
bool focusLeftDataGrid = true;
bool dataGridWillReceiveRoutedEvent = true;
Visual focusedObject = FocusManager.Instance.Current as Visual;
+ DataGridColumn editingColumn = null;
while (focusedObject != null)
{
@@ -3974,22 +3972,29 @@ namespace Avalonia.Controls
// Walk up the visual tree. If we hit the root, try using the framework element's
// parent. We do this because Popups behave differently with respect to the visual tree,
// and it could have a parent even if the VisualTreeHelper doesn't find it.
- Visual parent = focusedObject.GetVisualParent();
+ var parent = focusedObject.Parent as Visual;
if (parent == null)
{
- if (focusedObject is Control element)
- {
- parent = element.VisualParent;
- if (parent != null)
- {
- dataGridWillReceiveRoutedEvent = false;
- }
- }
+ parent = focusedObject.GetVisualParent();
+ }
+ else
+ {
+ dataGridWillReceiveRoutedEvent = false;
}
focusedObject = parent;
}
- if (focusLeftDataGrid)
+ if (EditingRow != null && EditingColumnIndex != -1)
+ {
+ editingColumn = ColumnsItemsInternal[EditingColumnIndex];
+
+ if (focusLeftDataGrid && editingColumn is DataGridTemplateColumn)
+ {
+ dataGridWillReceiveRoutedEvent = false;
+ }
+ }
+
+ if (focusLeftDataGrid && !(editingColumn is DataGridTemplateColumn))
{
ContainsFocus = false;
if (EditingRow != null)
@@ -4036,18 +4041,22 @@ namespace Avalonia.Controls
return true;
}
- Debug.Assert(EditingRow != null);
+ var editingRow = EditingRow;
+ if (editingRow is null)
+ {
+ return true;
+ }
+
Debug.Assert(_editingColumnIndex >= 0);
Debug.Assert(_editingColumnIndex < ColumnsItemsInternal.Count);
Debug.Assert(_editingColumnIndex == CurrentColumnIndex);
- Debug.Assert(EditingRow != null && EditingRow.Slot == CurrentSlot);
// Cache these to see if they change later
int currentSlot = CurrentSlot;
int currentColumnIndex = CurrentColumnIndex;
// We're ready to start ending, so raise the event
- DataGridCell editingCell = EditingRow.Cells[_editingColumnIndex];
+ DataGridCell editingCell = editingRow.Cells[_editingColumnIndex];
var editingElement = editingCell.Content as Control;
if (editingElement == null)
{
@@ -4055,7 +4064,7 @@ namespace Avalonia.Controls
}
if (raiseEvents)
{
- DataGridCellEditEndingEventArgs e = new DataGridCellEditEndingEventArgs(CurrentColumn, EditingRow, editingElement, editAction);
+ DataGridCellEditEndingEventArgs e = new DataGridCellEditEndingEventArgs(CurrentColumn, editingRow, editingElement, editAction);
OnCellEditEnding(e);
if (e.Cancel)
{
@@ -4109,7 +4118,7 @@ namespace Avalonia.Controls
}
else
{
- if (EditingRow != null)
+ if (editingRow != null)
{
if (editingCell.IsValid)
{
@@ -4117,10 +4126,10 @@ namespace Avalonia.Controls
editingCell.UpdatePseudoClasses();
}
- if (EditingRow.IsValid)
+ if (editingRow.IsValid)
{
- EditingRow.IsValid = false;
- EditingRow.UpdatePseudoClasses();
+ editingRow.IsValid = false;
+ editingRow.UpdatePseudoClasses();
}
}
@@ -4166,22 +4175,22 @@ namespace Avalonia.Controls
PopulateCellContent(
isCellEdited: !exitEditingMode,
dataGridColumn: CurrentColumn,
- dataGridRow: EditingRow,
+ dataGridRow: editingRow,
dataGridCell: editingCell);
- EditingRow.InvalidateDesiredHeight();
+ editingRow.InvalidateDesiredHeight();
var column = editingCell.OwningColumn;
if (column.Width.IsSizeToCells || column.Width.IsAuto)
{// Invalidate desired width and force recalculation
column.SetWidthDesiredValue(0);
- EditingRow.OwningGrid.AutoSizeColumn(column, editingCell.DesiredSize.Width);
+ editingRow.OwningGrid.AutoSizeColumn(column, editingCell.DesiredSize.Width);
}
}
// We're done, so raise the CellEditEnded event
if (raiseEvents)
{
- OnCellEditEnded(new DataGridCellEditEndedEventArgs(CurrentColumn, EditingRow, editAction));
+ OnCellEditEnded(new DataGridCellEditEndedEventArgs(CurrentColumn, editingRow, editAction));
}
// There's a chance that somebody reopened this cell for edit within the CellEditEnded handler,
@@ -4424,8 +4433,7 @@ namespace Avalonia.Controls
dataGridCell.Focus();
success = dataGridCell.ContainsFocusedElement();
}
- //TODO Check
- //success = dataGridCell.ContainsFocusedElement() ? true : dataGridCell.Focus();
+
_focusEditingControl = !success;
}
return success;
diff --git a/src/Avalonia.Controls.DataGrid/DataGridBoundColumn.cs b/src/Avalonia.Controls.DataGrid/DataGridBoundColumn.cs
index 110590fef2..61a1eb2bf0 100644
--- a/src/Avalonia.Controls.DataGrid/DataGridBoundColumn.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGridBoundColumn.cs
@@ -25,7 +25,7 @@ namespace Avalonia.Controls
///
//TODO Binding
[AssignBinding]
- [InheritDataTypeFromItems(nameof(DataGrid.Items), AncestorType = typeof(DataGrid))]
+ [InheritDataTypeFromItems(nameof(DataGrid.ItemsSource), AncestorType = typeof(DataGrid))]
public virtual IBinding Binding
{
get
diff --git a/src/Avalonia.Controls.DataGrid/DataGridCell.cs b/src/Avalonia.Controls.DataGrid/DataGridCell.cs
index dd802678d4..599bea056b 100644
--- a/src/Avalonia.Controls.DataGrid/DataGridCell.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGridCell.cs
@@ -33,6 +33,8 @@ namespace Avalonia.Controls
{
PointerPressedEvent.AddClassHandler(
(x,e) => x.DataGridCell_PointerPressed(e), handledEventsToo: true);
+ FocusableProperty.OverrideDefaultValue(true);
+ IsTabStopProperty.OverrideDefaultValue(false);
}
public DataGridCell()
{ }
@@ -169,8 +171,7 @@ namespace Avalonia.Controls
OwningGrid.OnCellPointerPressed(new DataGridCellPointerPressedEventArgs(this, OwningRow, OwningColumn, e));
if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
{
- if (!e.Handled)
- //if (!e.Handled && OwningGrid.IsTabStop)
+ if (!e.Handled && OwningGrid.IsTabStop)
{
OwningGrid.Focus();
}
@@ -190,8 +191,7 @@ namespace Avalonia.Controls
}
else if (e.GetCurrentPoint(this).Properties.IsRightButtonPressed)
{
- if (!e.Handled)
- //if (!e.Handled && OwningGrid.IsTabStop)
+ if (!e.Handled && OwningGrid.IsTabStop)
{
OwningGrid.Focus();
}
diff --git a/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs b/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs
index 5250f80f77..ef1e84c745 100644
--- a/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs
@@ -72,6 +72,7 @@ namespace Avalonia.Controls
{
AreSeparatorsVisibleProperty.Changed.AddClassHandler((x, e) => x.OnAreSeparatorsVisibleChanged(e));
PressedMixin.Attach();
+ IsTabStopProperty.OverrideDefaultValue(false);
}
///
diff --git a/src/Avalonia.Controls.DataGrid/DataGridColumns.cs b/src/Avalonia.Controls.DataGrid/DataGridColumns.cs
index 703bc0d9c3..4056b78bfe 100644
--- a/src/Avalonia.Controls.DataGrid/DataGridColumns.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGridColumns.cs
@@ -12,6 +12,7 @@ using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.Reflection;
+using Avalonia.Layout;
namespace Avalonia.Controls
{
@@ -489,7 +490,7 @@ namespace Avalonia.Controls
{
DataGridFillerColumn fillerColumn = ColumnsInternal.FillerColumn;
double totalColumnsWidth = ColumnsInternal.VisibleEdgedColumnsWidth;
- if (finalWidth > totalColumnsWidth)
+ if (finalWidth - totalColumnsWidth > LayoutHelper.LayoutEpsilon)
{
fillerColumn.FillerWidth = finalWidth - totalColumnsWidth;
}
@@ -971,6 +972,12 @@ namespace Avalonia.Controls
{
cx += _negHorizontalOffset;
_horizontalOffset -= _negHorizontalOffset;
+ if (_horizontalOffset < LayoutHelper.LayoutEpsilon)
+ {
+ // Snap to zero to avoid trying to partially scroll in first scrolled off column below
+ _horizontalOffset = 0;
+ }
+
_negHorizontalOffset = 0;
}
else
@@ -979,6 +986,11 @@ namespace Avalonia.Controls
_negHorizontalOffset -= displayWidth - cx;
cx = displayWidth;
}
+
+ // Make sure the HorizontalAdjustment is not greater than the new HorizontalOffset
+ // since it would cause an assertion failure in DataGridCellsPresenter.ShouldDisplayCell
+ // called by DataGridCellsPresenter.MeasureOverride.
+ HorizontalAdjustment = Math.Min(HorizontalAdjustment, _horizontalOffset);
}
// second try to scroll entire columns
if (cx < displayWidth && _horizontalOffset > 0)
diff --git a/src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs b/src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs
index ae52e5f970..ee9cc04420 100644
--- a/src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs
@@ -122,9 +122,9 @@ namespace Avalonia.Controls
// We need to use the raw ItemsSource as opposed to DataSource because DataSource
// may be the ItemsSource wrapped in a collection view, in which case we wouldn't
// be able to take T to be the type if we're given IEnumerable
- if (_dataType == null && _owner.Items != null)
+ if (_dataType == null && _owner.ItemsSource != null)
{
- _dataType = _owner.Items.GetItemType();
+ _dataType = _owner.ItemsSource.GetItemType();
}
return _dataType;
}
diff --git a/src/Avalonia.Controls.DataGrid/DataGridRow.cs b/src/Avalonia.Controls.DataGrid/DataGridRow.cs
index ea9b2fe972..dfda7d6e4f 100644
--- a/src/Avalonia.Controls.DataGrid/DataGridRow.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGridRow.cs
@@ -128,6 +128,7 @@ namespace Avalonia.Controls
DetailsTemplateProperty.Changed.AddClassHandler((x, e) => x.OnDetailsTemplateChanged(e));
AreDetailsVisibleProperty.Changed.AddClassHandler((x, e) => x.OnAreDetailsVisibleChanged(e));
PointerPressedEvent.AddClassHandler((x, e) => x.DataGridRow_PointerPressed(e), handledEventsToo: true);
+ IsTabStopProperty.OverrideDefaultValue(false);
}
///
diff --git a/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs b/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs
index 10efded58a..e51c2526b1 100644
--- a/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs
@@ -106,6 +106,7 @@ namespace Avalonia.Controls
{
SublevelIndentProperty.Changed.AddClassHandler((x,e) => x.OnSublevelIndentChanged(e));
PressedMixin.Attach();
+ IsTabStopProperty.OverrideDefaultValue(false);
}
///
@@ -301,8 +302,7 @@ namespace Avalonia.Controls
}
else
{
- //if (!e.Handled && OwningGrid.IsTabStop)
- if (!e.Handled)
+ if (!e.Handled && OwningGrid.IsTabStop)
{
OwningGrid.Focus();
}
diff --git a/src/Avalonia.Controls.DataGrid/DataGridRows.cs b/src/Avalonia.Controls.DataGrid/DataGridRows.cs
index 00e035270c..44079d24d0 100644
--- a/src/Avalonia.Controls.DataGrid/DataGridRows.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGridRows.cs
@@ -1589,6 +1589,23 @@ namespace Avalonia.Controls
CorrectSlotsAfterDeletion(slot, isRow);
OnRemovedElement(slot, item);
+
+ // Synchronize CurrentCellCoordinates, CurrentColumn, CurrentColumnIndex, CurrentItem
+ // and CurrentSlot with the currently edited cell, since OnRemovingElement called
+ // SetCurrentCellCore(-1, -1) to temporarily reset the current cell.
+ if (_temporarilyResetCurrentCell &&
+ _editingColumnIndex != -1 &&
+ _previousCurrentItem != null &&
+ EditingRow != null &&
+ EditingRow.Slot != -1)
+ {
+ ProcessSelectionAndCurrency(
+ columnIndex: _editingColumnIndex,
+ item: _previousCurrentItem,
+ backupSlot: this.EditingRow.Slot,
+ action: DataGridSelectionAction.None,
+ scrollIntoView: false);
+ }
}
private void RemoveNonDisplayedRows(int newFirstDisplayedSlot, int newLastDisplayedSlot)
diff --git a/src/Avalonia.Controls.DataGrid/DataGridTemplateColumn.cs b/src/Avalonia.Controls.DataGrid/DataGridTemplateColumn.cs
index 00318e2dd8..0bfb4b6913 100644
--- a/src/Avalonia.Controls.DataGrid/DataGridTemplateColumn.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGridTemplateColumn.cs
@@ -24,7 +24,7 @@ namespace Avalonia.Controls
(o, v) => o.CellTemplate = v);
[Content]
- [InheritDataTypeFromItems(nameof(DataGrid.Items), AncestorType = typeof(DataGrid))]
+ [InheritDataTypeFromItems(nameof(DataGrid.ItemsSource), AncestorType = typeof(DataGrid))]
public IDataTemplate CellTemplate
{
get { return _cellTemplate; }
@@ -51,7 +51,7 @@ namespace Avalonia.Controls
///
/// If this property is the column is read-only.
///
- [InheritDataTypeFromItems(nameof(DataGrid.Items), AncestorType = typeof(DataGrid))]
+ [InheritDataTypeFromItems(nameof(DataGrid.ItemsSource), AncestorType = typeof(DataGrid))]
public IDataTemplate CellEditingTemplate
{
get => _cellEditingCellTemplate;
diff --git a/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml b/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml
index e4642c1453..082eac60be 100644
--- a/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml
+++ b/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml
@@ -82,7 +82,6 @@
-
-
@@ -268,7 +266,6 @@
-
@@ -310,7 +307,6 @@
-
@@ -408,7 +404,6 @@
-
@@ -433,7 +428,7 @@
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
CornerRadius="{TemplateBinding CornerRadius}"
- Focusable="False"
+ IsTabStop="False"
Foreground="{TemplateBinding Foreground}" />
+
-
True if the currently focused element is within the visual tree of the parent
internal static bool ContainsFocusedElement(this Visual element)
{
- return (element == null) ? false : element.ContainsChild(FocusManager.Instance.Current as Visual);
+ return element is InputElement { IsKeyboardFocusWithin: true };
}
}
}
diff --git a/src/Avalonia.Controls.ItemsRepeater/Controls/ItemsRepeater.cs b/src/Avalonia.Controls.ItemsRepeater/Controls/ItemsRepeater.cs
index 499904deac..5fe9c6fa05 100644
--- a/src/Avalonia.Controls.ItemsRepeater/Controls/ItemsRepeater.cs
+++ b/src/Avalonia.Controls.ItemsRepeater/Controls/ItemsRepeater.cs
@@ -36,13 +36,13 @@ namespace Avalonia.Controls
ItemsControl.ItemTemplateProperty.AddOwner();
///
- /// Defines the property.
+ /// Defines the property.
///
- public static readonly DirectProperty ItemsProperty =
+ public static readonly DirectProperty ItemsSourceProperty =
AvaloniaProperty.RegisterDirect(
- nameof(Items),
- o => o.Items,
- (o, v) => o.Items = v);
+ nameof(ItemsSource),
+ o => o.ItemsSource,
+ (o, v) => o.ItemsSource = v);
///
/// Defines the property.
@@ -65,7 +65,7 @@ namespace Avalonia.Controls
private readonly ViewManager _viewManager;
private readonly ViewportManager _viewportManager;
private readonly TargetWeakEventSubscriber _layoutWeakSubscriber;
- private IEnumerable? _items;
+ private IEnumerable? _itemsSource;
private RepeaterLayoutContext? _layoutContext;
private EventHandler? _childIndexChanged;
private bool _isLayoutInProgress;
@@ -116,16 +116,16 @@ namespace Avalonia.Controls
///
/// Gets or sets an object source used to generate the content of the ItemsRepeater.
///
- public IEnumerable? Items
+ public IEnumerable? ItemsSource
{
- get => _items;
- set => SetAndRaise(ItemsProperty, ref _items, value);
+ get => _itemsSource;
+ set => SetAndRaise(ItemsSourceProperty, ref _itemsSource, value);
}
///
/// Gets or sets the template used to display each item.
///
- [InheritDataTypeFromItems(nameof(Items))]
+ [InheritDataTypeFromItems(nameof(ItemsSource))]
public IDataTemplate? ItemTemplate
{
get => GetValue(ItemTemplateProperty);
@@ -415,7 +415,7 @@ namespace Avalonia.Controls
///
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
- if (change.Property == ItemsProperty)
+ if (change.Property == ItemsSourceProperty)
{
var (oldEnumerable, newEnumerable) = change.GetOldAndNewValue();
diff --git a/src/Avalonia.Controls/AppBuilder.cs b/src/Avalonia.Controls/AppBuilder.cs
index 64bf92b7cd..77cc9d4dcb 100644
--- a/src/Avalonia.Controls/AppBuilder.cs
+++ b/src/Avalonia.Controls/AppBuilder.cs
@@ -118,6 +118,43 @@ namespace Avalonia
};
}
+ ///
+ /// Begin configuring an .
+ /// Should only be used for testing and design purposes, as it relies on dynamic code.
+ ///
+ ///
+ /// Parameter from which should be created.
+ /// It either needs to have BuildAvaloniaApp -> AppBuilder method or inherit Application.
+ ///
+ /// An instance. If can't be created, thrown an exception.
+ internal static AppBuilder Configure(
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
+ Type entryPointType)
+ {
+ var appBuilderObj = entryPointType
+ .GetMethod(
+ "BuildAvaloniaApp",
+ BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy,
+ null,
+ Array.Empty(),
+ null)?
+ .Invoke(null, Array.Empty());
+
+ if (appBuilderObj is AppBuilder appBuilder)
+ {
+ return appBuilder;
+ }
+
+ if (typeof(Application).IsAssignableFrom(entryPointType))
+ {
+ return Configure(() => (Application)Activator.CreateInstance(entryPointType)!);
+ }
+
+ throw new InvalidOperationException(
+ $"Unable to create AppBuilder from type {entryPointType.Name}." +
+ $"Input type either needs to have BuildAvaloniaApp -> AppBuilder method or inherit Application type.");
+ }
+
protected AppBuilder Self => this;
public AppBuilder AfterSetup(Action callback)
@@ -206,7 +243,7 @@ namespace Avalonia
_optionsInitializers += () => { AvaloniaLocator.CurrentMutable.Bind().ToFunc(options); };
return Self;
}
-
+
///
/// Registers an action that is executed with the current font manager.
///
@@ -251,17 +288,26 @@ namespace Avalonia
}
s_setupWasAlreadyCalled = true;
+ SetupUnsafe();
+ }
+
+ ///
+ /// Setup method that doesn't check for input initalizers being set.
+ /// Nor
+ ///
+ internal void SetupUnsafe()
+ {
_optionsInitializers?.Invoke();
- RuntimePlatformServicesInitializer();
- RenderingSubsystemInitializer();
- WindowingSubsystemInitializer();
- AfterPlatformServicesSetupCallback(Self);
- Instance = _appFactory();
+ RuntimePlatformServicesInitializer?.Invoke();
+ RenderingSubsystemInitializer?.Invoke();
+ WindowingSubsystemInitializer?.Invoke();
+ AfterPlatformServicesSetupCallback?.Invoke(Self);
+ Instance = _appFactory!();
Instance.ApplicationLifetime = _lifetime;
AvaloniaLocator.CurrentMutable.BindToSelf(Instance);
Instance.RegisterServices();
Instance.Initialize();
- AfterSetupCallback(Self);
+ AfterSetupCallback?.Invoke(Self);
Instance.OnFrameworkInitializationCompleted();
}
}
diff --git a/src/Avalonia.Controls/Application.cs b/src/Avalonia.Controls/Application.cs
index be3d5424fb..e907fd5988 100644
--- a/src/Avalonia.Controls/Application.cs
+++ b/src/Avalonia.Controls/Application.cs
@@ -94,6 +94,8 @@ namespace Avalonia
}
///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1031", Justification = "This property is supposed to be a styled readonly property.")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1030", Justification = "False positive.")]
public ThemeVariant ActualThemeVariant => GetValue(ActualThemeVariantProperty);
///
diff --git a/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs
index 9cc36e6a81..77f3b93efa 100644
--- a/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs
+++ b/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
+using Avalonia.Collections;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Interactivity;
@@ -16,7 +17,7 @@ namespace Avalonia.Controls.ApplicationLifetimes
private int _exitCode;
private CancellationTokenSource? _cts;
private bool _isShuttingDown;
- private readonly HashSet _windows = new();
+ private readonly AvaloniaList _windows = new();
private static ClassicDesktopStyleApplicationLifetime? s_activeLifetime;
@@ -35,7 +36,11 @@ namespace Avalonia.Controls.ApplicationLifetimes
private static void OnWindowOpened(object? sender, RoutedEventArgs e)
{
- s_activeLifetime?._windows.Add((Window)sender!);
+ var window = (Window)sender!;
+ if (s_activeLifetime is not null && !s_activeLifetime._windows.Contains(window))
+ {
+ s_activeLifetime._windows.Add(window);
+ }
}
public ClassicDesktopStyleApplicationLifetime()
@@ -67,7 +72,7 @@ namespace Avalonia.Controls.ApplicationLifetimes
public Window? MainWindow { get; set; }
///
- public IReadOnlyList Windows => _windows.ToArray();
+ public IReadOnlyList Windows => _windows;
private void HandleWindowClosed(Window? window)
{
@@ -161,7 +166,7 @@ namespace Avalonia.Controls.ApplicationLifetimes
// When an OS shutdown request is received, try to close all non-owned windows. Windows can cancel
// shutdown by setting e.Cancel = true in the Closing event. Owned windows will be shutdown by their
// owners.
- foreach (var w in Windows)
+ foreach (var w in Windows.ToArray())
{
if (w.Owner is null)
{
diff --git a/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs
index b38151854b..47a0c531ba 100644
--- a/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs
+++ b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs
@@ -143,12 +143,12 @@ namespace Avalonia.Controls
nameof(TextSelector));
///
- /// Identifies the property.
+ /// Identifies the property.
///
- /// The identifier for the property.
- public static readonly StyledProperty ItemsProperty =
+ /// The identifier for the property.
+ public static readonly StyledProperty ItemsSourceProperty =
AvaloniaProperty.Register(
- nameof(Items));
+ nameof(ItemsSource));
///
/// Identifies the property.
@@ -311,10 +311,10 @@ namespace Avalonia.Controls
///
/// Gets the text that is used to filter items in the
- /// item collection.
+ /// item collection.
///
/// The text that is used to filter items in the
- /// item collection.
+ /// item collection.
///
/// The SearchText value is typically the same as the
/// Text property, but is set after the TextChanged event occurs
@@ -339,7 +339,7 @@ namespace Avalonia.Controls
///
/// Gets or sets how the text in the text box is used to filter items
- /// specified by the
+ /// specified by the
/// property for display in the drop-down.
///
/// One of the
@@ -366,11 +366,11 @@ namespace Avalonia.Controls
///
/// Gets or sets the custom method that uses user-entered text to filter
- /// the items specified by the
+ /// the items specified by the
/// property for display in the drop-down.
///
/// The custom method that uses the user-entered text to filter
- /// the items specified by the
+ /// the items specified by the
/// property. The default is null.
///
/// The filter mode is automatically set to Custom if you set the
@@ -384,11 +384,11 @@ namespace Avalonia.Controls
///
/// Gets or sets the custom method that uses the user-entered text to
- /// filter items specified by the
+ /// filter items specified by the
/// property in a text-based way for display in the drop-down.
///
/// The custom method that uses the user-entered text to filter
- /// items specified by the
+ /// items specified by the
/// property in a text-based way for display in the drop-down.
///
/// The search mode is automatically set to Custom if you set the
@@ -402,11 +402,11 @@ namespace Avalonia.Controls
///
/// Gets or sets the custom method that combines the user-entered
- /// text and one of the items specified by the .
+ /// text and one of the items specified by the .
///
///
/// The custom method that combines the user-entered
- /// text and one of the items specified by the .
+ /// text and one of the items specified by the .
///
public AutoCompleteSelector? ItemSelector
{
@@ -417,11 +417,11 @@ namespace Avalonia.Controls
///
/// Gets or sets the custom method that combines the user-entered
/// text and one of the items specified by the
- /// in a text-based way.
+ /// in a text-based way.
///
///
/// The custom method that combines the user-entered
- /// text and one of the items specified by the
+ /// text and one of the items specified by the
/// in a text-based way.
///
public AutoCompleteSelector? TextSelector
@@ -442,10 +442,10 @@ namespace Avalonia.Controls
///
/// The collection that is used to generate the items of the
/// drop-down portion of the control.
- public IEnumerable? Items
+ public IEnumerable? ItemsSource
{
- get => GetValue(ItemsProperty);
- set => SetValue(ItemsProperty, value);
+ get => GetValue(ItemsSourceProperty);
+ set => SetValue(ItemsSourceProperty, value);
}
}
}
diff --git a/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs
index 72a23144cf..20711eecbc 100644
--- a/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs
+++ b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs
@@ -419,9 +419,9 @@ namespace Avalonia.Controls
/// ItemsSourceProperty property changed handler.
///
/// Event arguments.
- private void OnItemsPropertyChanged(AvaloniaPropertyChangedEventArgs e)
+ private void OnItemsSourcePropertyChanged(AvaloniaPropertyChangedEventArgs e)
{
- OnItemsChanged((IEnumerable?)e.NewValue);
+ OnItemsSourceChanged((IEnumerable?)e.NewValue);
}
private void OnItemTemplatePropertyChanged(AvaloniaPropertyChangedEventArgs e)
@@ -461,7 +461,7 @@ namespace Avalonia.Controls
SearchTextProperty.Changed.AddClassHandler((x,e) => x.OnSearchTextPropertyChanged(e));
FilterModeProperty.Changed.AddClassHandler((x,e) => x.OnFilterModePropertyChanged(e));
ItemFilterProperty.Changed.AddClassHandler((x,e) => x.OnItemFilterPropertyChanged(e));
- ItemsProperty.Changed.AddClassHandler((x,e) => x.OnItemsPropertyChanged(e));
+ ItemsSourceProperty.Changed.AddClassHandler((x,e) => x.OnItemsSourcePropertyChanged(e));
ItemTemplateProperty.Changed.AddClassHandler((x,e) => x.OnItemTemplatePropertyChanged(e));
IsEnabledProperty.Changed.AddClassHandler((x,e) => x.OnControlIsEnabledChanged(e));
}
@@ -559,7 +559,7 @@ namespace Avalonia.Controls
_adapter.Commit -= OnAdapterSelectionComplete;
_adapter.Cancel -= OnAdapterSelectionCanceled;
_adapter.Cancel -= OnAdapterSelectionComplete;
- _adapter.Items = null;
+ _adapter.ItemsSource = null;
}
_adapter = value;
@@ -570,7 +570,7 @@ namespace Avalonia.Controls
_adapter.Commit += OnAdapterSelectionComplete;
_adapter.Cancel += OnAdapterSelectionCanceled;
_adapter.Cancel += OnAdapterSelectionComplete;
- _adapter.Items = _view;
+ _adapter.ItemsSource = _view;
}
}
}
@@ -1128,7 +1128,7 @@ namespace Avalonia.Controls
{
if (!cancellationToken.IsCancellationRequested)
{
- SetCurrentValue(ItemsProperty, resultList);
+ SetCurrentValue(ItemsSourceProperty, resultList);
PopulateComplete();
}
});
@@ -1475,7 +1475,7 @@ namespace Avalonia.Controls
/// adapter's ItemsSource to the view if appropriate.
///
/// The new enumerable reference.
- private void OnItemsChanged(IEnumerable? newValue)
+ private void OnItemsSourceChanged(IEnumerable? newValue)
{
// Remove handler for oldValue.CollectionChanged (if present)
_collectionChangeSubscription?.Dispose();
@@ -1492,9 +1492,9 @@ namespace Avalonia.Controls
// Clear and set the view on the selection adapter
ClearView();
- if (SelectionAdapter != null && SelectionAdapter.Items != _view)
+ if (SelectionAdapter != null && SelectionAdapter.ItemsSource != _view)
{
- SelectionAdapter.Items = _view;
+ SelectionAdapter.ItemsSource = _view;
}
if (IsDropDownOpen)
{
@@ -1545,9 +1545,9 @@ namespace Avalonia.Controls
{
// Significant changes to the underlying data.
ClearView();
- if (Items != null)
+ if (ItemsSource != null)
{
- _items = new List(Items.Cast());
+ _items = new List(ItemsSource.Cast());
}
}
@@ -1582,9 +1582,9 @@ namespace Avalonia.Controls
PopulatedEventArgs populated = new PopulatedEventArgs(new ReadOnlyCollection(_view!));
OnPopulated(populated);
- if (SelectionAdapter != null && SelectionAdapter.Items != _view)
+ if (SelectionAdapter != null && SelectionAdapter.ItemsSource != _view)
{
- SelectionAdapter.Items = _view;
+ SelectionAdapter.ItemsSource = _view;
}
bool isDropDownOpen = _userCalledPopulate && (_view!.Count > 0);
@@ -2042,6 +2042,8 @@ namespace Avalonia.Controls
///
/// Identifies the Value dependency property.
///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1002:AvaloniaProperty objects should not be owned by a generic type",
+ Justification = "This property is not supposed to be used from XAML.")]
public static readonly StyledProperty ValueProperty =
AvaloniaProperty.Register, T>(nameof(Value));
diff --git a/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteFilterMode.cs b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteFilterMode.cs
index c17f5a19ab..fa956a79fe 100644
--- a/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteFilterMode.cs
+++ b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteFilterMode.cs
@@ -9,7 +9,7 @@ namespace Avalonia.Controls
{
///
/// Specifies how text in the text box portion of the
- /// control is used to filter items specified by the
+ /// control is used to filter items specified by the
/// property for display in the drop-down.
///
public enum AutoCompleteFilterMode
diff --git a/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs
index e8fb6b75ad..d04dfec3e8 100644
--- a/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs
+++ b/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs
@@ -88,10 +88,10 @@ namespace Avalonia.Automation.Peers
if (string.IsNullOrWhiteSpace(result) && GetLabeledBy() is AutomationPeer labeledBy)
{
- return labeledBy.GetName();
+ result = labeledBy.GetName();
}
- return null;
+ return result;
}
protected override AutomationPeer? GetParentCore()
diff --git a/src/Avalonia.Controls/Automation/Peers/ThumbAutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/ThumbAutomationPeer.cs
new file mode 100644
index 0000000000..1566370df0
--- /dev/null
+++ b/src/Avalonia.Controls/Automation/Peers/ThumbAutomationPeer.cs
@@ -0,0 +1,12 @@
+using Avalonia.Automation.Peers;
+using Avalonia.Controls.Primitives;
+
+namespace Avalonia.Controls.Automation.Peers
+{
+ public class ThumbAutomationPeer : ControlAutomationPeer
+ {
+ public ThumbAutomationPeer(Thumb owner) : base(owner) { }
+ protected override AutomationControlType GetAutomationControlTypeCore() => AutomationControlType.Thumb;
+ protected override bool IsContentElementCore() => false;
+ }
+}
diff --git a/src/Avalonia.Controls/Avalonia.Controls.csproj b/src/Avalonia.Controls/Avalonia.Controls.csproj
index 3195c38eef..48761ca8b8 100644
--- a/src/Avalonia.Controls/Avalonia.Controls.csproj
+++ b/src/Avalonia.Controls/Avalonia.Controls.csproj
@@ -15,6 +15,12 @@
+
+
+
+
+
+
diff --git a/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.cs b/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.cs
index c091d07632..90153d3293 100644
--- a/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.cs
+++ b/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.cs
@@ -875,10 +875,11 @@ namespace Avalonia.Controls
{
if (_textBox != null)
{
+ SetCurrentValue(TextProperty, String.Empty);
+
if (string.IsNullOrEmpty(Watermark) && !UseFloatingWatermark)
{
DateTimeFormatInfo dtfi = DateTimeHelper.GetCurrentDateFormat();
- SetCurrentValue(TextProperty, string.Empty);
_defaultText = string.Empty;
var watermarkFormat = "<{0}>";
string watermarkText;
diff --git a/src/Avalonia.Controls/Chrome/CaptionButtons.cs b/src/Avalonia.Controls/Chrome/CaptionButtons.cs
index f5fcbed9fb..237fa87968 100644
--- a/src/Avalonia.Controls/Chrome/CaptionButtons.cs
+++ b/src/Avalonia.Controls/Chrome/CaptionButtons.cs
@@ -8,13 +8,19 @@ namespace Avalonia.Controls.Chrome
///
/// Draws window minimize / maximize / close buttons in a when managed client decorations are enabled.
///
- [TemplatePart("PART_CloseButton", typeof(Button))]
- [TemplatePart("PART_RestoreButton", typeof(Button))]
- [TemplatePart("PART_MinimiseButton", typeof(Button))]
- [TemplatePart("PART_FullScreenButton", typeof(Button))]
+ [TemplatePart(PART_CloseButton, typeof(Button))]
+ [TemplatePart(PART_RestoreButton, typeof(Button))]
+ [TemplatePart(PART_MinimizeButton, typeof(Button))]
+ [TemplatePart(PART_FullScreenButton, typeof(Button))]
[PseudoClasses(":minimized", ":normal", ":maximized", ":fullscreen")]
public class CaptionButtons : TemplatedControl
{
+ private const string PART_CloseButton = "PART_CloseButton";
+ private const string PART_RestoreButton = "PART_RestoreButton";
+ private const string PART_MinimizeButton = "PART_MinimizeButton";
+ private const string PART_FullScreenButton = "PART_FullScreenButton";
+
+ private Button? _restoreButton;
private IDisposable? _disposables;
///
@@ -28,14 +34,23 @@ namespace Avalonia.Controls.Chrome
{
HostWindow = hostWindow;
- _disposables = HostWindow.GetObservable(Window.WindowStateProperty)
- .Subscribe(x =>
- {
- PseudoClasses.Set(":minimized", x == WindowState.Minimized);
- PseudoClasses.Set(":normal", x == WindowState.Normal);
- PseudoClasses.Set(":maximized", x == WindowState.Maximized);
- PseudoClasses.Set(":fullscreen", x == WindowState.FullScreen);
- });
+ _disposables = new CompositeDisposable
+ {
+ HostWindow.GetObservable(Window.CanResizeProperty)
+ .Subscribe(x =>
+ {
+ if (_restoreButton is not null)
+ _restoreButton.IsEnabled = x;
+ }),
+ HostWindow.GetObservable(Window.WindowStateProperty)
+ .Subscribe(x =>
+ {
+ PseudoClasses.Set(":minimized", x == WindowState.Minimized);
+ PseudoClasses.Set(":normal", x == WindowState.Normal);
+ PseudoClasses.Set(":maximized", x == WindowState.Maximized);
+ PseudoClasses.Set(":fullscreen", x == WindowState.FullScreen);
+ }),
+ };
}
}
@@ -85,15 +100,18 @@ namespace Avalonia.Controls.Chrome
{
base.OnApplyTemplate(e);
- var closeButton = e.NameScope.Get
-
-
+
+
diff --git a/src/Avalonia.FreeDesktop/DBusPlatformSettings.cs b/src/Avalonia.FreeDesktop/DBusPlatformSettings.cs
index a25bb68458..c7552823d4 100644
--- a/src/Avalonia.FreeDesktop/DBusPlatformSettings.cs
+++ b/src/Avalonia.FreeDesktop/DBusPlatformSettings.cs
@@ -1,7 +1,8 @@
using System;
using System.Threading.Tasks;
-using Avalonia.Logging;
+using Avalonia.Media;
using Avalonia.Platform;
+using Tmds.DBus.Protocol;
using Tmds.DBus.SourceGenerator;
namespace Avalonia.FreeDesktop
@@ -9,7 +10,10 @@ namespace Avalonia.FreeDesktop
internal class DBusPlatformSettings : DefaultPlatformSettings
{
private readonly OrgFreedesktopPortalSettings? _settings;
+
private PlatformColorValues? _lastColorValues;
+ private PlatformThemeVariant? _themeVariant;
+ private Color? _accentColor;
public DBusPlatformSettings()
{
@@ -18,53 +22,88 @@ namespace Avalonia.FreeDesktop
_settings = new OrgFreedesktopPortalSettings(DBusHelper.Connection, "org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop");
_ = _settings.WatchSettingChangedAsync(SettingsChangedHandler);
- _ = TryGetInitialValueAsync();
+ _ = TryGetInitialValuesAsync();
}
- public override PlatformColorValues GetColorValues()
+ public override PlatformColorValues GetColorValues() => _lastColorValues ?? base.GetColorValues();
+
+ private async Task TryGetInitialValuesAsync()
{
- return _lastColorValues ?? base.GetColorValues();
+ _themeVariant = await TryGetThemeVariantAsync();
+ _accentColor = await TryGetAccentColorAsync();
+ _lastColorValues = BuildPlatformColorValues();
+ if (_lastColorValues is not null)
+ OnColorValuesChanged(_lastColorValues);
}
- private async Task TryGetInitialValueAsync()
+ private async Task TryGetThemeVariantAsync()
{
try
{
var value = await _settings!.ReadAsync("org.freedesktop.appearance", "color-scheme");
- _lastColorValues = GetColorValuesFromSetting(value);
- OnColorValuesChanged(_lastColorValues);
+ return ToColorScheme(((value.Value as DBusVariantItem)!.Value as DBusUInt32Item)!.Value);
+ }
+ catch (DBusException)
+ {
+ return null;
+ }
+ }
+
+ private async Task TryGetAccentColorAsync()
+ {
+ try
+ {
+ var value = await _settings!.ReadAsync("org.kde.kdeglobals.General", "AccentColor");
+ return ToAccentColor(((value.Value as DBusVariantItem)!.Value as DBusStringItem)!.Value);
}
- catch (Exception ex)
+ catch (DBusException)
{
- _lastColorValues = base.GetColorValues();
- Logger.TryGet(LogEventLevel.Error, LogArea.FreeDesktopPlatform)?.Log(this, "Unable to get setting value", ex);
+ return null;
}
}
- private void SettingsChangedHandler(Exception? exception, (string @namespace, string key, DBusVariantItem value) valueTuple)
+ private async void SettingsChangedHandler(Exception? exception, (string @namespace, string key, DBusVariantItem value) valueTuple)
{
if (exception is not null)
return;
- if (valueTuple is ("org.freedesktop.appearance", "color-scheme", { } value))
+ switch (valueTuple)
{
- /*
- 0: No preference
- 1: Prefer dark appearance
- 2: Prefer light appearance
- */
- _lastColorValues = GetColorValuesFromSetting(value);
- OnColorValuesChanged(_lastColorValues);
+ case ("org.freedesktop.appearance", "color-scheme", { } colorScheme):
+ _themeVariant = ToColorScheme((colorScheme.Value as DBusUInt32Item)!.Value);
+ _accentColor = await TryGetAccentColorAsync();
+ _lastColorValues = BuildPlatformColorValues();
+ OnColorValuesChanged(_lastColorValues!);
+ break;
}
}
- private static PlatformColorValues GetColorValuesFromSetting(DBusVariantItem value)
+ private PlatformColorValues? BuildPlatformColorValues()
{
- var isDark = ((value.Value as DBusVariantItem)!.Value as DBusUInt32Item)!.Value == 1;
- return new PlatformColorValues
- {
- ThemeVariant = isDark ? PlatformThemeVariant.Dark : PlatformThemeVariant.Light
- };
+ if (_themeVariant is { } themeVariant && _accentColor is { } accentColor)
+ return new PlatformColorValues { ThemeVariant = themeVariant, AccentColor1 = accentColor };
+ if (_themeVariant is { } themeVariant1)
+ return new PlatformColorValues { ThemeVariant = themeVariant1 };
+ if (_accentColor is { } accentColor1)
+ return new PlatformColorValues { AccentColor1 = accentColor1 };
+ return null;
+ }
+
+ private static PlatformThemeVariant ToColorScheme(uint value)
+ {
+ /*
+ 0: No preference
+ 1: Prefer dark appearance
+ 2: Prefer light appearance
+ */
+ var isDark = value == 1;
+ return isDark ? PlatformThemeVariant.Dark : PlatformThemeVariant.Light;
+ }
+
+ private static Color ToAccentColor(string value)
+ {
+ var rgb = value.Split(',');
+ return new Color(255, byte.Parse(rgb[0]), byte.Parse(rgb[1]), byte.Parse(rgb[2]));
}
}
}
diff --git a/src/Avalonia.FreeDesktop/DBusSystemDialog.cs b/src/Avalonia.FreeDesktop/DBusSystemDialog.cs
index 20583dd6ac..cd6f829d7a 100644
--- a/src/Avalonia.FreeDesktop/DBusSystemDialog.cs
+++ b/src/Avalonia.FreeDesktop/DBusSystemDialog.cs
@@ -21,7 +21,7 @@ namespace Avalonia.FreeDesktop
var dbusFileChooser = new OrgFreedesktopPortalFileChooser(DBusHelper.Connection, "org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop");
try
{
- await dbusFileChooser.GetVersionAsync();
+ await dbusFileChooser.GetVersionPropertyAsync();
}
catch
{
diff --git a/src/Avalonia.Headless/Avalonia.Headless.csproj b/src/Avalonia.Headless/Avalonia.Headless.csproj
deleted file mode 100644
index 95f7b79009..0000000000
--- a/src/Avalonia.Headless/Avalonia.Headless.csproj
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
- net6.0;netstandard2.0
-
-
-
-
-
-
-
-
-
-
diff --git a/src/Avalonia.Headless/HeadlessPlatformStubs.cs b/src/Avalonia.Headless/HeadlessPlatformStubs.cs
deleted file mode 100644
index ee4cd5af98..0000000000
--- a/src/Avalonia.Headless/HeadlessPlatformStubs.cs
+++ /dev/null
@@ -1,262 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Threading.Tasks;
-using Avalonia.Controls;
-using Avalonia.Controls.Platform;
-using Avalonia.Input;
-using Avalonia.Input.Platform;
-using Avalonia.Media;
-using Avalonia.Media.Fonts;
-using Avalonia.Media.TextFormatting;
-using Avalonia.Platform;
-using Avalonia.Platform.Storage;
-using Avalonia.Platform.Storage.FileIO;
-using Avalonia.Utilities;
-
-namespace Avalonia.Headless
-{
- class HeadlessClipboardStub : IClipboard
- {
- private string _text;
- private IDataObject _data;
-
- public Task GetTextAsync()
- {
- return Task.Run(() => _text);
- }
-
- public Task SetTextAsync(string text)
- {
- return Task.Run(() => _text = text);
- }
-
- public Task ClearAsync()
- {
- return Task.Run(() => _text = null);
- }
-
- public Task SetDataObjectAsync(IDataObject data)
- {
- return Task.Run(() => _data = data);
- }
-
- public Task GetFormatsAsync()
- {
- throw new NotImplementedException();
- }
-
- public async Task GetDataAsync(string format)
- {
- return await Task.Run(() => _data);
- }
- }
-
- class HeadlessCursorFactoryStub : ICursorFactory
- {
- public ICursorImpl GetCursor(StandardCursorType cursorType) => new CursorStub();
- public ICursorImpl CreateCursor(IBitmapImpl cursor, PixelPoint hotSpot) => new CursorStub();
-
- private class CursorStub : ICursorImpl
- {
- public void Dispose() { }
- }
- }
-
- class HeadlessGlyphTypefaceImpl : IGlyphTypeface
- {
- public FontMetrics Metrics => new FontMetrics
- {
- DesignEmHeight = 1,
- Ascent = 8,
- Descent = 4,
- LineGap = 0,
- UnderlinePosition = 2,
- UnderlineThickness = 1,
- StrikethroughPosition = 2,
- StrikethroughThickness = 1,
- IsFixedPitch = true
- };
-
- public int GlyphCount => 1337;
-
- public FontSimulations FontSimulations { get; }
-
- public string FamilyName => "Arial";
-
- public FontWeight Weight => FontWeight.Normal;
-
- public FontStyle Style => FontStyle.Normal;
-
- public FontStretch Stretch => FontStretch.Normal;
-
- public void Dispose()
- {
- }
-
- public ushort GetGlyph(uint codepoint)
- {
- return 1;
- }
-
- public bool TryGetGlyph(uint codepoint, out ushort glyph)
- {
- glyph = 1;
-
- return true;
- }
-
- public int GetGlyphAdvance(ushort glyph)
- {
- return 12;
- }
-
- public int[] GetGlyphAdvances(ReadOnlySpan glyphs)
- {
- return glyphs.ToArray().Select(x => (int)x).ToArray();
- }
-
- public ushort[] GetGlyphs(ReadOnlySpan codepoints)
- {
- return codepoints.ToArray().Select(x => (ushort)x).ToArray();
- }
-
- public bool TryGetTable(uint tag, out byte[] table)
- {
- table = null;
- return false;
- }
-
- public bool TryGetGlyphMetrics(ushort glyph, out GlyphMetrics metrics)
- {
- metrics = new GlyphMetrics
- {
- Height = 10,
- Width = 8
- };
-
- return true;
- }
- }
-
- class HeadlessTextShaperStub : ITextShaperImpl
- {
- public ShapedBuffer ShapeText(ReadOnlyMemory text, TextShaperOptions options)
- {
- var typeface = options.Typeface;
- var fontRenderingEmSize = options.FontRenderingEmSize;
- var bidiLevel = options.BidiLevel;
-
- return new ShapedBuffer(text, text.Length, typeface, fontRenderingEmSize, bidiLevel);
- }
- }
-
- class HeadlessFontManagerStub : IFontManagerImpl
- {
- public string GetDefaultFontFamilyName()
- {
- return "Arial";
- }
-
- public string[] GetInstalledFontFamilyNames(bool checkForUpdates = false)
- {
- return new string[] { "Arial" };
- }
-
- public bool TryCreateGlyphTypeface(string familyName, FontStyle style, FontWeight weight, FontStretch stretch, out IGlyphTypeface glyphTypeface)
- {
- glyphTypeface= new HeadlessGlyphTypefaceImpl();
-
- return true;
- }
-
- public bool TryCreateGlyphTypeface(Stream stream, out IGlyphTypeface glyphTypeface)
- {
- glyphTypeface = new HeadlessGlyphTypefaceImpl();
-
- return true;
- }
-
- public bool TryMatchCharacter(int codepoint, FontStyle fontStyle, FontWeight fontWeight, FontStretch fontStretch,
- FontFamily fontFamily, CultureInfo culture, out Typeface typeface)
- {
- typeface = new Typeface("Arial", fontStyle, fontWeight, fontStretch);
- return true;
- }
- }
-
- class HeadlessIconLoaderStub : IPlatformIconLoader
- {
-
- class IconStub : IWindowIconImpl
- {
- public void Save(Stream outputStream)
- {
-
- }
- }
- public IWindowIconImpl LoadIcon(string fileName)
- {
- return new IconStub();
- }
-
- public IWindowIconImpl LoadIcon(Stream stream)
- {
- return new IconStub();
- }
-
- public IWindowIconImpl LoadIcon(IBitmapImpl bitmap)
- {
- return new IconStub();
- }
- }
-
- class HeadlessScreensStub : IScreenImpl
- {
- public int ScreenCount { get; } = 1;
-
- public IReadOnlyList AllScreens { get; } = new[]
- {
- new Screen(1, new PixelRect(0, 0, 1920, 1280),
- new PixelRect(0, 0, 1920, 1280), true),
- };
-
- public Screen ScreenFromPoint(PixelPoint point)
- {
- return ScreenHelper.ScreenFromPoint(point, AllScreens);
- }
-
- public Screen ScreenFromRect(PixelRect rect)
- {
- return ScreenHelper.ScreenFromRect(rect, AllScreens);
- }
-
- public Screen ScreenFromWindow(IWindowBaseImpl window)
- {
- return ScreenHelper.ScreenFromWindow(window, AllScreens);
- }
- }
-
- internal class NoopStorageProvider : BclStorageProvider
- {
- public override bool CanOpen => false;
- public override Task> OpenFilePickerAsync(FilePickerOpenOptions options)
- {
- return Task.FromResult>(Array.Empty());
- }
-
- public override bool CanSave => false;
- public override Task SaveFilePickerAsync(FilePickerSaveOptions options)
- {
- return Task.FromResult(null);
- }
-
- public override bool CanPickFolder => false;
- public override Task> OpenFolderPickerAsync(FolderPickerOpenOptions options)
- {
- return Task.FromResult>(Array.Empty());
- }
- }
-}
diff --git a/src/Avalonia.Native/Avalonia.Native.csproj b/src/Avalonia.Native/Avalonia.Native.csproj
index 095662a538..e69c39a41e 100644
--- a/src/Avalonia.Native/Avalonia.Native.csproj
+++ b/src/Avalonia.Native/Avalonia.Native.csproj
@@ -26,8 +26,4 @@
-
-
-
-
diff --git a/src/Avalonia.Native/AvaloniaNativeDragSource.cs b/src/Avalonia.Native/AvaloniaNativeDragSource.cs
index 7f4c462ee0..5063c7a0a0 100644
--- a/src/Avalonia.Native/AvaloniaNativeDragSource.cs
+++ b/src/Avalonia.Native/AvaloniaNativeDragSource.cs
@@ -4,7 +4,6 @@ using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Input.Platform;
-using Avalonia.Interactivity;
using Avalonia.Native.Interop;
namespace Avalonia.Native
@@ -18,16 +17,6 @@ namespace Avalonia.Native
_factory = factory;
}
- private static TopLevel FindRoot(object? element)
- {
- while (element is Interactive interactive && element is not Visual)
- element = interactive.GetInteractiveParent();
- if (element == null)
- return null;
- var visual = (Visual)element;
- return TopLevel.GetTopLevel(visual);
- }
-
class DndCallback : NativeCallbackBase, IAvnDndResultCallback
{
private TaskCompletionSource _tcs;
@@ -46,7 +35,7 @@ namespace Avalonia.Native
public Task DoDragDrop(PointerEventArgs triggerEvent, IDataObject data, DragDropEffects allowedEffects)
{
// Sanity check
- var tl = FindRoot(triggerEvent.Source);
+ var tl = TopLevel.GetTopLevel(triggerEvent.Source as Visual);
var view = tl?.PlatformImpl as WindowBaseImpl;
if (view == null)
throw new ArgumentException();
diff --git a/src/Avalonia.Native/AvaloniaNativeTextInputMethod.cs b/src/Avalonia.Native/AvaloniaNativeTextInputMethod.cs
index 8438ef10b5..4c93c06ef3 100644
--- a/src/Avalonia.Native/AvaloniaNativeTextInputMethod.cs
+++ b/src/Avalonia.Native/AvaloniaNativeTextInputMethod.cs
@@ -98,7 +98,7 @@ namespace Avalonia.Native
var surroundingText = _client.SurroundingText;
_inputMethod.SetSurroundingText(
- surroundingText.Text,
+ surroundingText.Text ?? "",
surroundingText.AnchorOffset,
surroundingText.CursorOffset
);
diff --git a/src/Avalonia.Native/PopupImpl.cs b/src/Avalonia.Native/PopupImpl.cs
index 0953527284..6b7f7e8883 100644
--- a/src/Avalonia.Native/PopupImpl.cs
+++ b/src/Avalonia.Native/PopupImpl.cs
@@ -1,4 +1,5 @@
using System;
+using Avalonia.Controls;
using Avalonia.Controls.Primitives.PopupPositioning;
using Avalonia.Native.Interop;
using Avalonia.Platform;
@@ -29,7 +30,7 @@ namespace Avalonia.Native
private void MoveResize(PixelPoint position, Size size, double scaling)
{
Position = position;
- Resize(size, PlatformResizeReason.Layout);
+ Resize(size, WindowResizeReason.Layout);
//TODO: We ignore the scaling override for now
}
diff --git a/src/Avalonia.Native/WindowImpl.cs b/src/Avalonia.Native/WindowImpl.cs
index 5d0e6a2d18..817fe3d080 100644
--- a/src/Avalonia.Native/WindowImpl.cs
+++ b/src/Avalonia.Native/WindowImpl.cs
@@ -21,6 +21,7 @@ namespace Avalonia.Native
private DoubleClickHelper _doubleClickHelper;
private readonly ITopLevelNativeMenuExporter _nativeMenuExporter;
private readonly AvaloniaNativeTextInputMethod _inputMethod;
+ private bool _canResize = true;
internal WindowImpl(IAvaloniaNativeFactory factory, AvaloniaNativePlatformOptions opts,
AvaloniaNativeGlPlatformGraphics glFeature) : base(factory, opts, glFeature)
@@ -75,6 +76,7 @@ namespace Avalonia.Native
public void CanResize(bool value)
{
+ _canResize = value;
_native.SetCanResize(value.AsComBool());
}
@@ -137,14 +139,10 @@ namespace Avalonia.Native
{
if (_doubleClickHelper.IsDoubleClick(e.Timestamp, e.Position))
{
- // TOGGLE WINDOW STATE.
- if (WindowState == WindowState.Maximized || WindowState == WindowState.FullScreen)
+ if (_canResize)
{
- WindowState = WindowState.Normal;
- }
- else
- {
- WindowState = WindowState.Maximized;
+ WindowState = WindowState is WindowState.Maximized or WindowState.FullScreen ?
+ WindowState.Normal : WindowState.Maximized;
}
}
else
diff --git a/src/Avalonia.Native/WindowImplBase.cs b/src/Avalonia.Native/WindowImplBase.cs
index 0dff46057e..b802b1db71 100644
--- a/src/Avalonia.Native/WindowImplBase.cs
+++ b/src/Avalonia.Native/WindowImplBase.cs
@@ -95,7 +95,7 @@ namespace Avalonia.Native
var monitor = Screen.AllScreens.OrderBy(x => x.Scaling)
.FirstOrDefault(m => m.Bounds.Contains(Position));
- Resize(new Size(monitor.WorkingArea.Width * 0.75d, monitor.WorkingArea.Height * 0.7d), PlatformResizeReason.Layout);
+ Resize(new Size(monitor.WorkingArea.Width * 0.75d, monitor.WorkingArea.Height * 0.7d), WindowResizeReason.Layout);
}
public IAvnWindowBase Native => _native;
@@ -160,7 +160,7 @@ namespace Avalonia.Native
public Action LostFocus { get; set; }
public Action Paint { get; set; }
- public Action Resized { get; set; }
+ public Action Resized { get; set; }
public Action Closed { get; set; }
public IMouseDevice MouseDevice => _mouse;
public abstract IPopupImpl CreatePopup();
@@ -211,7 +211,7 @@ namespace Avalonia.Native
{
var s = new Size(size->Width, size->Height);
_parent._savedLogicalSize = s;
- _parent.Resized?.Invoke(s, (PlatformResizeReason)reason);
+ _parent.Resized?.Invoke(s, (WindowResizeReason)reason);
}
}
@@ -220,17 +220,17 @@ namespace Avalonia.Native
_parent.PositionChanged?.Invoke(position.ToAvaloniaPixelPoint());
}
- void IAvnWindowBaseEvents.RawMouseEvent(AvnRawMouseEventType type, uint timeStamp, AvnInputModifiers modifiers, AvnPoint point, AvnVector delta)
+ void IAvnWindowBaseEvents.RawMouseEvent(AvnRawMouseEventType type, ulong timeStamp, AvnInputModifiers modifiers, AvnPoint point, AvnVector delta)
{
_parent.RawMouseEvent(type, timeStamp, modifiers, point, delta);
}
- int IAvnWindowBaseEvents.RawKeyEvent(AvnRawKeyEventType type, uint timeStamp, AvnInputModifiers modifiers, uint key)
+ int IAvnWindowBaseEvents.RawKeyEvent(AvnRawKeyEventType type, ulong timeStamp, AvnInputModifiers modifiers, uint key)
{
return _parent.RawKeyEvent(type, timeStamp, modifiers, key).AsComBool();
}
- int IAvnWindowBaseEvents.RawTextInputEvent(uint timeStamp, string text)
+ int IAvnWindowBaseEvents.RawTextInputEvent(ulong timeStamp, string text)
{
return _parent.RawTextInputEvent(timeStamp, text).AsComBool();
}
@@ -286,7 +286,7 @@ namespace Avalonia.Native
_native?.Activate();
}
- public bool RawTextInputEvent(uint timeStamp, string text)
+ public bool RawTextInputEvent(ulong timeStamp, string text)
{
if (_inputRoot is null)
return false;
@@ -300,7 +300,7 @@ namespace Avalonia.Native
return args.Handled;
}
- public bool RawKeyEvent(AvnRawKeyEventType type, uint timeStamp, AvnInputModifiers modifiers, uint key)
+ public bool RawKeyEvent(AvnRawKeyEventType type, ulong timeStamp, AvnInputModifiers modifiers, uint key)
{
if (_inputRoot is null)
return false;
@@ -319,7 +319,7 @@ namespace Avalonia.Native
return false;
}
- public void RawMouseEvent(AvnRawMouseEventType type, uint timeStamp, AvnInputModifiers modifiers, AvnPoint point, AvnVector delta)
+ public void RawMouseEvent(AvnRawMouseEventType type, ulong timeStamp, AvnInputModifiers modifiers, AvnPoint point, AvnVector delta)
{
if (_inputRoot is null)
return;
@@ -360,7 +360,7 @@ namespace Avalonia.Native
}
}
- public void Resize(Size clientSize, PlatformResizeReason reason)
+ public void Resize(Size clientSize, WindowResizeReason reason)
{
_native?.Resize(clientSize.Width, clientSize.Height, (AvnPlatformResizeReason)reason);
}
diff --git a/src/Avalonia.Native/avn.idl b/src/Avalonia.Native/avn.idl
index 09e9168d8f..a58a00d59d 100644
--- a/src/Avalonia.Native/avn.idl
+++ b/src/Avalonia.Native/avn.idl
@@ -1,6 +1,7 @@
@clr-namespace Avalonia.Native.Interop
@clr-access internal
@clr-map bool int
+@clr-map u_int64_t ulong
@cpp-preamble @@
#pragma once
#include "com.h"
@@ -583,12 +584,12 @@ interface IAvnWindowBaseEvents : IUnknown
void Resized([const] AvnSize& size, AvnPlatformResizeReason reason);
void PositionChanged(AvnPoint position);
void RawMouseEvent(AvnRawMouseEventType type,
- uint timeStamp,
+ u_int64_t timeStamp,
AvnInputModifiers modifiers,
AvnPoint point,
AvnVector delta);
- bool RawKeyEvent(AvnRawKeyEventType type, uint timeStamp, AvnInputModifiers modifiers, uint key);
- bool RawTextInputEvent(uint timeStamp, [const] char* text);
+ bool RawKeyEvent(AvnRawKeyEventType type, u_int64_t timeStamp, AvnInputModifiers modifiers, uint key);
+ bool RawTextInputEvent(u_int64_t timeStamp, [const] char* text);
void ScalingChanged(double scaling);
void RunRenderPriorityJobs();
void LostFocus();
diff --git a/src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs b/src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs
index 7e73397743..83bf795b03 100644
--- a/src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs
+++ b/src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs
@@ -8,6 +8,8 @@ using Avalonia.Rendering;
using Avalonia.Rendering.Composition;
using Avalonia.VisualTree;
using Avalonia.Platform;
+using System.ComponentModel;
+
namespace Avalonia.OpenGL.Controls
{
public abstract class OpenGlControlBase : Control
@@ -217,7 +219,7 @@ namespace Avalonia.OpenGL.Controls
return true;
}
- [Obsolete("Use RequestNextFrameRendering()")]
+ [Obsolete("Use RequestNextFrameRendering()"), EditorBrowsable(EditorBrowsableState.Never)]
// ReSharper disable once MemberCanBeProtected.Global
public new void InvalidateVisual() => RequestNextFrameRendering();
diff --git a/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj b/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj
index 22ee548823..4cae8e82df 100644
--- a/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj
+++ b/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj
@@ -12,4 +12,5 @@
+
diff --git a/src/Avalonia.ReactiveUI/ReactiveUserControl.cs b/src/Avalonia.ReactiveUI/ReactiveUserControl.cs
index 21cdef2634..b0978dc3f6 100644
--- a/src/Avalonia.ReactiveUI/ReactiveUserControl.cs
+++ b/src/Avalonia.ReactiveUI/ReactiveUserControl.cs
@@ -17,6 +17,7 @@ namespace Avalonia.ReactiveUI
/// ViewModel type.
public class ReactiveUserControl : UserControl, IViewFor where TViewModel : class
{
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1002", Justification = "Generic avalonia property is expected here.")]
public static readonly StyledProperty ViewModelProperty = AvaloniaProperty
.Register, TViewModel?>(nameof(ViewModel));
diff --git a/src/Avalonia.ReactiveUI/ReactiveWindow.cs b/src/Avalonia.ReactiveUI/ReactiveWindow.cs
index 726fb3d661..14e9353096 100644
--- a/src/Avalonia.ReactiveUI/ReactiveWindow.cs
+++ b/src/Avalonia.ReactiveUI/ReactiveWindow.cs
@@ -17,6 +17,7 @@ namespace Avalonia.ReactiveUI
/// ViewModel type.
public class ReactiveWindow : Window, IViewFor where TViewModel : class
{
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1002", Justification = "Generic avalonia property is expected here.")]
public static readonly StyledProperty ViewModelProperty = AvaloniaProperty
.Register, TViewModel?>(nameof(ViewModel));
diff --git a/src/Avalonia.ReactiveUI/RoutedViewHost.cs b/src/Avalonia.ReactiveUI/RoutedViewHost.cs
index 2d848d4cd7..701b0d0f46 100644
--- a/src/Avalonia.ReactiveUI/RoutedViewHost.cs
+++ b/src/Avalonia.ReactiveUI/RoutedViewHost.cs
@@ -50,7 +50,7 @@ namespace Avalonia.ReactiveUI
/// ReactiveUI routing documentation website for more info.
///
///
- public class RoutedViewHost : TransitioningContentControl, IActivatableView, IEnableLogger, IStyleable
+ public class RoutedViewHost : TransitioningContentControl, IActivatableView, IEnableLogger
{
///
/// for the property.
@@ -126,7 +126,7 @@ namespace Avalonia.ReactiveUI
///
public IViewLocator? ViewLocator { get; set; }
- Type IStyleable.StyleKey => typeof(TransitioningContentControl);
+ protected override Type StyleKeyOverride => typeof(TransitioningContentControl);
///
/// Invoked when ReactiveUI router navigates to a view model.
diff --git a/src/Avalonia.ReactiveUI/ViewModelViewHost.cs b/src/Avalonia.ReactiveUI/ViewModelViewHost.cs
index dc45758046..3e7ed42662 100644
--- a/src/Avalonia.ReactiveUI/ViewModelViewHost.cs
+++ b/src/Avalonia.ReactiveUI/ViewModelViewHost.cs
@@ -13,7 +13,7 @@ namespace Avalonia.ReactiveUI
/// the ViewModel property and display it. This control is very useful
/// inside a DataTemplate to display the View associated with a ViewModel.
///
- public class ViewModelViewHost : TransitioningContentControl, IViewFor, IEnableLogger, IStyleable
+ public class ViewModelViewHost : TransitioningContentControl, IViewFor, IEnableLogger
{
///
/// for the property.
@@ -78,7 +78,7 @@ namespace Avalonia.ReactiveUI
///
public IViewLocator? ViewLocator { get; set; }
- Type IStyleable.StyleKey => typeof(TransitioningContentControl);
+ protected override Type StyleKeyOverride => typeof(TransitioningContentControl);
///
/// Invoked when ReactiveUI router navigates to a view model.
diff --git a/src/Avalonia.Remote.Protocol/MetsysBson.cs b/src/Avalonia.Remote.Protocol/MetsysBson.cs
index c0263b3518..8966dd4206 100644
--- a/src/Avalonia.Remote.Protocol/MetsysBson.cs
+++ b/src/Avalonia.Remote.Protocol/MetsysBson.cs
@@ -715,7 +715,8 @@ namespace Metsys.Bson
public MagicProperty FindProperty(string name)
{
- return _properties.ContainsKey(name) ? _properties[name] : null;
+ _properties.TryGetValue(name, out var property);
+ return property;
}
public static TypeHelper GetHelperForType(Type type)
@@ -1196,7 +1197,9 @@ namespace Metsys.Bson
}
object container = null;
var property = typeHelper.FindProperty(name);
- var propertyType = property != null ? property.Type : _typeMap.ContainsKey(storageType) ? _typeMap[storageType] : typeof(object);
+ var propertyType = property?.Type
+ ?? (_typeMap.TryGetValue(storageType, out var type1) ? type1 : null)
+ ?? typeof(object);
if (property != null && property.Setter == null)
{
container = property.Getter(instance);
@@ -1588,7 +1591,7 @@ namespace Metsys.Bson.Configuration
{
return property;
}
- return map.ContainsKey(property) ? map[property] : property;
+ return map.TryGetValue(property, out var value) ? value : property;
}
public void AddIgnore(string name)
diff --git a/src/Avalonia.Themes.Fluent/Accents/AccentColors.xaml b/src/Avalonia.Themes.Fluent/Accents/AccentColors.xaml
deleted file mode 100644
index 0fb3ab73c2..0000000000
--- a/src/Avalonia.Themes.Fluent/Accents/AccentColors.xaml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
- #FF0078D7
- #FF005A9E
- #FF004275
- #FF002642
- #FF429CE3
- #FF76B9ED
- #FFA6D8FF
-
diff --git a/src/Avalonia.Themes.Fluent/Accents/BaseColorsPalette.xaml b/src/Avalonia.Themes.Fluent/Accents/BaseColorsPalette.xaml
new file mode 100644
index 0000000000..362d543646
--- /dev/null
+++ b/src/Avalonia.Themes.Fluent/Accents/BaseColorsPalette.xaml
@@ -0,0 +1,69 @@
+
+
+
+
+ #FFFFFFFF
+ #33FFFFFF
+ #99FFFFFF
+ #CCFFFFFF
+ #66FFFFFF
+ #FF000000
+ #33000000
+ #99000000
+ #CC000000
+ #66000000
+ #FF171717
+ #FF000000
+ #33000000
+ #66000000
+ #CC000000
+ #FFCCCCCC
+ #FF7A7A7A
+ #FFCCCCCC
+ #FFF2F2F2
+ #FFE6E6E6
+ #FFF2F2F2
+ #FFFFFFFF
+ #FF767676
+ #19000000
+ #33000000
+ #C50500
+ #FFFFFFFF
+ #17000000
+ #2E000000
+
+
+ #FF000000
+ #33000000
+ #99000000
+ #CC000000
+ #66000000
+ #FFFFFFFF
+ #33FFFFFF
+ #99FFFFFF
+ #CCFFFFFF
+ #66FFFFFF
+ #FFF2F2F2
+ #FF000000
+ #33000000
+ #66000000
+ #CC000000
+ #FF333333
+ #FF858585
+ #FF767676
+ #FF171717
+ #FF1F1F1F
+ #FF2B2B2B
+ #FFFFFFFF
+ #FF767676
+ #19FFFFFF
+ #33FFFFFF
+ #FFF000
+ #FF000000
+ #18FFFFFF
+ #30FFFFFF
+
+
+
+
diff --git a/src/Avalonia.Themes.Fluent/Accents/Base.xaml b/src/Avalonia.Themes.Fluent/Accents/BaseResources.xaml
similarity index 78%
rename from src/Avalonia.Themes.Fluent/Accents/Base.xaml
rename to src/Avalonia.Themes.Fluent/Accents/BaseResources.xaml
index c19a4f5c09..517a80fd7e 100644
--- a/src/Avalonia.Themes.Fluent/Accents/Base.xaml
+++ b/src/Avalonia.Themes.Fluent/Accents/BaseResources.xaml
@@ -2,7 +2,6 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="using:System"
xmlns:converters="using:Avalonia.Controls.Converters">
-
fonts:Inter#Inter, $Default
14
@@ -28,39 +27,33 @@
-
-
-
- #FFFFFFFF
- #33FFFFFF
- #99FFFFFF
- #CCFFFFFF
- #66FFFFFF
- #FF000000
- #33000000
- #99000000
- #CC000000
- #66000000
- #FF171717
- #FF000000
- #33000000
- #66000000
- #CC000000
- #FFCCCCCC
- #FF7A7A7A
- #FFCCCCCC
- #FFF2F2F2
- #FFE6E6E6
- #FFF2F2F2
- #FFFFFFFF
- #FF767676
- #19000000
- #33000000
- #C50500
+ 374
+ 0,2,0,2
+ 1
+ -1,0,-1,0
+ 32
+ 64
+ 456
+ 0
+ 1
+ 0
+
+ 12,11,12,12
+ 96
+ 40
+ 758
- #17000000
- #2E000000
+
+ 0
+
+ 0,4,0,4
+
+
+ 12,0,12,0
+
+
+
-
-
-
-
-
-
-
- #FFFFFFFF
-
-
-
- 374
- 0,2,0,2
- 1
- -1,0,-1,0
- 32
- 64
- 456
- 0
- 1
- 0
-
- 12,11,12,12
- 96
- 40
- 758
-
-
- 0
-
-
- 0,4,0,4
-
-
- 12,0,12,0
+
-
- #FF000000
- #33000000
- #99000000
- #CC000000
- #66000000
- #FFFFFFFF
- #33FFFFFF
- #99FFFFFF
- #CCFFFFFF
- #66FFFFFF
- #FFF2F2F2
- #FF000000
- #33000000
- #66000000
- #CC000000
- #FF333333
- #FF858585
- #FF767676
- #FF171717
- #FF1F1F1F
- #FF2B2B2B
- #FFFFFFFF
- #FF767676
- #19FFFFFF
- #33FFFFFF
- #FFF000
-
- #18FFFFFF
- #30FFFFFF
-
-
-
-
-
-
-
- #FF000000
-
-
- 374
- 0,2,0,2
- 1
- -1,0,-1,0
- 32
- 64
- 456
- 0
- 1
- 0
-
- 12,11,12,12
- 96
- 40
- 758
-
-
- 0
-
-
- 0,4,0,4
-
-
- 12,0,12,0
+
diff --git a/src/Avalonia.Themes.Fluent/Accents/FluentControlResources.xaml b/src/Avalonia.Themes.Fluent/Accents/FluentControlResources.xaml
index a9bc622221..61a74f26a4 100644
--- a/src/Avalonia.Themes.Fluent/Accents/FluentControlResources.xaml
+++ b/src/Avalonia.Themes.Fluent/Accents/FluentControlResources.xaml
@@ -4,8 +4,8 @@
-
-
+
+
@@ -52,7 +52,8 @@
-
+
@@ -291,15 +292,17 @@
ResourceKey="SystemControlHighlightBaseHighBrush" />
-
+
-
-
+
+
@@ -309,13 +312,17 @@
ResourceKey="SystemControlBackgroundBaseMediumLowBrush" />
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
@@ -470,8 +481,8 @@
-
-
+
+
@@ -502,8 +513,8 @@
-
-
+
+
@@ -701,8 +712,9 @@
-
-
+
+
@@ -775,8 +787,8 @@
-
-
+
+
@@ -823,7 +835,8 @@
-
+
@@ -1065,14 +1078,17 @@
ResourceKey="SystemControlHighlightBaseHighBrush" />
-
-
+
+
-
-
+
+
@@ -1082,13 +1098,17 @@
ResourceKey="SystemControlBackgroundBaseMediumLowBrush" />
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
@@ -1243,8 +1267,8 @@
-
-
+
+
@@ -1275,12 +1299,12 @@
-
-
+
+
-
-
+
+
@@ -1476,8 +1500,8 @@
-
-
+
+
diff --git a/src/Avalonia.Themes.Fluent/Accents/SystemAccentColors.cs b/src/Avalonia.Themes.Fluent/Accents/SystemAccentColors.cs
new file mode 100644
index 0000000000..a4ef15f950
--- /dev/null
+++ b/src/Avalonia.Themes.Fluent/Accents/SystemAccentColors.cs
@@ -0,0 +1,163 @@
+using System;
+using Avalonia.Controls;
+using Avalonia.Media;
+using Avalonia.Platform;
+using Avalonia.Styling;
+
+namespace Avalonia.Themes.Fluent.Accents;
+
+internal class SystemAccentColors : IResourceProvider
+{
+ public const string AccentKey = "SystemAccentColor";
+ public const string AccentDark1Key = "SystemAccentColorDark1";
+ public const string AccentDark2Key = "SystemAccentColorDark2";
+ public const string AccentDark3Key = "SystemAccentColorDark3";
+ public const string AccentLight1Key = "SystemAccentColorLight1";
+ public const string AccentLight2Key = "SystemAccentColorLight2";
+ public const string AccentLight3Key = "SystemAccentColorLight3";
+
+ private static readonly Color s_defaultSystemAccentColor = Color.FromRgb(0, 120, 215);
+ private readonly IPlatformSettings? _platformSettings;
+ private bool _invalidateColors = true;
+ private Color _systemAccentColor;
+ private Color _systemAccentColorDark1, _systemAccentColorDark2, _systemAccentColorDark3;
+ private Color _systemAccentColorLight1, _systemAccentColorLight2, _systemAccentColorLight3;
+
+ public SystemAccentColors()
+ {
+ _platformSettings = AvaloniaLocator.Current.GetService();
+ }
+
+ public bool HasResources => true;
+ public bool TryGetResource(object key, ThemeVariant? theme, out object? value)
+ {
+ if (key is string strKey)
+ {
+ if (strKey.Equals(AccentKey, StringComparison.InvariantCulture))
+ {
+ EnsureColors();
+ value = _systemAccentColor;
+ return true;
+ }
+
+ if (strKey.Equals(AccentDark1Key, StringComparison.InvariantCulture))
+ {
+ EnsureColors();
+ value = _systemAccentColorDark1;
+ return true;
+ }
+
+ if (strKey.Equals(AccentDark2Key, StringComparison.InvariantCulture))
+ {
+ EnsureColors();
+ value = _systemAccentColorDark2;
+ return true;
+ }
+
+ if (strKey.Equals(AccentDark3Key, StringComparison.InvariantCulture))
+ {
+ EnsureColors();
+ value = _systemAccentColorDark3;
+ return true;
+ }
+
+ if (strKey.Equals(AccentLight1Key, StringComparison.InvariantCulture))
+ {
+ EnsureColors();
+ value = _systemAccentColorLight1;
+ return true;
+ }
+
+ if (strKey.Equals(AccentLight2Key, StringComparison.InvariantCulture))
+ {
+ EnsureColors();
+ value = _systemAccentColorLight2;
+ return true;
+ }
+
+ if (strKey.Equals(AccentLight3Key, StringComparison.InvariantCulture))
+ {
+ EnsureColors();
+ value = _systemAccentColorLight3;
+ return true;
+ }
+ }
+
+ value = null;
+ return false;
+ }
+
+ public IResourceHost? Owner { get; private set; }
+ public event EventHandler? OwnerChanged;
+ public void AddOwner(IResourceHost owner)
+ {
+ if (Owner != owner)
+ {
+ Owner = owner;
+ OwnerChanged?.Invoke(this, EventArgs.Empty);
+
+ if (_platformSettings is not null)
+ {
+ _platformSettings.ColorValuesChanged += PlatformSettingsOnColorValuesChanged;
+ }
+ }
+ }
+
+ public void RemoveOwner(IResourceHost owner)
+ {
+ if (Owner == owner)
+ {
+ Owner = null;
+ OwnerChanged?.Invoke(this, EventArgs.Empty);
+
+ if (_platformSettings is not null)
+ {
+ _platformSettings.ColorValuesChanged -= PlatformSettingsOnColorValuesChanged;
+ }
+ }
+ }
+
+ private void EnsureColors()
+ {
+ if (_invalidateColors)
+ {
+ _invalidateColors = false;
+
+ _systemAccentColor = _platformSettings?.GetColorValues().AccentColor1 ?? s_defaultSystemAccentColor;
+ (_systemAccentColorDark1,_systemAccentColorDark2, _systemAccentColorDark3,
+ _systemAccentColorLight1, _systemAccentColorLight2, _systemAccentColorLight3) = CalculateAccentShades(_systemAccentColor);
+ }
+ }
+
+ public static (Color d1, Color d2, Color d3, Color l1, Color l2, Color l3) CalculateAccentShades(Color accentColor)
+ {
+ // dark1step = (hslAccent.L - SystemAccentColorDark1.L) * 255
+ const double dark1step = 28.5 / 255d;
+ const double dark2step = 49 / 255d;
+ const double dark3step = 74.5 / 255d;
+ // light1step = (SystemAccentColorLight1.L - hslAccent.L) * 255
+ const double light1step = 39 / 255d;
+ const double light2step = 70 / 255d;
+ const double light3step = 103 / 255d;
+
+ var hslAccent = accentColor.ToHsl();
+
+ return (
+ // Darker shades
+ new HslColor(hslAccent.A, hslAccent.H, hslAccent.S, hslAccent.L - dark1step).ToRgb(),
+ new HslColor(hslAccent.A, hslAccent.H, hslAccent.S, hslAccent.L - dark2step).ToRgb(),
+ new HslColor(hslAccent.A, hslAccent.H, hslAccent.S, hslAccent.L - dark3step).ToRgb(),
+
+ // Lighter shades
+ new HslColor(hslAccent.A, hslAccent.H, hslAccent.S, hslAccent.L + light1step).ToRgb(),
+ new HslColor(hslAccent.A, hslAccent.H, hslAccent.S, hslAccent.L + light2step).ToRgb(),
+ new HslColor(hslAccent.A, hslAccent.H, hslAccent.S, hslAccent.L + light3step).ToRgb()
+ );
+ }
+
+ private void PlatformSettingsOnColorValuesChanged(object? sender, PlatformColorValues e)
+ {
+ _invalidateColors = true;
+ Owner?.NotifyHostedResourcesChanged(ResourcesChangedEventArgs.Empty);
+ }
+}
diff --git a/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj b/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj
index 8d266ce82f..e63a0c9c26 100644
--- a/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj
+++ b/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj
@@ -15,4 +15,5 @@
+
diff --git a/src/Avalonia.Themes.Fluent/ColorPaletteResources.Properties.cs b/src/Avalonia.Themes.Fluent/ColorPaletteResources.Properties.cs
new file mode 100644
index 0000000000..366af8e227
--- /dev/null
+++ b/src/Avalonia.Themes.Fluent/ColorPaletteResources.Properties.cs
@@ -0,0 +1,158 @@
+using Avalonia.Media;
+
+namespace Avalonia.Themes.Fluent;
+
+public partial class ColorPaletteResources
+{
+ private bool _hasAccentColor;
+ private Color _accentColor;
+ private Color _accentColorDark1, _accentColorDark2, _accentColorDark3;
+ private Color _accentColorLight1, _accentColorLight2, _accentColorLight3;
+
+ public static readonly DirectProperty AccentProperty
+ = AvaloniaProperty.RegisterDirect(nameof(Accent), r => r.Accent, (r, v) => r.Accent = v);
+
+ ///
+ /// Gets or sets the Accent color value.
+ ///
+ public Color Accent
+ {
+ get => _accentColor;
+ set => SetAndRaise(AccentProperty, ref _accentColor, value);
+ }
+
+ ///
+ /// Gets or sets the AltHigh color value.
+ ///
+ public Color AltHigh { get => GetColor("SystemAltHighColor"); set => SetColor("SystemAltHighColor", value); }
+
+ ///
+ /// Gets or sets the AltLow color value.
+ ///
+ public Color AltLow { get => GetColor("SystemAltLowColor"); set => SetColor("SystemAltLowColor", value); }
+
+ ///
+ /// Gets or sets the AltMedium color value.
+ ///
+ public Color AltMedium { get => GetColor("SystemAltMediumColor"); set => SetColor("SystemAltMediumColor", value); }
+
+ ///
+ /// Gets or sets the AltMediumHigh color value.
+ ///
+ public Color AltMediumHigh { get => GetColor("SystemAltMediumHighColor"); set => SetColor("SystemAltMediumHighColor", value); }
+
+ ///
+ /// Gets or sets the AltMediumLow color value.
+ ///
+ public Color AltMediumLow { get => GetColor("SystemAltMediumLowColor"); set => SetColor("SystemAltMediumLowColor", value); }
+
+ ///
+ /// Gets or sets the BaseHigh color value.
+ ///
+ public Color BaseHigh { get => GetColor("SystemBaseHighColor"); set => SetColor("SystemBaseHighColor", value); }
+
+ ///
+ /// Gets or sets the BaseLow color value.
+ ///
+ public Color BaseLow { get => GetColor("SystemBaseLowColor"); set => SetColor("SystemBaseLowColor", value); }
+
+ ///
+ /// Gets or sets the BaseMedium color value.
+ ///
+ public Color BaseMedium { get => GetColor("SystemBaseMediumColor"); set => SetColor("SystemBaseMediumColor", value); }
+
+ ///
+ /// Gets or sets the BaseMediumHigh color value.
+ ///
+ public Color BaseMediumHigh { get => GetColor("SystemBaseMediumHighColor"); set => SetColor("SystemBaseMediumHighColor", value); }
+
+ ///
+ /// Gets or sets the BaseMediumLow color value.
+ ///
+ public Color BaseMediumLow { get => GetColor("SystemBaseMediumLowColor"); set => SetColor("SystemBaseMediumLowColor", value); }
+
+ ///
+ /// Gets or sets the ChromeAltLow color value.
+ ///
+ public Color ChromeAltLow { get => GetColor("SystemChromeAltLowColor"); set => SetColor("SystemChromeAltLowColor", value); }
+
+ ///
+ /// Gets or sets the ChromeBlackHigh color value.
+ ///
+ public Color ChromeBlackHigh { get => GetColor("SystemChromeBlackHighColor"); set => SetColor("SystemChromeBlackHighColor", value); }
+
+ ///
+ /// Gets or sets the ChromeBlackLow color value.
+ ///
+ public Color ChromeBlackLow { get => GetColor("SystemChromeBlackLowColor"); set => SetColor("SystemChromeBlackLowColor", value); }
+
+ ///
+ /// Gets or sets the ChromeBlackMedium color value.
+ ///
+ public Color ChromeBlackMedium { get => GetColor("SystemChromeBlackMediumColor"); set => SetColor("SystemChromeBlackMediumColor", value); }
+
+ ///
+ /// Gets or sets the ChromeBlackMediumLow color value.
+ ///
+ public Color ChromeBlackMediumLow { get => GetColor("SystemChromeBlackMediumLowColor"); set => SetColor("SystemChromeBlackMediumLowColor", value); }
+
+ ///
+ /// Gets or sets the ChromeDisabledHigh color value.
+ ///
+ public Color ChromeDisabledHigh { get => GetColor("SystemChromeDisabledHighColor"); set => SetColor("SystemChromeDisabledHighColor", value); }
+
+ ///
+ /// Gets or sets the ChromeDisabledLow color value.
+ ///
+ public Color ChromeDisabledLow { get => GetColor("SystemChromeDisabledLowColor"); set => SetColor("SystemChromeDisabledLowColor", value); }
+
+ ///
+ /// Gets or sets the ChromeGray color value.
+ ///
+ public Color ChromeGray { get => GetColor("SystemChromeGrayColor"); set => SetColor("SystemChromeGrayColor", value); }
+
+ ///
+ /// Gets or sets the ChromeHigh color value.
+ ///
+ public Color ChromeHigh { get => GetColor("SystemChromeHighColor"); set => SetColor("SystemChromeHighColor", value); }
+
+ ///
+ /// Gets or sets the ChromeLow color value.
+ ///
+ public Color ChromeLow { get => GetColor("SystemChromeLowColor"); set => SetColor("SystemChromeLowColor", value); }
+
+ ///
+ /// Gets or sets the ChromeMedium color value.
+ ///
+ public Color ChromeMedium { get => GetColor("SystemChromeMediumColor"); set => SetColor("SystemChromeMediumColor", value); }
+
+ ///
+ /// Gets or sets the ChromeMediumLow color value.
+ ///
+ public Color ChromeMediumLow { get => GetColor("SystemChromeMediumLowColor"); set => SetColor("SystemChromeMediumLowColor", value); }
+
+ ///
+ /// Gets or sets the ChromeWhite color value.
+ ///
+ public Color ChromeWhite { get => GetColor("SystemChromeWhiteColor"); set => SetColor("SystemChromeWhiteColor", value); }
+
+ ///
+ /// Gets or sets the ErrorText color value.
+ ///
+ public Color ErrorText { get => GetColor("SystemErrorTextColor"); set => SetColor("SystemErrorTextColor", value); }
+
+ ///
+ /// Gets or sets the ListLow color value.
+ ///
+ public Color ListLow { get => GetColor("SystemListLowColor"); set => SetColor("SystemListLowColor", value); }
+
+ ///
+ /// Gets or sets the ListMedium color value.
+ ///
+ public Color ListMedium { get => GetColor("SystemListMediumColor"); set => SetColor("SystemListMediumColor", value); }
+
+ ///
+ /// Gets or sets the RegionColor color value.
+ ///
+ public Color RegionColor { get => GetColor("SystemRegionColor"); set => SetColor("SystemRegionColor", value); }
+}
diff --git a/src/Avalonia.Themes.Fluent/ColorPaletteResources.cs b/src/Avalonia.Themes.Fluent/ColorPaletteResources.cs
new file mode 100644
index 0000000000..ce52f51752
--- /dev/null
+++ b/src/Avalonia.Themes.Fluent/ColorPaletteResources.cs
@@ -0,0 +1,118 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Avalonia.Controls;
+using Avalonia.Media;
+using Avalonia.Styling;
+using Avalonia.Themes.Fluent.Accents;
+
+namespace Avalonia.Themes.Fluent;
+
+///
+/// Represents a specialized resource dictionary that contains color resources used by FluentTheme elements.
+///
+///
+/// This class can only be used in .
+///
+public partial class ColorPaletteResources : AvaloniaObject, IResourceNode
+{
+ private readonly Dictionary _colors = new(StringComparer.InvariantCulture);
+
+ public bool HasResources => _hasAccentColor || _colors.Count > 0;
+
+ public bool TryGetResource(object key, ThemeVariant? theme, out object? value)
+ {
+ if (key is string strKey)
+ {
+ if (strKey.Equals(SystemAccentColors.AccentKey, StringComparison.InvariantCulture))
+ {
+ value = _accentColor;
+ return _hasAccentColor;
+ }
+
+ if (strKey.Equals(SystemAccentColors.AccentDark1Key, StringComparison.InvariantCulture))
+ {
+ value = _accentColorDark1;
+ return _hasAccentColor;
+ }
+
+ if (strKey.Equals(SystemAccentColors.AccentDark2Key, StringComparison.InvariantCulture))
+ {
+ value = _accentColorDark2;
+ return _hasAccentColor;
+ }
+
+ if (strKey.Equals(SystemAccentColors.AccentDark3Key, StringComparison.InvariantCulture))
+ {
+ value = _accentColorDark3;
+ return _hasAccentColor;
+ }
+
+ if (strKey.Equals(SystemAccentColors.AccentLight1Key, StringComparison.InvariantCulture))
+ {
+ value = _accentColorLight1;
+ return _hasAccentColor;
+ }
+
+ if (strKey.Equals(SystemAccentColors.AccentLight2Key, StringComparison.InvariantCulture))
+ {
+ value = _accentColorLight2;
+ return _hasAccentColor;
+ }
+
+ if (strKey.Equals(SystemAccentColors.AccentLight3Key, StringComparison.InvariantCulture))
+ {
+ value = _accentColorLight3;
+ return _hasAccentColor;
+ }
+
+ if (_colors.TryGetValue(strKey, out var color))
+ {
+ value = color;
+ return true;
+ }
+ }
+
+ value = null;
+ return false;
+ }
+
+ private Color GetColor(string key)
+ {
+ if (_colors.TryGetValue(key, out var color))
+ {
+ return color;
+ }
+
+ return default;
+ }
+
+ private void SetColor(string key, Color value)
+ {
+ if (value == default)
+ {
+ _colors.Remove(key);
+ }
+ else
+ {
+ _colors[key] = value;
+ }
+ }
+
+ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+ {
+ base.OnPropertyChanged(change);
+
+ if (change.Property == AccentProperty)
+ {
+ _hasAccentColor = _accentColor != default;
+
+ if (_hasAccentColor)
+ {
+ (_accentColorDark1, _accentColorDark2, _accentColorDark3,
+ _accentColorLight1, _accentColorLight2, _accentColorLight3) =
+ SystemAccentColors.CalculateAccentShades(_accentColor);
+ }
+ }
+ }
+}
diff --git a/src/Avalonia.Themes.Fluent/ColorPaletteResourcesCollection.cs b/src/Avalonia.Themes.Fluent/ColorPaletteResourcesCollection.cs
new file mode 100644
index 0000000000..261de5497d
--- /dev/null
+++ b/src/Avalonia.Themes.Fluent/ColorPaletteResourcesCollection.cs
@@ -0,0 +1,65 @@
+using System;
+using Avalonia.Collections;
+using Avalonia.Controls;
+using Avalonia.Styling;
+
+namespace Avalonia.Themes.Fluent;
+
+internal class ColorPaletteResourcesCollection : AvaloniaDictionary, IResourceProvider
+{
+ public ColorPaletteResourcesCollection() : base(2)
+ {
+ this.ForEachItem(
+ (_, x) =>
+ {
+ if (Owner is not null)
+ {
+ x.PropertyChanged += Palette_PropertyChanged;
+ }
+ },
+ (_, x) =>
+ {
+ if (Owner is not null)
+ {
+ x.PropertyChanged -= Palette_PropertyChanged;
+ }
+ },
+ () => throw new NotSupportedException("Dictionary reset not supported"));
+ }
+
+ public bool HasResources => Count > 0;
+ public bool TryGetResource(object key, ThemeVariant? theme, out object? value)
+ {
+ theme ??= ThemeVariant.Default;
+ if (base.TryGetValue(theme, out var paletteResources)
+ && paletteResources.TryGetResource(key, theme, out value))
+ {
+ return true;
+ }
+
+ value = null;
+ return false;
+ }
+
+ public IResourceHost? Owner { get; private set; }
+ public event EventHandler? OwnerChanged;
+ public void AddOwner(IResourceHost owner)
+ {
+ Owner = owner;
+ OwnerChanged?.Invoke(this, EventArgs.Empty);
+ }
+
+ public void RemoveOwner(IResourceHost owner)
+ {
+ Owner = null;
+ OwnerChanged?.Invoke(this, EventArgs.Empty);
+ }
+
+ private void Palette_PropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
+ {
+ if (e.Property == ColorPaletteResources.AccentProperty)
+ {
+ Owner?.NotifyHostedResourcesChanged(ResourcesChangedEventArgs.Empty);
+ }
+ }
+}
diff --git a/src/Avalonia.Themes.Fluent/Controls/AutoCompleteBox.xaml b/src/Avalonia.Themes.Fluent/Controls/AutoCompleteBox.xaml
index 6004d42120..2113ae4cb0 100644
--- a/src/Avalonia.Themes.Fluent/Controls/AutoCompleteBox.xaml
+++ b/src/Avalonia.Themes.Fluent/Controls/AutoCompleteBox.xaml
@@ -1,18 +1,21 @@
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:generic="using:System.Collections.Generic">
-
- Alabama
- Alaska
- Arizona
- Arkansas
- California
- Colorado
- Connecticut
- Delaware
-
+
+
+ Alabama
+ Alaska
+ Arizona
+ Arkansas
+ California
+ Colorado
+ Connecticut
+ Delaware
+
+
diff --git a/src/Avalonia.Themes.Fluent/Controls/CaptionButtons.xaml b/src/Avalonia.Themes.Fluent/Controls/CaptionButtons.xaml
index 71ae012289..0180c3f395 100644
--- a/src/Avalonia.Themes.Fluent/Controls/CaptionButtons.xaml
+++ b/src/Avalonia.Themes.Fluent/Controls/CaptionButtons.xaml
@@ -47,8 +47,9 @@
Data="M2048 2048v-819h-205v469l-1493 -1493h469v-205h-819v819h205v-469l1493 1493h-469v205h819z" />
-
+
+ Theme="{StaticResource FluentCaptionButton}"
+ AutomationProperties.Name="Maximize">
@@ -70,7 +72,8 @@
+ Theme="{StaticResource FluentCaptionButton}"
+ AutomationProperties.Name="Close">
-
+
diff --git a/src/Avalonia.Themes.Fluent/Controls/EmbeddableControlRoot.xaml b/src/Avalonia.Themes.Fluent/Controls/EmbeddableControlRoot.xaml
index f60424a2dc..ee51ef8085 100644
--- a/src/Avalonia.Themes.Fluent/Controls/EmbeddableControlRoot.xaml
+++ b/src/Avalonia.Themes.Fluent/Controls/EmbeddableControlRoot.xaml
@@ -2,7 +2,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
-
+
diff --git a/src/Avalonia.Themes.Fluent/Controls/ProgressBar.xaml b/src/Avalonia.Themes.Fluent/Controls/ProgressBar.xaml
index 253d85852e..64a598e359 100644
--- a/src/Avalonia.Themes.Fluent/Controls/ProgressBar.xaml
+++ b/src/Avalonia.Themes.Fluent/Controls/ProgressBar.xaml
@@ -125,13 +125,13 @@
-
+
-
+
-
+
@@ -140,13 +140,13 @@
-
+
-
+
-
+
@@ -155,13 +155,13 @@
-
+
-
+
-
+
@@ -170,28 +170,28 @@
-
+
-
+
-
+
diff --git a/src/Avalonia.Themes.Fluent/Controls/SelectableTextBlock.xaml b/src/Avalonia.Themes.Fluent/Controls/SelectableTextBlock.xaml
index f630969ae6..2556f80ef6 100644
--- a/src/Avalonia.Themes.Fluent/Controls/SelectableTextBlock.xaml
+++ b/src/Avalonia.Themes.Fluent/Controls/SelectableTextBlock.xaml
@@ -10,6 +10,8 @@
+
+
-
+
diff --git a/src/Avalonia.Themes.Simple/Controls/ProgressBar.xaml b/src/Avalonia.Themes.Simple/Controls/ProgressBar.xaml
index 3eb158d5b6..1f4bf006a0 100644
--- a/src/Avalonia.Themes.Simple/Controls/ProgressBar.xaml
+++ b/src/Avalonia.Themes.Simple/Controls/ProgressBar.xaml
@@ -93,7 +93,7 @@
-
+
diff --git a/src/Avalonia.Themes.Simple/Controls/SelectableTextBlock.xaml b/src/Avalonia.Themes.Simple/Controls/SelectableTextBlock.xaml
index aaa6448aea..e504a8cf8e 100644
--- a/src/Avalonia.Themes.Simple/Controls/SelectableTextBlock.xaml
+++ b/src/Avalonia.Themes.Simple/Controls/SelectableTextBlock.xaml
@@ -10,6 +10,8 @@
+
+