diff --git a/.editorconfig b/.editorconfig
index 128b6f1712..9ae52b8bbd 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -134,6 +134,20 @@ csharp_space_between_parentheses = false
csharp_space_between_square_brackets = false
space_within_single_line_array_initializer_braces = true
+#Net Analyzer
+dotnet_analyzer_diagnostic.category-Performance.severity = none #error - Uncomment when all violations are fixed.
+
+# CA1802: Use literals where appropriate
+dotnet_diagnostic.CA1802.severity = warning
+# CA1820: Test for empty strings using string length
+dotnet_diagnostic.CA1820.severity = warning
+# CA1821: Remove empty finalizers
+dotnet_diagnostic.CA1821.severity = warning
+# CA1825: Avoid zero-length array allocations
+dotnet_diagnostic.CA1825.severity = warning
+#CA1847: Use string.Contains(char) instead of string.Contains(string) with single characters
+dotnet_diagnostic.CA1847.severity = warning
+
# Wrapping preferences
csharp_wrap_before_ternary_opsigns = false
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
index c5a719ce90..df070c35cf 100644
--- a/.github/FUNDING.yml
+++ b/.github/FUNDING.yml
@@ -1 +1,2 @@
+github: avaloniaui
open_collective: avalonia
diff --git a/Avalonia.sln b/Avalonia.sln
index 81a9b43890..461de8530b 100644
--- a/Avalonia.sln
+++ b/Avalonia.sln
@@ -41,6 +41,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{A689DE
src\Shared\IsExternalInit.cs = src\Shared\IsExternalInit.cs
src\Shared\ModuleInitializer.cs = src\Shared\ModuleInitializer.cs
src\Shared\SourceGeneratorAttributes.cs = src\Shared\SourceGeneratorAttributes.cs
+ src\Avalonia.Base\Compatibility\StringCompatibilityExtensions.cs = src\Avalonia.Base\Compatibility\StringCompatibilityExtensions.cs
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.ReactiveUI", "src\Avalonia.ReactiveUI\Avalonia.ReactiveUI.csproj", "{6417B24E-49C2-4985-8DB2-3AB9D898EC91}"
@@ -90,6 +91,7 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1-27F5-4255-9AFC-04ABFD11683A}"
ProjectSection(SolutionItems) = preProject
build\ApiDiff.props = build\ApiDiff.props
+ build\AvaloniaPublicKey.props = build\AvaloniaPublicKey.props
build\Base.props = build\Base.props
build\Binding.props = build\Binding.props
build\CoreLibraries.props = build\CoreLibraries.props
@@ -102,8 +104,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1
build\Microsoft.CSharp.props = build\Microsoft.CSharp.props
build\Microsoft.Reactive.Testing.props = build\Microsoft.Reactive.Testing.props
build\Moq.props = build\Moq.props
+ build\NetAnalyzers.props = build\NetAnalyzers.props
build\NetCore.props = build\NetCore.props
build\NetFX.props = build\NetFX.props
+ build\NullableEnable.props = build\NullableEnable.props
build\ReactiveUI.props = build\ReactiveUI.props
build\ReferenceCoreLibraries.props = build\ReferenceCoreLibraries.props
build\Rx.props = build\Rx.props
@@ -198,8 +202,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Web", "Web", "{86A3F706-DC3
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Web.Blazor", "src\Web\Avalonia.Web.Blazor\Avalonia.Web.Blazor.csproj", "{25831348-EB2A-483E-9576-E8F6528674A5}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.Web", "samples\ControlCatalog.Web\ControlCatalog.Web.csproj", "{C08E9894-AA92-426E-BF56-033E262CAD3E}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WindowsInteropTest", "samples\interop\WindowsInteropTest\WindowsInteropTest.csproj", "{26A98DA1-D89D-4A95-8152-349F404DA2E2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlSamples", "samples\SampleControls\ControlSamples.csproj", "{A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}"
@@ -216,15 +218,17 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevGenerators", "src\tools\
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Web", "src\Web\Avalonia.Web\Avalonia.Web.csproj", "{76D39FF6-6B4F-46C4-93CD-E6FC4665739E}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Web.Sample", "src\Web\Avalonia.Web.Sample\Avalonia.Web.Sample.csproj", "{1F61B6F1-B881-4E27-A5B0-09D1F543F7AC}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MobileSandbox", "samples\MobileSandbox\MobileSandbox.csproj", "{3B8519C1-2F51-4F12-A348-120AB91D4532}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MobileSandbox.Android", "samples\MobileSandbox.Android\MobileSandbox.Android.csproj", "{C90FE60B-B01E-4F35-91D6-379D6966030F}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MobileSandbox", "samples\MobileSandbox\MobileSandbox.csproj", "{3B8519C1-2F51-4F12-A348-120AB91D4532}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MobileSandbox.iOS", "samples\MobileSandbox.iOS\MobileSandbox.iOS.csproj", "{FED9A71D-00D7-4F40-A9E4-1229EEA28EEB}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MobileSandbox.Android", "samples\MobileSandbox.Android\MobileSandbox.Android.csproj", "{C90FE60B-B01E-4F35-91D6-379D6966030F}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MobileSandbox.Desktop", "samples\MobileSandbox.Desktop\MobileSandbox.Desktop.csproj", "{62D392C9-81CF-487F-92E8-598B2AF3FDCE}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MobileSandbox.iOS", "samples\MobileSandbox.iOS\MobileSandbox.iOS.csproj", "{FED9A71D-00D7-4F40-A9E4-1229EEA28EEB}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.Blazor.Web", "samples\ControlCatalog.Blazor.Web\ControlCatalog.Blazor.Web.csproj", "{6A710364-AE6D-40BD-968B-024311527AC2}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MobileSandbox.Desktop", "samples\MobileSandbox.Desktop\MobileSandbox.Desktop.csproj", "{62D392C9-81CF-487F-92E8-598B2AF3FDCE}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.Web", "samples\ControlCatalog.Web\ControlCatalog.Web.csproj", "{8B3E8405-DE18-4048-A459-9CA4AC3319A2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -480,10 +484,6 @@ Global
{25831348-EB2A-483E-9576-E8F6528674A5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{25831348-EB2A-483E-9576-E8F6528674A5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{25831348-EB2A-483E-9576-E8F6528674A5}.Release|Any CPU.Build.0 = Release|Any CPU
- {C08E9894-AA92-426E-BF56-033E262CAD3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {C08E9894-AA92-426E-BF56-033E262CAD3E}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {C08E9894-AA92-426E-BF56-033E262CAD3E}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {C08E9894-AA92-426E-BF56-033E262CAD3E}.Release|Any CPU.Build.0 = Release|Any CPU
{26A98DA1-D89D-4A95-8152-349F404DA2E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{26A98DA1-D89D-4A95-8152-349F404DA2E2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{26A98DA1-D89D-4A95-8152-349F404DA2E2}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -516,10 +516,6 @@ Global
{76D39FF6-6B4F-46C4-93CD-E6FC4665739E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{76D39FF6-6B4F-46C4-93CD-E6FC4665739E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{76D39FF6-6B4F-46C4-93CD-E6FC4665739E}.Release|Any CPU.Build.0 = Release|Any CPU
- {1F61B6F1-B881-4E27-A5B0-09D1F543F7AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {1F61B6F1-B881-4E27-A5B0-09D1F543F7AC}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {1F61B6F1-B881-4E27-A5B0-09D1F543F7AC}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {1F61B6F1-B881-4E27-A5B0-09D1F543F7AC}.Release|Any CPU.Build.0 = Release|Any CPU
{3B8519C1-2F51-4F12-A348-120AB91D4532}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3B8519C1-2F51-4F12-A348-120AB91D4532}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3B8519C1-2F51-4F12-A348-120AB91D4532}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -536,6 +532,14 @@ Global
{62D392C9-81CF-487F-92E8-598B2AF3FDCE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{62D392C9-81CF-487F-92E8-598B2AF3FDCE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{62D392C9-81CF-487F-92E8-598B2AF3FDCE}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6A710364-AE6D-40BD-968B-024311527AC2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6A710364-AE6D-40BD-968B-024311527AC2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6A710364-AE6D-40BD-968B-024311527AC2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6A710364-AE6D-40BD-968B-024311527AC2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8B3E8405-DE18-4048-A459-9CA4AC3319A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8B3E8405-DE18-4048-A459-9CA4AC3319A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8B3E8405-DE18-4048-A459-9CA4AC3319A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8B3E8405-DE18-4048-A459-9CA4AC3319A2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -579,6 +583,7 @@ Global
{41B02319-965D-4945-8005-C1A3D1224165} = {86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B}
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{AF915D5C-AB00-4EA0-B5E6-001F4AE84E68} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
+ {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA} = {86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B}
{351337F5-D66F-461B-A957-4EF60BDB4BA6} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{909A8CBD-7D0E-42FD-B841-022AD8925820} = {8B6A8209-894F-4BA1-B880-965FD453982C}
{11BE52AF-E2DD-4CF0-B19A-05285ACAF571} = {9B9E3891-2366-4253-A952-D08BCEB71098}
@@ -586,7 +591,6 @@ Global
{676D6BFD-029D-4E43-BFC7-3892265CE251} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{F2CE566B-E7F6-447A-AB1A-3F574A6FE43A} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{25831348-EB2A-483E-9576-E8F6528674A5} = {86A3F706-DC3C-43C6-BE1B-B98F5BAAA268}
- {C08E9894-AA92-426E-BF56-033E262CAD3E} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{26A98DA1-D89D-4A95-8152-349F404DA2E2} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9}
{A0D0A6A4-5C72-4ADA-9B27-621C7D94F270} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{70B9F5CC-E2F9-4314-9514-EDE762ACCC4B} = {9B9E3891-2366-4253-A952-D08BCEB71098}
@@ -594,12 +598,12 @@ Global
{EABE2161-989B-42BF-BD8D-1E34B20C21F1} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{1BBFAD42-B99E-47E0-B00A-A4BC6B6BB4BB} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637}
{76D39FF6-6B4F-46C4-93CD-E6FC4665739E} = {86A3F706-DC3C-43C6-BE1B-B98F5BAAA268}
- {1F61B6F1-B881-4E27-A5B0-09D1F543F7AC} = {86A3F706-DC3C-43C6-BE1B-B98F5BAAA268}
- {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA} = {86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B}
{3B8519C1-2F51-4F12-A348-120AB91D4532} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{C90FE60B-B01E-4F35-91D6-379D6966030F} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{FED9A71D-00D7-4F40-A9E4-1229EEA28EEB} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{62D392C9-81CF-487F-92E8-598B2AF3FDCE} = {9B9E3891-2366-4253-A952-D08BCEB71098}
+ {6A710364-AE6D-40BD-968B-024311527AC2} = {9B9E3891-2366-4253-A952-D08BCEB71098}
+ {8B3E8405-DE18-4048-A459-9CA4AC3319A2} = {9B9E3891-2366-4253-A952-D08BCEB71098}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A}
diff --git a/NOTICE.md b/NOTICE.md
index 92fd725957..e97fc654c9 100644
--- a/NOTICE.md
+++ b/NOTICE.md
@@ -111,7 +111,7 @@ DEALINGS IN THE SOFTWARE.
# Metsys.Bson
-Copyright (c) 2010, Karl Seguin - http://www.openmymind.net/
+Copyright (c) 2010, Karl Seguin - https://www.openmymind.net/
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -302,4 +302,4 @@ https://github.com/chromium/chromium
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/azure-pipelines-integrationtests.yml b/azure-pipelines-integrationtests.yml
index ee8abb75c1..4fba4ca36f 100644
--- a/azure-pipelines-integrationtests.yml
+++ b/azure-pipelines-integrationtests.yml
@@ -12,6 +12,16 @@ jobs:
name: 'AvaloniaMacPool'
steps:
+ - task: UseDotNet@2
+ displayName: 'Use .NET Core SDK 6.0.401'
+ inputs:
+ version: 6.0.401
+
+ - task: UseDotNet@2
+ displayName: 'Use .NET Core SDK 7.0.100-rc.2.22477.23'
+ inputs:
+ version: 7.0.100-rc.2.22477.23
+
- script: system_profiler SPDisplaysDataType |grep Resolution
- script: |
@@ -45,6 +55,11 @@ jobs:
inputs:
version: 6.0.401
+ - task: UseDotNet@2
+ displayName: 'Use .NET Core SDK 7.0.100-rc.2.22477.23'
+ inputs:
+ version: 7.0.100-rc.2.22477.23
+
- task: Windows Application Driver@0
inputs:
OperationType: 'Start'
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 33b2dc670a..903f9e3843 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -35,9 +35,9 @@ jobs:
version: 6.0.401
- task: UseDotNet@2
- displayName: 'Use .NET Core SDK 7.0.100-rc.1.22431.12'
+ displayName: 'Use .NET Core SDK 7.0.100-rc.2.22477.23'
inputs:
- version: 7.0.100-rc.1.22431.12
+ version: 7.0.100-rc.2.22477.23
- task: CmdLine@2
displayName: 'Install Workloads'
@@ -72,9 +72,9 @@ jobs:
version: 6.0.401
- task: UseDotNet@2
- displayName: 'Use .NET Core SDK 7.0.100-rc.1.22431.12'
+ displayName: 'Use .NET Core SDK 7.0.100-rc.2.22477.23'
inputs:
- version: 7.0.100-rc.1.22431.12
+ version: 7.0.100-rc.2.22477.23
- task: CmdLine@2
displayName: 'Install Workloads'
@@ -143,9 +143,9 @@ jobs:
version: 6.0.401
- task: UseDotNet@2
- displayName: 'Use .NET Core SDK 7.0.100-rc.1.22431.12'
+ displayName: 'Use .NET Core SDK 7.0.100-rc.2.22477.23'
inputs:
- version: 7.0.100-rc.1.22431.12
+ version: 7.0.100-rc.2.22477.23
- task: CmdLine@2
displayName: 'Install Workloads'
diff --git a/build/NetAnalyzers.props b/build/NetAnalyzers.props
new file mode 100644
index 0000000000..dfca9ecf9e
--- /dev/null
+++ b/build/NetAnalyzers.props
@@ -0,0 +1,5 @@
+
+
+ true
+
+
diff --git a/build/SharedVersion.props b/build/SharedVersion.props
index 1b60bb4df9..5838519596 100644
--- a/build/SharedVersion.props
+++ b/build/SharedVersion.props
@@ -8,7 +8,7 @@
https://github.com/AvaloniaUI/Avalonia/
true
CS1591
- latest
+ preview
MIT
Icon.png
Avalonia is a cross-platform UI framework for .NET providing a flexible styling system and supporting a wide range of Operating Systems such as Windows, Linux, macOS and with experimental support for Android, iOS and WebAssembly.
diff --git a/dirs.proj b/dirs.proj
index a2544ef951..f1eaae8a4a 100644
--- a/dirs.proj
+++ b/dirs.proj
@@ -9,21 +9,18 @@
-
-
-
-
-
-
-
+
+
+
+
-
+
diff --git a/global.json b/global.json
index 44d4e10dbf..dc6da556b3 100644
--- a/global.json
+++ b/global.json
@@ -1,4 +1,8 @@
{
+ "sdk": {
+ "version": "7.0.100-rc.2.22477.23",
+ "rollForward": "latestFeature"
+ },
"msbuild-sdks": {
"Microsoft.Build.Traversal": "1.0.43",
"MSBuild.Sdk.Extras": "3.0.22",
diff --git a/native/Avalonia.Native/src/OSX/Screens.mm b/native/Avalonia.Native/src/OSX/Screens.mm
index b9c75ed742..83ab1bfd01 100644
--- a/native/Avalonia.Native/src/OSX/Screens.mm
+++ b/native/Avalonia.Native/src/OSX/Screens.mm
@@ -41,9 +41,9 @@ public:
ret->WorkingArea.X = [screen visibleFrame].origin.x;
ret->WorkingArea.Y = ConvertPointY(ToAvnPoint([screen visibleFrame].origin)).Y - ret->WorkingArea.Height;
- ret->PixelDensity = [screen backingScaleFactor];
+ ret->Scaling = [screen backingScaleFactor];
- ret->Primary = index == 0;
+ ret->IsPrimary = index == 0;
return S_OK;
}
diff --git a/packages/Avalonia/AvaloniaBuildTasks.targets b/packages/Avalonia/AvaloniaBuildTasks.targets
index d43a5c1624..4b9e33ffe8 100644
--- a/packages/Avalonia/AvaloniaBuildTasks.targets
+++ b/packages/Avalonia/AvaloniaBuildTasks.targets
@@ -3,7 +3,6 @@
<_AvaloniaUseExternalMSBuild>$(AvaloniaUseExternalMSBuild)
<_AvaloniaUseExternalMSBuild Condition="'$(_AvaloniaForceInternalMSBuild)' == 'true'">false
low
- <_AvaloniaPatchComInterop Condition="'$(_AvaloniaPatchComInterop)' == ''">false
<_AvaloniaSkipXamlCompilation Condition="'$(_AvaloniaSkipXamlCompilation)' == ''">false
@@ -71,7 +70,6 @@
Output="$(AvaloniaResourcesTemporaryFilePath)"
Root="$(MSBuildProjectDirectory)"
Resources="@(AvaloniaResource)"
- EmbeddedResources="@(EmbeddedResources)"
ReportImportance="$(AvaloniaXamlReportImportance)"/>
diff --git a/samples/ControlCatalog.Android/MainActivity.cs b/samples/ControlCatalog.Android/MainActivity.cs
index 33ca511340..62c582610c 100644
--- a/samples/ControlCatalog.Android/MainActivity.cs
+++ b/samples/ControlCatalog.Android/MainActivity.cs
@@ -5,16 +5,8 @@ using Avalonia.Android;
namespace ControlCatalog.Android
{
- [Activity(Label = "ControlCatalog.Android", Theme = "@style/MyTheme.NoActionBar", Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleInstance, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)]
- public class MainActivity : AvaloniaActivity
+ [Activity(Label = "ControlCatalog.Android", Theme = "@style/MyTheme.NoActionBar", Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleTop, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)]
+ public class MainActivity : AvaloniaMainActivity
{
- protected override AppBuilder CustomizeAppBuilder(AppBuilder builder)
- {
- return base.CustomizeAppBuilder(builder)
- .AfterSetup(_ =>
- {
- Pages.EmbedSample.Implementation = new EmbedSampleAndroid();
- });
- }
}
}
diff --git a/samples/ControlCatalog.Android/SplashActivity.cs b/samples/ControlCatalog.Android/SplashActivity.cs
index dc292fd37b..908b5f082a 100644
--- a/samples/ControlCatalog.Android/SplashActivity.cs
+++ b/samples/ControlCatalog.Android/SplashActivity.cs
@@ -1,12 +1,23 @@
using Android.App;
using Android.Content;
+using Android.Content.PM;
using Android.OS;
+using Avalonia.Android;
namespace ControlCatalog.Android
{
[Activity(Theme = "@style/MyTheme.Splash", MainLauncher = true, NoHistory = true)]
- public class SplashActivity : Activity
+ public class SplashActivity : AvaloniaSplashActivity
{
+ protected override Avalonia.AppBuilder CustomizeAppBuilder(Avalonia.AppBuilder builder)
+ {
+ return base.CustomizeAppBuilder(builder)
+ .AfterSetup(_ =>
+ {
+ Pages.EmbedSample.Implementation = new EmbedSampleAndroid();
+ });
+ }
+
protected override void OnCreate(Bundle? savedInstanceState)
{
base.OnCreate(savedInstanceState);
diff --git a/samples/ControlCatalog.Web/App.razor b/samples/ControlCatalog.Blazor.Web/App.razor
similarity index 100%
rename from samples/ControlCatalog.Web/App.razor
rename to samples/ControlCatalog.Blazor.Web/App.razor
diff --git a/samples/ControlCatalog.Blazor.Web/App.razor.cs b/samples/ControlCatalog.Blazor.Web/App.razor.cs
new file mode 100644
index 0000000000..8cc0095f20
--- /dev/null
+++ b/samples/ControlCatalog.Blazor.Web/App.razor.cs
@@ -0,0 +1,17 @@
+using Avalonia;
+using Avalonia.Web.Blazor;
+
+namespace ControlCatalog.Blazor.Web;
+
+public partial class App
+{
+ protected override void OnParametersSet()
+ {
+ AppBuilder.Configure()
+ .UseBlazor()
+ // .With(new SkiaOptions { CustomGpuFactory = null }) // uncomment to disable GPU/GL rendering
+ .SetupWithSingleViewLifetime();
+
+ base.OnParametersSet();
+ }
+}
diff --git a/samples/ControlCatalog.Blazor.Web/ControlCatalog.Blazor.Web.csproj b/samples/ControlCatalog.Blazor.Web/ControlCatalog.Blazor.Web.csproj
new file mode 100644
index 0000000000..03fb31f0d3
--- /dev/null
+++ b/samples/ControlCatalog.Blazor.Web/ControlCatalog.Blazor.Web.csproj
@@ -0,0 +1,29 @@
+
+
+ net7.0
+ browser-wasm
+ enable
+ 16777216
+ false
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/ControlCatalog.Web/Pages/Index.razor b/samples/ControlCatalog.Blazor.Web/Pages/Index.razor
similarity index 100%
rename from samples/ControlCatalog.Web/Pages/Index.razor
rename to samples/ControlCatalog.Blazor.Web/Pages/Index.razor
diff --git a/samples/ControlCatalog.Blazor.Web/Program.cs b/samples/ControlCatalog.Blazor.Web/Program.cs
new file mode 100644
index 0000000000..d71b125fa1
--- /dev/null
+++ b/samples/ControlCatalog.Blazor.Web/Program.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Net.Http;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
+using Microsoft.Extensions.DependencyInjection;
+using ControlCatalog.Blazor.Web;
+
+public class Program
+{
+ public static async Task Main(string[] args)
+ {
+ await CreateHostBuilder(args).Build().RunAsync();
+ }
+
+ public static WebAssemblyHostBuilder CreateHostBuilder(string[] args)
+ {
+ var builder = WebAssemblyHostBuilder.CreateDefault(args);
+
+ builder.RootComponents.Add("#app");
+
+ builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
+
+ return builder;
+ }
+}
+
+
+
+
diff --git a/samples/ControlCatalog.Web/Properties/launchSettings.json b/samples/ControlCatalog.Blazor.Web/Properties/launchSettings.json
similarity index 100%
rename from samples/ControlCatalog.Web/Properties/launchSettings.json
rename to samples/ControlCatalog.Blazor.Web/Properties/launchSettings.json
diff --git a/samples/ControlCatalog.Web/Shared/MainLayout.razor b/samples/ControlCatalog.Blazor.Web/Shared/MainLayout.razor
similarity index 100%
rename from samples/ControlCatalog.Web/Shared/MainLayout.razor
rename to samples/ControlCatalog.Blazor.Web/Shared/MainLayout.razor
diff --git a/samples/ControlCatalog.Web/_Imports.razor b/samples/ControlCatalog.Blazor.Web/_Imports.razor
similarity index 85%
rename from samples/ControlCatalog.Web/_Imports.razor
rename to samples/ControlCatalog.Blazor.Web/_Imports.razor
index 04c7a8690e..0e6d11b419 100644
--- a/samples/ControlCatalog.Web/_Imports.razor
+++ b/samples/ControlCatalog.Blazor.Web/_Imports.razor
@@ -6,6 +6,5 @@
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.AspNetCore.Components.WebAssembly.Http
@using Microsoft.JSInterop
-@using ControlCatalog.Web
-@using ControlCatalog.Web.Shared
+@using ControlCatalog.Blazor.Web.Shared
@using SkiaSharp
diff --git a/samples/ControlCatalog.Web/wwwroot/css/app.css b/samples/ControlCatalog.Blazor.Web/wwwroot/css/app.css
similarity index 100%
rename from samples/ControlCatalog.Web/wwwroot/css/app.css
rename to samples/ControlCatalog.Blazor.Web/wwwroot/css/app.css
diff --git a/samples/ControlCatalog.Web/wwwroot/favicon.ico b/samples/ControlCatalog.Blazor.Web/wwwroot/favicon.ico
similarity index 100%
rename from samples/ControlCatalog.Web/wwwroot/favicon.ico
rename to samples/ControlCatalog.Blazor.Web/wwwroot/favicon.ico
diff --git a/samples/ControlCatalog.Web/wwwroot/index.html b/samples/ControlCatalog.Blazor.Web/wwwroot/index.html
similarity index 100%
rename from samples/ControlCatalog.Web/wwwroot/index.html
rename to samples/ControlCatalog.Blazor.Web/wwwroot/index.html
diff --git a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
index 0667644643..6c17e9ac43 100644
--- a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
+++ b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
@@ -5,6 +5,7 @@
net6.0
true
true
+ 6.0.9
diff --git a/samples/ControlCatalog.Web/App.razor.cs b/samples/ControlCatalog.Web/App.razor.cs
deleted file mode 100644
index bcd2a6fefc..0000000000
--- a/samples/ControlCatalog.Web/App.razor.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using Avalonia;
-using Avalonia.Web.Blazor;
-
-namespace ControlCatalog.Web;
-
-public partial class App
-{
- protected override void OnParametersSet()
- {
- WebAppBuilder.Configure()
- .AfterSetup(_ =>
- {
- ControlCatalog.Pages.EmbedSample.Implementation = new EmbedSampleWeb();
- })
- .With(new SkiaOptions { CustomGpuFactory = null }) // uncomment to disable GPU/GL rendering
- .SetupWithSingleViewLifetime();
-
- base.OnParametersSet();
- }
-}
diff --git a/samples/ControlCatalog.Web/ControlCatalog.Web.csproj b/samples/ControlCatalog.Web/ControlCatalog.Web.csproj
index b2c9ec72eb..06a5466619 100644
--- a/samples/ControlCatalog.Web/ControlCatalog.Web.csproj
+++ b/samples/ControlCatalog.Web/ControlCatalog.Web.csproj
@@ -1,57 +1,44 @@
-
+
- net6.0
- enable
-
- true
- 16777216
- false
- false
+ net7.0
+ browser-wasm
+ main.js
+ Exe
+ true
+ true
+ true
+ -sVERBOSE -sERROR_ON_UNDEFINED_SYMBOLS=0
-
-
- false
- -O1
- false
-
-
-
- true
- true
- -O3
- -O3
- false
- false
- false
- false
- false
- false
- true
- false
- true
- true
+
+ true
true
- link
- true
+ full
+ true
+ true
+ -O2
+ -O2
-
-
+
-
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
diff --git a/samples/ControlCatalog.Web/EmbedSample.Browser.cs b/samples/ControlCatalog.Web/EmbedSample.Browser.cs
index 5fe14409de..5cfbb608cc 100644
--- a/samples/ControlCatalog.Web/EmbedSample.Browser.cs
+++ b/samples/ControlCatalog.Web/EmbedSample.Browser.cs
@@ -1,34 +1,42 @@
using System;
-
-using Avalonia;
+using System.Runtime.InteropServices.JavaScript;
using Avalonia.Platform;
-using Avalonia.Web.Blazor;
+using Avalonia.Web;
using ControlCatalog.Pages;
-using Microsoft.JSInterop;
-
namespace ControlCatalog.Web;
public class EmbedSampleWeb : INativeDemoControl
{
public IPlatformHandle CreateControl(bool isSecond, IPlatformHandle parent, Func createDefault)
{
- var runtime = AvaloniaLocator.Current.GetRequiredService();
-
if (isSecond)
{
- var iframe = runtime.Invoke("document.createElement", "iframe");
- iframe.InvokeVoid("setAttribute", "src", "https://www.youtube.com/embed/kZCIporjJ70");
+ var iframe = EmbedInterop.CreateElement("iframe");
+ iframe.SetProperty("src", "https://www.youtube.com/embed/kZCIporjJ70");
return new JSObjectControlHandle(iframe);
}
else
{
- // window.createAppButton source is defined in "app.js" file.
- var button = runtime.Invoke("window.createAppButton");
+ var defaultHandle = (JSObjectControlHandle)createDefault();
+
+ _ = JSHost.ImportAsync("embed.js", "./embed.js").ContinueWith(_ =>
+ {
+ EmbedInterop.AddAppButton(defaultHandle.Object);
+ });
- return new JSObjectControlHandle(button);
+ return defaultHandle;
}
}
}
+
+internal static partial class EmbedInterop
+{
+ [JSImport("globalThis.document.createElement")]
+ public static partial JSObject CreateElement(string tagName);
+
+ [JSImport("addAppButton", "embed.js")]
+ public static partial void AddAppButton(JSObject parentObject);
+}
diff --git a/src/Web/Avalonia.Web.Sample/Logo.svg b/samples/ControlCatalog.Web/Logo.svg
similarity index 100%
rename from src/Web/Avalonia.Web.Sample/Logo.svg
rename to samples/ControlCatalog.Web/Logo.svg
diff --git a/samples/ControlCatalog.Web/Program.cs b/samples/ControlCatalog.Web/Program.cs
index d1a7925813..7d05c8e462 100644
--- a/samples/ControlCatalog.Web/Program.cs
+++ b/samples/ControlCatalog.Web/Program.cs
@@ -1,29 +1,22 @@
-using System;
-using System.Net.Http;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
-using Microsoft.Extensions.DependencyInjection;
+using System.Runtime.Versioning;
+using Avalonia;
+using Avalonia.Web;
+using ControlCatalog;
using ControlCatalog.Web;
-public class Program
+[assembly:SupportedOSPlatform("browser")]
+
+internal partial class Program
{
- public static async Task Main(string[] args)
+ private static void Main(string[] args)
{
- await CreateHostBuilder(args).Build().RunAsync();
+ BuildAvaloniaApp()
+ .AfterSetup(_ =>
+ {
+ ControlCatalog.Pages.EmbedSample.Implementation = new EmbedSampleWeb();
+ }).SetupBrowserApp("out");
}
- public static WebAssemblyHostBuilder CreateHostBuilder(string[] args)
- {
- var builder = WebAssemblyHostBuilder.CreateDefault(args);
-
- builder.RootComponents.Add("#app");
-
- builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
-
- return builder;
- }
+ public static AppBuilder BuildAvaloniaApp()
+ => AppBuilder.Configure();
}
-
-
-
-
diff --git a/samples/ControlCatalog.Web/Roots.xml b/samples/ControlCatalog.Web/Roots.xml
new file mode 100644
index 0000000000..b07fd86fa2
--- /dev/null
+++ b/samples/ControlCatalog.Web/Roots.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/src/Web/Avalonia.Web.Sample/app.css b/samples/ControlCatalog.Web/app.css
similarity index 52%
rename from src/Web/Avalonia.Web.Sample/app.css
rename to samples/ControlCatalog.Web/app.css
index 04ea5bee19..27680f6e1a 100644
--- a/src/Web/Avalonia.Web.Sample/app.css
+++ b/samples/ControlCatalog.Web/app.css
@@ -4,12 +4,15 @@
}
#avalonia-splash {
- position: absolute;
+ position: relative;
height: 100%;
width: 100%;
color: whitesmoke;
background: #171C2C;
font-family: 'Nunito', sans-serif;
+ background-position: center;
+ background-size: cover;
+ background-repeat: no-repeat;
}
#avalonia-splash a{
@@ -24,15 +27,23 @@
}
.splash-close {
- animation: fadeOut 1s forwards;
+ animation: slide 0.5s linear 1s forwards;
}
-@keyframes fadeOut {
- from {
- opacity: 1;
+@keyframes slide {
+ 0% {
+ top: 0%;
}
- to {
+ 50% {
+ opacity: 80%;
+ }
+
+ 100% {
+ top: 100%;
+ overflow: hidden;
opacity: 0;
+ display: none;
+ visibility: collapse;
}
}
diff --git a/src/Web/Avalonia.Web.Sample/embed.js b/samples/ControlCatalog.Web/embed.js
similarity index 100%
rename from src/Web/Avalonia.Web.Sample/embed.js
rename to samples/ControlCatalog.Web/embed.js
diff --git a/src/Web/Avalonia.Web.Sample/favicon.ico b/samples/ControlCatalog.Web/favicon.ico
similarity index 100%
rename from src/Web/Avalonia.Web.Sample/favicon.ico
rename to samples/ControlCatalog.Web/favicon.ico
diff --git a/src/Web/Avalonia.Web.Sample/index.html b/samples/ControlCatalog.Web/index.html
similarity index 95%
rename from src/Web/Avalonia.Web.Sample/index.html
rename to samples/ControlCatalog.Web/index.html
index ee023790fb..226ae70695 100644
--- a/src/Web/Avalonia.Web.Sample/index.html
+++ b/samples/ControlCatalog.Web/index.html
@@ -4,7 +4,7 @@
- Avalonia.Web.Sample
+ AvaloniaUI - ControlCatalog
diff --git a/src/Web/Avalonia.Web.Sample/main.js b/samples/ControlCatalog.Web/main.js
similarity index 84%
rename from src/Web/Avalonia.Web.Sample/main.js
rename to samples/ControlCatalog.Web/main.js
index 3683aea181..87f8a4f943 100644
--- a/src/Web/Avalonia.Web.Sample/main.js
+++ b/samples/ControlCatalog.Web/main.js
@@ -2,7 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
import { dotnet } from './dotnet.js'
-import { createAvaloniaRuntime } from './avalonia.js';
+import { registerAvaloniaModule } from './avalonia.js';
const is_browser = typeof window != "undefined";
if (!is_browser) throw new Error(`Expected to be running in a browser`);
@@ -12,7 +12,7 @@ const dotnetRuntime = await dotnet
.withApplicationArgumentsFromQuery()
.create();
-await createAvaloniaRuntime(dotnetRuntime);
+await registerAvaloniaModule(dotnetRuntime);
const config = dotnetRuntime.getConfig();
diff --git a/src/Web/Avalonia.Web.Sample/runtimeconfig.template.json b/samples/ControlCatalog.Web/runtimeconfig.template.json
similarity index 100%
rename from src/Web/Avalonia.Web.Sample/runtimeconfig.template.json
rename to samples/ControlCatalog.Web/runtimeconfig.template.json
diff --git a/samples/ControlCatalog/Pages/ColorPickerPage.xaml b/samples/ControlCatalog/Pages/ColorPickerPage.xaml
index ad54eb95fc..69ceaea328 100644
--- a/samples/ControlCatalog/Pages/ColorPickerPage.xaml
+++ b/samples/ControlCatalog/Pages/ColorPickerPage.xaml
@@ -11,16 +11,17 @@
x:Class="ControlCatalog.Pages.ColorPickerPage">
-
-
@@ -56,8 +57,5 @@
IsAccentColorsVisible="False"
HsvColor="{Binding HsvColor, ElementName=ColorSpectrum1}" />
-
-
diff --git a/samples/ControlCatalog/Pages/ColorPickerPage.xaml.cs b/samples/ControlCatalog/Pages/ColorPickerPage.xaml.cs
index 6e017e381f..4671bbdb7c 100644
--- a/samples/ControlCatalog/Pages/ColorPickerPage.xaml.cs
+++ b/samples/ControlCatalog/Pages/ColorPickerPage.xaml.cs
@@ -1,6 +1,8 @@
using Avalonia;
using Avalonia.Controls;
+using Avalonia.Layout;
using Avalonia.Markup.Xaml;
+using Avalonia.Media;
namespace ControlCatalog.Pages
{
@@ -9,6 +11,20 @@ namespace ControlCatalog.Pages
public ColorPickerPage()
{
InitializeComponent();
+
+ var layoutRoot = this.GetControl("LayoutRoot");
+
+ // ColorPicker added from code-behind
+ var colorPicker = new ColorPicker()
+ {
+ Color = Colors.Blue,
+ Margin = new Thickness(0, 50, 0, 0),
+ HorizontalAlignment = HorizontalAlignment.Center,
+ };
+ Grid.SetColumn(colorPicker, 2);
+ Grid.SetRow(colorPicker, 1);
+
+ layoutRoot.Children.Add(colorPicker);
}
private void InitializeComponent()
diff --git a/samples/ControlCatalog/Pages/DateTimePickerPage.xaml b/samples/ControlCatalog/Pages/DateTimePickerPage.xaml
index 29cd939520..6217d39b21 100644
--- a/samples/ControlCatalog/Pages/DateTimePickerPage.xaml
+++ b/samples/ControlCatalog/Pages/DateTimePickerPage.xaml
@@ -13,17 +13,17 @@
Margin="16"
HorizontalAlignment="Stretch"
Spacing="16">
- A simple DatePicker with a header
+ A simple DatePicker
-
+
- <DatePicker Header="Pick a date" />
+ <DatePicker/>
@@ -33,7 +33,7 @@
-
+
@@ -79,24 +79,24 @@
-
+
- A TimePicker with a header and minute increments specified.
+ A TimePicker with minute increments specified.
-
+
- <TimePicker Header="Arrival time" MinuteIncrement="15" />
+ <TimePicker MinuteIncrement="15" />
@@ -107,13 +107,13 @@
-
+
- <TimePicker ClockIdentifier="12HourClock" Header="12 hour clock" />
+ <TimePicker ClockIdentifier="12HourClock" />
@@ -124,13 +124,13 @@
-
+
- <TimePicker ClockIdentifier="24HourClock" Header="24 hour clock" />
+ <TimePicker ClockIdentifier="24HourClock" />
diff --git a/samples/ControlCatalog/Pages/ExpanderPage.xaml b/samples/ControlCatalog/Pages/ExpanderPage.xaml
index f9ae1c7a3c..8c8702c665 100644
--- a/samples/ControlCatalog/Pages/ExpanderPage.xaml
+++ b/samples/ControlCatalog/Pages/ExpanderPage.xaml
@@ -32,6 +32,23 @@
Expanded content
+
+
+
+
+
+ Expanded content
+
+
+
+
+ Expanded content
+
+
Rounded
diff --git a/samples/ControlCatalog/Pages/ScreenPage.cs b/samples/ControlCatalog/Pages/ScreenPage.cs
index 823f59e030..ff62b834c4 100644
--- a/samples/ControlCatalog/Pages/ScreenPage.cs
+++ b/samples/ControlCatalog/Pages/ScreenPage.cs
@@ -62,10 +62,10 @@ namespace ControlCatalog.Pages
CreateFormattedText($"WorkArea: {screen.WorkingArea.Width}:{screen.WorkingArea.Height}");
context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height + 20));
- formattedText = CreateFormattedText($"Scaling: {screen.PixelDensity * 100}%");
+ formattedText = CreateFormattedText($"Scaling: {screen.Scaling * 100}%");
context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height + 40));
- formattedText = CreateFormattedText($"Primary: {screen.Primary}");
+ formattedText = CreateFormattedText($"IsPrimary: {screen.IsPrimary}");
context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height + 60));
formattedText =
diff --git a/samples/MobileSandbox.Android/MainActivity.cs b/samples/MobileSandbox.Android/MainActivity.cs
index ac9242dd52..d65f0dec92 100644
--- a/samples/MobileSandbox.Android/MainActivity.cs
+++ b/samples/MobileSandbox.Android/MainActivity.cs
@@ -5,8 +5,8 @@ using Avalonia.Android;
namespace MobileSandbox.Android
{
- [Activity(Label = "MobileSandbox.Android", Theme = "@style/MyTheme.NoActionBar", Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleInstance, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)]
- public class MainActivity : AvaloniaActivity
+ [Activity(Label = "MobileSandbox.Android", Theme = "@style/MyTheme.NoActionBar", Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleTop, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)]
+ public class MainActivity : AvaloniaMainActivity
{
}
}
diff --git a/samples/MobileSandbox.Android/SplashActivity.cs b/samples/MobileSandbox.Android/SplashActivity.cs
index c26371d6fe..ced092554d 100644
--- a/samples/MobileSandbox.Android/SplashActivity.cs
+++ b/samples/MobileSandbox.Android/SplashActivity.cs
@@ -1,11 +1,11 @@
using Android.App;
using Android.Content;
-using Android.OS;
+using Avalonia.Android;
namespace MobileSandbox.Android
{
[Activity(Theme = "@style/MyTheme.Splash", MainLauncher = true, NoHistory = true)]
- public class SplashActivity : Activity
+ public class SplashActivity : AvaloniaSplashActivity
{
protected override void OnResume()
{
diff --git a/samples/RenderDemo/Pages/GlyphRunPage.xaml.cs b/samples/RenderDemo/Pages/GlyphRunPage.xaml.cs
index 674ed8e61f..5c31e138e0 100644
--- a/samples/RenderDemo/Pages/GlyphRunPage.xaml.cs
+++ b/samples/RenderDemo/Pages/GlyphRunPage.xaml.cs
@@ -22,7 +22,7 @@ namespace RenderDemo.Pages
public class GlyphRunControl : Control
{
- private GlyphTypeface _glyphTypeface = Typeface.Default.GlyphTypeface;
+ private IGlyphTypeface _glyphTypeface = Typeface.Default.GlyphTypeface;
private readonly Random _rand = new Random();
private ushort[] _glyphIndices = new ushort[1];
private char[] _characters = new char[1];
@@ -81,7 +81,7 @@ namespace RenderDemo.Pages
public class GlyphRunGeometryControl : Control
{
- private GlyphTypeface _glyphTypeface = Typeface.Default.GlyphTypeface;
+ private IGlyphTypeface _glyphTypeface = Typeface.Default.GlyphTypeface;
private readonly Random _rand = new Random();
private ushort[] _glyphIndices = new ushort[1];
private char[] _characters = new char[1];
diff --git a/src/Android/Avalonia.Android/AvaloniaActivity.cs b/src/Android/Avalonia.Android/AvaloniaActivity.cs
deleted file mode 100644
index 4ee4bc1375..0000000000
--- a/src/Android/Avalonia.Android/AvaloniaActivity.cs
+++ /dev/null
@@ -1,105 +0,0 @@
-using Android.OS;
-using AndroidX.AppCompat.App;
-using Android.Content.Res;
-using AndroidX.Lifecycle;
-using Avalonia.Controls.ApplicationLifetimes;
-using Avalonia.Controls;
-using Android.Runtime;
-using Android.App;
-using Android.Content;
-using System;
-
-namespace Avalonia.Android
-{
- public abstract class AvaloniaActivity : AppCompatActivity
- {
- internal class SingleViewLifetime : ISingleViewApplicationLifetime
- {
- public AvaloniaView View { get; internal set; }
-
- public Control MainView
- {
- get => (Control)View.Content;
- set => View.Content = value;
- }
- }
-
- internal Action ActivityResult;
- internal AvaloniaView View;
- internal AvaloniaViewModel _viewModel;
-
- protected abstract AppBuilder CreateAppBuilder();
-
- protected override void OnCreate(Bundle savedInstanceState)
- {
- var builder = CreateAppBuilder();
-
-
- var lifetime = new SingleViewLifetime();
-
- builder.AfterSetup(x =>
- {
- _viewModel = new ViewModelProvider(this).Get(Java.Lang.Class.FromType(typeof(AvaloniaViewModel))) as AvaloniaViewModel;
-
- View = new AvaloniaView(this);
- if (_viewModel.Content != null)
- {
- View.Content = _viewModel.Content;
- }
-
- SetContentView(View);
- lifetime.View = View;
-
- View.Prepare();
- });
-
- builder.SetupWithLifetime(lifetime);
-
- base.OnCreate(savedInstanceState);
- }
- public object Content
- {
- get
- {
- return _viewModel.Content;
- }
- set
- {
- _viewModel.Content = value;
- if (View != null)
- View.Content = value;
- }
- }
-
- public override void OnConfigurationChanged(Configuration newConfig)
- {
- base.OnConfigurationChanged(newConfig);
- }
-
- protected override void OnDestroy()
- {
- View.Content = null;
-
- base.OnDestroy();
- }
-
- protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data)
- {
- base.OnActivityResult(requestCode, resultCode, data);
-
- ActivityResult?.Invoke(requestCode, resultCode, data);
- }
- }
-
- public abstract class AvaloniaActivity : AvaloniaActivity where TApp : Application, new()
- {
- protected virtual AppBuilder CustomizeAppBuilder(AppBuilder builder) => builder.UseAndroid();
-
- protected override AppBuilder CreateAppBuilder()
- {
- var builder = AppBuilder.Configure();
-
- return CustomizeAppBuilder(builder);
- }
- }
-}
diff --git a/src/Android/Avalonia.Android/AvaloniaMainActivity.cs b/src/Android/Avalonia.Android/AvaloniaMainActivity.cs
new file mode 100644
index 0000000000..de8d02f188
--- /dev/null
+++ b/src/Android/Avalonia.Android/AvaloniaMainActivity.cs
@@ -0,0 +1,70 @@
+using System;
+using Android.App;
+using Android.Content;
+using Android.Content.Res;
+using Android.OS;
+using Android.Runtime;
+using AndroidX.AppCompat.App;
+using AndroidX.Lifecycle;
+
+namespace Avalonia.Android
+{
+ public abstract class AvaloniaMainActivity : AppCompatActivity, IActivityResultHandler
+ {
+ internal static object ViewContent;
+
+ public Action ActivityResult { get; set; }
+ internal AvaloniaView View;
+
+ protected override void OnCreate(Bundle savedInstanceState)
+ {
+ View = new AvaloniaView(this);
+ if (ViewContent != null)
+ {
+ View.Content = ViewContent;
+ }
+
+ if (Avalonia.Application.Current.ApplicationLifetime is SingleViewLifetime lifetime)
+ {
+ lifetime.View = View;
+ }
+
+ base.OnCreate(savedInstanceState);
+
+ SetContentView(View);
+ }
+
+ public object Content
+ {
+ get
+ {
+ return ViewContent;
+ }
+ set
+ {
+ ViewContent = value;
+ if (View != null)
+ View.Content = value;
+ }
+ }
+
+ public override void OnConfigurationChanged(Configuration newConfig)
+ {
+ base.OnConfigurationChanged(newConfig);
+ }
+
+ protected override void OnDestroy()
+ {
+ View.Content = null;
+
+ base.OnDestroy();
+ }
+
+ protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data)
+ {
+ base.OnActivityResult(requestCode, resultCode, data);
+
+ ActivityResult?.Invoke(requestCode, resultCode, data);
+ }
+ }
+}
diff --git a/src/Android/Avalonia.Android/AvaloniaSplashActivity.cs b/src/Android/Avalonia.Android/AvaloniaSplashActivity.cs
new file mode 100644
index 0000000000..5b5ebd1bd9
--- /dev/null
+++ b/src/Android/Avalonia.Android/AvaloniaSplashActivity.cs
@@ -0,0 +1,34 @@
+using Android.OS;
+using AndroidX.AppCompat.App;
+using AndroidX.Lifecycle;
+
+namespace Avalonia.Android
+{
+ public abstract class AvaloniaSplashActivity : AppCompatActivity
+ {
+ protected abstract AppBuilder CreateAppBuilder();
+
+ protected override void OnCreate(Bundle? savedInstanceState)
+ {
+ base.OnCreate(savedInstanceState);
+
+ var builder = CreateAppBuilder();
+
+ var lifetime = new SingleViewLifetime();
+
+ builder.SetupWithLifetime(lifetime);
+ }
+ }
+
+ public abstract class AvaloniaSplashActivity : AvaloniaSplashActivity where TApp : Application, new()
+ {
+ protected virtual AppBuilder CustomizeAppBuilder(AppBuilder builder) => builder.UseAndroid();
+
+ protected override AppBuilder CreateAppBuilder()
+ {
+ var builder = AppBuilder.Configure();
+
+ return CustomizeAppBuilder(builder);
+ }
+ }
+}
diff --git a/src/Android/Avalonia.Android/AvaloniaView.cs b/src/Android/Avalonia.Android/AvaloniaView.cs
index 94e863210b..5267843bfc 100644
--- a/src/Android/Avalonia.Android/AvaloniaView.cs
+++ b/src/Android/Avalonia.Android/AvaloniaView.cs
@@ -21,10 +21,7 @@ namespace Avalonia.Android
{
_view = new ViewImpl(this);
AddView(_view.View);
- }
- internal void Prepare ()
- {
_root = new EmbeddableControlRoot(_view);
_root.Prepare();
}
diff --git a/src/Android/Avalonia.Android/AvaloniaViewModel.cs b/src/Android/Avalonia.Android/AvaloniaViewModel.cs
deleted file mode 100644
index 1b2c00987a..0000000000
--- a/src/Android/Avalonia.Android/AvaloniaViewModel.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace Avalonia.Android
-{
- internal class AvaloniaViewModel : AndroidX.Lifecycle.ViewModel
- {
- public object Content { get; set; }
- }
-}
diff --git a/src/Android/Avalonia.Android/IActivityResultHandler.cs b/src/Android/Avalonia.Android/IActivityResultHandler.cs
new file mode 100644
index 0000000000..14094ee185
--- /dev/null
+++ b/src/Android/Avalonia.Android/IActivityResultHandler.cs
@@ -0,0 +1,11 @@
+using System;
+using Android.App;
+using Android.Content;
+
+namespace Avalonia.Android
+{
+ public interface IActivityResultHandler
+ {
+ public Action ActivityResult { get; set; }
+ }
+}
diff --git a/src/Android/Avalonia.Android/OpenGL/GlPlatformSurface.cs b/src/Android/Avalonia.Android/OpenGL/GlPlatformSurface.cs
index a9710039f8..e85ed11028 100644
--- a/src/Android/Avalonia.Android/OpenGL/GlPlatformSurface.cs
+++ b/src/Android/Avalonia.Android/OpenGL/GlPlatformSurface.cs
@@ -1,4 +1,5 @@
-using Avalonia.OpenGL.Egl;
+using Avalonia.OpenGL;
+using Avalonia.OpenGL.Egl;
using Avalonia.OpenGL.Surfaces;
namespace Avalonia.Android.OpenGL
@@ -19,7 +20,8 @@ namespace Avalonia.Android.OpenGL
public static GlPlatformSurface TryCreate(IEglWindowGlPlatformSurfaceInfo info)
{
- if (EglPlatformOpenGlInterface.TryCreate() is EglPlatformOpenGlInterface egl)
+ var feature = AvaloniaLocator.Current.GetService();
+ if (feature is EglPlatformOpenGlInterface egl)
{
return new GlPlatformSurface(egl, info);
}
diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
index f8eaeba897..984eb775b5 100644
--- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
+++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
@@ -1,22 +1,16 @@
using System;
using System.Collections.Generic;
-
+using Android.App;
using Android.Content;
using Android.Graphics;
-using Android.Media.TV;
-using Android.OS;
using Android.Runtime;
-using Android.Text;
using Android.Views;
using Android.Views.InputMethods;
-using Android.Widget;
using Avalonia.Android.OpenGL;
-using Avalonia.Android.Platform.Input;
using Avalonia.Android.Platform.Specific;
using Avalonia.Android.Platform.Specific.Helpers;
using Avalonia.Android.Platform.Storage;
using Avalonia.Controls;
-using Avalonia.Controls.Documents;
using Avalonia.Controls.Platform;
using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Input;
@@ -29,7 +23,6 @@ using Avalonia.Platform.Storage;
using Avalonia.Rendering;
using Avalonia.Rendering.Composition;
using Java.Lang;
-using static System.Net.Mime.MediaTypeNames;
namespace Avalonia.Android.Platform.SkiaPlatform
{
@@ -59,7 +52,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
_view.Resources.DisplayMetrics.HeightPixels).ToSize(RenderScaling);
NativeControlHost = new AndroidNativeControlHostImpl(avaloniaView);
- StorageProvider = new AndroidStorageProvider((AvaloniaActivity)avaloniaView.Context);
+ StorageProvider = new AndroidStorageProvider((Activity)avaloniaView.Context);
}
public virtual Point GetAvaloniaPointFromEvent(MotionEvent e, int pointerIndex) =>
@@ -301,7 +294,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
IsComposing = true;
- _inputMethod.Client.SetPreeditText(ComposingText);
+ _inputMethod.Client?.SetPreeditText(ComposingText);
return base.SetComposingText(text, newCursorPosition);
}
diff --git a/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidMotionEventsHelper.cs b/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidMotionEventsHelper.cs
index ce385ebe34..6d0e6be0ad 100644
--- a/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidMotionEventsHelper.cs
+++ b/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidMotionEventsHelper.cs
@@ -15,7 +15,7 @@ namespace Avalonia.Android.Platform.Specific.Helpers
internal class AndroidMotionEventsHelper : IDisposable
{
private static readonly PooledList s_intermediatePointsPooledList = new(ClearMode.Never);
- private static readonly float s_radiansToDegree = (float)(180f * Math.PI);
+ private const float RadiansToDegree = (float)(180f * Math.PI);
private readonly TouchDevice _touchDevice;
private readonly MouseDevice _mouseDevice;
private readonly PenDevice _penDevice;
@@ -223,7 +223,7 @@ namespace Avalonia.Android.Platform.Specific.Helpers
{
Position = new Point(e.GetX(index), e.GetY(index)) / _view.RenderScaling,
Pressure = Math.Min(e.GetPressure(index), 1), // android pressure can depend on the device, can be mixed up with "GetSize", may be larger than 1.0f on some devices
- Twist = e.GetOrientation(index) * s_radiansToDegree
+ Twist = e.GetOrientation(index) * RadiansToDegree
};
}
@@ -233,7 +233,7 @@ namespace Avalonia.Android.Platform.Specific.Helpers
{
Position = new Point(e.GetHistoricalX(index, pos), e.GetHistoricalY(index, pos)) / _view.RenderScaling,
Pressure = Math.Min(e.GetHistoricalPressure(index, pos), 1),
- Twist = e.GetHistoricalOrientation(index, pos) * s_radiansToDegree
+ Twist = e.GetHistoricalOrientation(index, pos) * RadiansToDegree
};
}
diff --git a/src/Android/Avalonia.Android/Platform/Storage/AndroidStorageProvider.cs b/src/Android/Avalonia.Android/Platform/Storage/AndroidStorageProvider.cs
index 653f450ec8..62e43ff2ef 100644
--- a/src/Android/Avalonia.Android/Platform/Storage/AndroidStorageProvider.cs
+++ b/src/Android/Avalonia.Android/Platform/Storage/AndroidStorageProvider.cs
@@ -14,10 +14,10 @@ namespace Avalonia.Android.Platform.Storage;
internal class AndroidStorageProvider : IStorageProvider
{
- private readonly AvaloniaActivity _activity;
+ private readonly Activity _activity;
private int _lastRequestCode = 20000;
- public AndroidStorageProvider(AvaloniaActivity activity)
+ public AndroidStorageProvider(Activity activity)
{
_activity = activity;
}
@@ -119,7 +119,10 @@ internal class AndroidStorageProvider : IStorageProvider
var tcs = new TaskCompletionSource();
var currentRequestCode = _lastRequestCode++;
- _activity.ActivityResult += OnActivityResult;
+ if (_activity is IActivityResultHandler mainActivity)
+ {
+ mainActivity.ActivityResult += OnActivityResult;
+ }
_activity.StartActivityForResult(pickerIntent, currentRequestCode);
var result = await tcs.Task;
@@ -158,7 +161,11 @@ internal class AndroidStorageProvider : IStorageProvider
return;
}
- _activity.ActivityResult -= OnActivityResult;
+
+ if (_activity is IActivityResultHandler mainActivity)
+ {
+ mainActivity.ActivityResult -= OnActivityResult;
+ }
_ = tcs.TrySetResult(resultCode == Result.Ok ? data : null);
}
diff --git a/src/Android/Avalonia.Android/SingleViewLifetime.cs b/src/Android/Avalonia.Android/SingleViewLifetime.cs
new file mode 100644
index 0000000000..eef763a932
--- /dev/null
+++ b/src/Android/Avalonia.Android/SingleViewLifetime.cs
@@ -0,0 +1,26 @@
+using Avalonia.Controls;
+using Avalonia.Controls.ApplicationLifetimes;
+
+namespace Avalonia.Android
+{
+ internal class SingleViewLifetime : ISingleViewApplicationLifetime
+ {
+ private AvaloniaView _view;
+
+ public AvaloniaView View
+ {
+ get => _view; internal set
+ {
+ if (_view != null)
+ {
+ _view.Content = null;
+ _view.Dispose();
+ }
+ _view = value;
+ _view.Content = MainView;
+ }
+ }
+
+ public Control MainView { get; set; }
+ }
+}
diff --git a/src/Avalonia.Base/Avalonia.Base.csproj b/src/Avalonia.Base/Avalonia.Base.csproj
index 1cb29e4e37..bdb5882ef2 100644
--- a/src/Avalonia.Base/Avalonia.Base.csproj
+++ b/src/Avalonia.Base/Avalonia.Base.csproj
@@ -21,6 +21,10 @@
+
+
+
+
@@ -38,11 +42,12 @@
-
+
+
diff --git a/src/Avalonia.Base/AvaloniaProperty.cs b/src/Avalonia.Base/AvaloniaProperty.cs
index fd43ced196..46ba4082fb 100644
--- a/src/Avalonia.Base/AvaloniaProperty.cs
+++ b/src/Avalonia.Base/AvaloniaProperty.cs
@@ -41,7 +41,7 @@ namespace Avalonia
{
_ = name ?? throw new ArgumentNullException(nameof(name));
- if (name.Contains("."))
+ if (name.Contains('.'))
{
throw new ArgumentException("'name' may not contain periods.");
}
diff --git a/src/Avalonia.Base/AvaloniaPropertyRegistry.cs b/src/Avalonia.Base/AvaloniaPropertyRegistry.cs
index d86e723b38..62265d3c59 100644
--- a/src/Avalonia.Base/AvaloniaPropertyRegistry.cs
+++ b/src/Avalonia.Base/AvaloniaPropertyRegistry.cs
@@ -228,7 +228,7 @@ namespace Avalonia
_ = type ?? throw new ArgumentNullException(nameof(type));
_ = name ?? throw new ArgumentNullException(nameof(name));
- if (name.Contains("."))
+ if (name.Contains('.'))
{
throw new InvalidOperationException("Attached properties not supported.");
}
diff --git a/src/Avalonia.Base/Input/Cursor.cs b/src/Avalonia.Base/Input/Cursor.cs
index 98c4258a90..8e79206f93 100644
--- a/src/Avalonia.Base/Input/Cursor.cs
+++ b/src/Avalonia.Base/Input/Cursor.cs
@@ -33,7 +33,7 @@ namespace Avalonia.Input
DragLink,
None,
- // Not available in GTK directly, see http://www.pixelbeat.org/programming/x_cursors/
+ // Not available in GTK directly, see https://www.pixelbeat.org/programming/x_cursors/
// We might enable them later, preferably, by loading pixmax directly from theme with fallback image
// SizeNorthWestSouthEast,
// SizeNorthEastSouthWest,
diff --git a/src/Avalonia.Base/Input/DragDrop.cs b/src/Avalonia.Base/Input/DragDrop.cs
index 723d577964..fe75f678e1 100644
--- a/src/Avalonia.Base/Input/DragDrop.cs
+++ b/src/Avalonia.Base/Input/DragDrop.cs
@@ -13,7 +13,7 @@ namespace Avalonia.Input
///
/// Event which is raised, when a drag-and-drop operation leaves the element.
///
- public static readonly RoutedEvent DragLeaveEvent = RoutedEvent.Register("DragLeave", RoutingStrategies.Bubble, typeof(DragDrop));
+ public static readonly RoutedEvent DragLeaveEvent = RoutedEvent.Register("DragLeave", RoutingStrategies.Bubble, typeof(DragDrop));
///
/// Event which is raised, when a drag-and-drop operation is updated while over the element.
///
diff --git a/src/Avalonia.Base/Input/DragDropDevice.cs b/src/Avalonia.Base/Input/DragDropDevice.cs
index 30a08eda17..3c91856dcd 100644
--- a/src/Avalonia.Base/Input/DragDropDevice.cs
+++ b/src/Avalonia.Base/Input/DragDropDevice.cs
@@ -54,7 +54,7 @@ namespace Avalonia.Input
try
{
if (_lastTarget != null)
- _lastTarget.RaiseEvent(new RoutedEventArgs(DragDrop.DragLeaveEvent));
+ RaiseDragEvent(_lastTarget, inputRoot, point, DragDrop.DragLeaveEvent, effects, data, modifiers);
return RaiseDragEvent(target, inputRoot, point, DragDrop.DragEnterEvent, effects, data, modifiers);
}
finally
@@ -63,13 +63,13 @@ namespace Avalonia.Input
}
}
- private void DragLeave(IInputElement inputRoot)
+ private void DragLeave(IInputRoot inputRoot, Point point, IDataObject data, DragDropEffects effects, KeyModifiers modifiers)
{
if (_lastTarget == null)
return;
try
{
- _lastTarget.RaiseEvent(new RoutedEventArgs(DragDrop.DragLeaveEvent));
+ RaiseDragEvent(_lastTarget, inputRoot, point, DragDrop.DragLeaveEvent, effects, data, modifiers);
}
finally
{
@@ -106,7 +106,7 @@ namespace Avalonia.Input
e.Effects = DragOver(e.Root, e.Location, e.Data, e.Effects, e.KeyModifiers);
break;
case RawDragEventType.DragLeave:
- DragLeave(e.Root);
+ DragLeave(e.Root, e.Location, e.Data, e.Effects, e.KeyModifiers);
break;
case RawDragEventType.Drop:
e.Effects = Drop(e.Root, e.Location, e.Data, e.Effects, e.KeyModifiers);
diff --git a/src/Avalonia.Base/Media/Color.cs b/src/Avalonia.Base/Media/Color.cs
index cb90404f6d..5470a735b3 100644
--- a/src/Avalonia.Base/Media/Color.cs
+++ b/src/Avalonia.Base/Media/Color.cs
@@ -9,6 +9,7 @@ using System;
using System.Globalization;
#if !BUILDTASK
using Avalonia.Animation.Animators;
+using static Avalonia.Utilities.SpanHelpers;
#endif
namespace Avalonia.Media
@@ -295,9 +296,7 @@ namespace Avalonia.Media
return false;
}
- // TODO: (netstandard 2.1) Can use allocation free parsing.
- if (!uint.TryParse(input.ToString(), NumberStyles.HexNumber, CultureInfo.InvariantCulture,
- out var parsed))
+ if (!input.TryParseUInt(NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var parsed))
{
return false;
}
@@ -382,9 +381,9 @@ namespace Avalonia.Media
if (components.Length == 3) // RGB
{
- if (InternalTryParseByte(components[0], out byte red) &&
- InternalTryParseByte(components[1], out byte green) &&
- InternalTryParseByte(components[2], out byte blue))
+ if (InternalTryParseByte(components[0].AsSpan(), out byte red) &&
+ InternalTryParseByte(components[1].AsSpan(), out byte green) &&
+ InternalTryParseByte(components[2].AsSpan(), out byte blue))
{
color = new Color(0xFF, red, green, blue);
return true;
@@ -392,10 +391,10 @@ namespace Avalonia.Media
}
else if (components.Length == 4) // RGBA
{
- if (InternalTryParseByte(components[0], out byte red) &&
- InternalTryParseByte(components[1], out byte green) &&
- InternalTryParseByte(components[2], out byte blue) &&
- InternalTryParseDouble(components[3], out double alpha))
+ if (InternalTryParseByte(components[0].AsSpan(), out byte red) &&
+ InternalTryParseByte(components[1].AsSpan(), out byte green) &&
+ InternalTryParseByte(components[2].AsSpan(), out byte blue) &&
+ InternalTryParseDouble(components[3].AsSpan(), out double alpha))
{
color = new Color((byte)Math.Round(alpha * 255.0), red, green, blue);
return true;
@@ -403,17 +402,14 @@ namespace Avalonia.Media
}
// Local function to specially parse a byte value with an optional percentage sign
- bool InternalTryParseByte(string inString, out byte outByte)
+ bool InternalTryParseByte(ReadOnlySpan inString, out byte outByte)
{
// The percent sign, if it exists, must be at the end of the number
- int percentIndex = inString.IndexOf("%", StringComparison.Ordinal);
+ int percentIndex = inString.IndexOf("%".AsSpan(), StringComparison.Ordinal);
if (percentIndex >= 0)
{
- var result = double.TryParse(
- inString.Substring(0, percentIndex),
- NumberStyles.Number,
- CultureInfo.InvariantCulture,
+ var result = inString.Slice(0, percentIndex).TryParseDouble(NumberStyles.Number, CultureInfo.InvariantCulture,
out double percentage);
outByte = (byte)Math.Round((percentage / 100.0) * 255.0);
@@ -421,37 +417,28 @@ namespace Avalonia.Media
}
else
{
- return byte.TryParse(
- inString,
- NumberStyles.Number,
- CultureInfo.InvariantCulture,
+ return inString.TryParseByte(NumberStyles.Number, CultureInfo.InvariantCulture,
out outByte);
}
}
// Local function to specially parse a double value with an optional percentage sign
- bool InternalTryParseDouble(string inString, out double outDouble)
+ bool InternalTryParseDouble(ReadOnlySpan inString, out double outDouble)
{
// The percent sign, if it exists, must be at the end of the number
- int percentIndex = inString.IndexOf("%", StringComparison.Ordinal);
+ int percentIndex = inString.IndexOf("%".AsSpan(), StringComparison.Ordinal);
if (percentIndex >= 0)
{
- var result = double.TryParse(
- inString.Substring(0, percentIndex),
- NumberStyles.Number,
- CultureInfo.InvariantCulture,
- out double percentage);
+ var result = inString.Slice(0, percentIndex).TryParseDouble(NumberStyles.Number, CultureInfo.InvariantCulture,
+ out double percentage);
outDouble = percentage / 100.0;
return result;
}
else
{
- return double.TryParse(
- inString,
- NumberStyles.Number,
- CultureInfo.InvariantCulture,
+ return inString.TryParseDouble(NumberStyles.Number, CultureInfo.InvariantCulture,
out outDouble);
}
}
diff --git a/src/Avalonia.Base/Media/FontManager.cs b/src/Avalonia.Base/Media/FontManager.cs
index 37091b82e3..d92d003c2a 100644
--- a/src/Avalonia.Base/Media/FontManager.cs
+++ b/src/Avalonia.Base/Media/FontManager.cs
@@ -13,8 +13,8 @@ namespace Avalonia.Media
///
public sealed class FontManager
{
- private readonly ConcurrentDictionary _glyphTypefaceCache =
- new ConcurrentDictionary();
+ private readonly ConcurrentDictionary _glyphTypefaceCache =
+ new ConcurrentDictionary();
private readonly FontFamily _defaultFontFamily;
private readonly IReadOnlyList? _fontFallbacks;
@@ -81,13 +81,13 @@ namespace Avalonia.Media
PlatformImpl.GetInstalledFontFamilyNames(checkForUpdates);
///
- /// Returns a new , or an existing one if a matching exists.
+ /// Returns a new , or an existing one if a matching exists.
///
/// The typeface.
///
- /// The .
+ /// The .
///
- public GlyphTypeface GetOrAddGlyphTypeface(Typeface typeface)
+ public IGlyphTypeface GetOrAddGlyphTypeface(Typeface typeface)
{
while (true)
{
@@ -96,7 +96,7 @@ namespace Avalonia.Media
return glyphTypeface;
}
- glyphTypeface = new GlyphTypeface(typeface);
+ glyphTypeface = PlatformImpl.CreateGlyphTypeface(typeface);
if (_glyphTypefaceCache.TryAdd(typeface, glyphTypeface))
{
diff --git a/src/Avalonia.Base/Media/FontMetrics.cs b/src/Avalonia.Base/Media/FontMetrics.cs
new file mode 100644
index 0000000000..1cd01675db
--- /dev/null
+++ b/src/Avalonia.Base/Media/FontMetrics.cs
@@ -0,0 +1,58 @@
+namespace Avalonia.Media
+{
+ ///
+ /// The font metrics is holding information about a font's ascent, descent, etc. in design em units.
+ ///
+ public readonly struct FontMetrics
+ {
+ ///
+ /// Gets the font design units per em.
+ ///
+ public short DesignEmHeight { get; init; }
+
+ ///
+ /// A value indicating whether all glyphs in the font have the same advancement.
+ ///
+ public bool IsFixedPitch { get; init; }
+
+ ///
+ /// Gets the recommended distance above the baseline in design em size.
+ ///
+ public int Ascent { get; init; }
+
+ ///
+ /// Gets the recommended distance under the baseline in design em size.
+ ///
+ public int Descent { get; init; }
+
+ ///
+ /// Gets the recommended additional space between two lines of text in design em size.
+ ///
+ public int LineGap { get; init; }
+
+ ///
+ /// Gets the recommended line spacing of a formed text line.
+ ///
+ public int LineSpacing => Descent - Ascent + LineGap;
+
+ ///
+ /// Gets a value that indicates the distance of the underline from the baseline in design em size.
+ ///
+ public int UnderlinePosition { get; init; }
+
+ ///
+ /// Gets a value that indicates the thickness of the underline in design em size.
+ ///
+ public int UnderlineThickness { get; init; }
+
+ ///
+ /// Gets a value that indicates the distance of the strikethrough from the baseline in design em size.
+ ///
+ public int StrikethroughPosition { get; init; }
+
+ ///
+ /// Gets a value that indicates the thickness of the underline in design em size.
+ ///
+ public int StrikethroughThickness { get; init; }
+ }
+}
diff --git a/src/Avalonia.Base/Media/FontSimulations.cs b/src/Avalonia.Base/Media/FontSimulations.cs
new file mode 100644
index 0000000000..2faf53f1d8
--- /dev/null
+++ b/src/Avalonia.Base/Media/FontSimulations.cs
@@ -0,0 +1,27 @@
+using System;
+
+namespace Avalonia.Media
+{
+ ///
+ /// Specifies algorithmic style simulations to be applied to the typeface.
+ /// Bold and oblique simulations can be combined via bitwise OR operation.
+ ///
+ [Flags]
+ public enum FontSimulations : byte
+ {
+ ///
+ /// No simulations are performed.
+ ///
+ None = 0x0000,
+
+ ///
+ /// Algorithmic emboldening is performed.
+ ///
+ Bold = 0x0001,
+
+ ///
+ /// Algorithmic italicization is performed.
+ ///
+ Oblique = 0x0002
+ }
+}
diff --git a/src/Avalonia.Base/Media/GlyphMetrics.cs b/src/Avalonia.Base/Media/GlyphMetrics.cs
new file mode 100644
index 0000000000..2ee1f87d38
--- /dev/null
+++ b/src/Avalonia.Base/Media/GlyphMetrics.cs
@@ -0,0 +1,24 @@
+namespace Avalonia.Media;
+
+public readonly struct GlyphMetrics
+{
+ ///
+ /// Distance from the x-origin to the left extremum of the glyph.
+ ///
+ public int XBearing { get; init; }
+
+ ///
+ /// Distance from the top extremum of the glyph to the y-origin.
+ ///
+ public int YBearing{ get; init; }
+
+ ///
+ /// Distance from the left extremum of the glyph to the right extremum.
+ ///
+ public int Width{ get; init; }
+
+ ///
+ /// Distance from the top extremum of the glyph to the bottom extremum.
+ ///
+ public int Height{ get; init; }
+}
diff --git a/src/Avalonia.Base/Media/GlyphRun.cs b/src/Avalonia.Base/Media/GlyphRun.cs
index 2289f98228..a1cb00e209 100644
--- a/src/Avalonia.Base/Media/GlyphRun.cs
+++ b/src/Avalonia.Base/Media/GlyphRun.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Drawing;
using Avalonia.Media.TextFormatting.Unicode;
using Avalonia.Platform;
using Avalonia.Utilities;
@@ -15,7 +16,7 @@ namespace Avalonia.Media
private static readonly IComparer s_descendingComparer = new ReverseComparer();
private IGlyphRunImpl? _glyphRunImpl;
- private GlyphTypeface _glyphTypeface;
+ private IGlyphTypeface _glyphTypeface;
private double _fontRenderingEmSize;
private int _biDiLevel;
private Point? _baselineOrigin;
@@ -42,7 +43,7 @@ namespace Avalonia.Media
/// The glyph clusters.
/// The bidi level.
public GlyphRun(
- GlyphTypeface glyphTypeface,
+ IGlyphTypeface glyphTypeface,
double fontRenderingEmSize,
ReadOnlySlice characters,
IReadOnlyList glyphIndices,
@@ -69,9 +70,9 @@ namespace Avalonia.Media
}
///
- /// Gets the for the .
+ /// Gets the for the .
///
- public GlyphTypeface GlyphTypeface => _glyphTypeface;
+ public IGlyphTypeface GlyphTypeface => _glyphTypeface;
///
/// Gets or sets the em size used for rendering the .
@@ -171,7 +172,7 @@ namespace Avalonia.Media
///
/// Gets the scale of the current
///
- internal double Scale => FontRenderingEmSize / GlyphTypeface.DesignEmHeight;
+ internal double Scale => FontRenderingEmSize / GlyphTypeface.Metrics.DesignEmHeight;
///
/// Returns true if the text direction is left-to-right. Otherwise, returns false.
@@ -612,7 +613,7 @@ namespace Avalonia.Media
/// The baseline origin.
private Point CalculateBaselineOrigin()
{
- return new Point(0, -GlyphTypeface.Ascent * Scale);
+ return new Point(0, -GlyphTypeface.Metrics.Ascent * Scale);
}
private GlyphRunMetrics CreateGlyphRunMetrics()
@@ -636,7 +637,7 @@ namespace Avalonia.Media
}
var isReversed = firstCluster > lastCluster;
- var height = (GlyphTypeface.Descent - GlyphTypeface.Ascent + GlyphTypeface.LineGap) * Scale;
+ var height = GlyphTypeface.Metrics.LineSpacing * Scale;
var widthIncludingTrailingWhitespace = 0d;
var trailingWhitespaceLength = GetTrailingWhitespaceLength(isReversed, out var newLineLength, out var glyphCount);
@@ -854,9 +855,87 @@ namespace Avalonia.Media
throw new InvalidOperationException();
}
+ _glyphRunImpl = CreateGlyphRunImpl();
+ }
+
+ private IGlyphRunImpl CreateGlyphRunImpl()
+ {
+ IGlyphRunImpl glyphRunImpl;
+
var platformRenderInterface = AvaloniaLocator.Current.GetRequiredService();
+ var count = GlyphIndices.Count;
+ var scale = (float)(FontRenderingEmSize / GlyphTypeface.Metrics.DesignEmHeight);
+
+ if (GlyphOffsets == null)
+ {
+ if (GlyphTypeface.Metrics.IsFixedPitch)
+ {
+ var buffer = platformRenderInterface.AllocateGlyphRun(GlyphTypeface, (float)FontRenderingEmSize, count);
+
+ var glyphs = buffer.GlyphIndices;
+
+ for (int i = 0; i < glyphs.Length; i++)
+ {
+ glyphs[i] = GlyphIndices[i];
+ }
+
+ glyphRunImpl = buffer.Build();
+ }
+ else
+ {
+ var buffer = platformRenderInterface.AllocateHorizontalGlyphRun(GlyphTypeface, (float)FontRenderingEmSize, count);
+ var glyphs = buffer.GlyphIndices;
+ var positions = buffer.GlyphPositions;
+ var width = 0d;
+
+ for (var i = 0; i < count; i++)
+ {
+ positions[i] = (float)width;
+
+ if (GlyphAdvances == null)
+ {
+ width += GlyphTypeface.GetGlyphAdvance(GlyphIndices[i]) * scale;
+ }
+ else
+ {
+ width += GlyphAdvances[i];
+ }
+
+ glyphs[i] = GlyphIndices[i];
+ }
+
+ glyphRunImpl = buffer.Build();
+ }
+ }
+ else
+ {
+ var buffer = platformRenderInterface.AllocatePositionedGlyphRun(GlyphTypeface, (float)FontRenderingEmSize, count);
+ var glyphs = buffer.GlyphIndices;
+ var glyphPositions = buffer.GlyphPositions;
+ var currentX = 0.0;
+
+ for (var i = 0; i < count; i++)
+ {
+ var glyphOffset = GlyphOffsets[i];
+
+ glyphPositions[i] = new PointF((float)(currentX + glyphOffset.X), (float)glyphOffset.Y);
+
+ if (GlyphAdvances == null)
+ {
+ currentX += GlyphTypeface.GetGlyphAdvance(GlyphIndices[i]) * scale;
+ }
+ else
+ {
+ currentX += GlyphAdvances[i];
+ }
+
+ glyphs[i] = GlyphIndices[i];
+ }
+
+ glyphRunImpl = buffer.Build();
+ }
- _glyphRunImpl = platformRenderInterface.CreateGlyphRun(this);
+ return glyphRunImpl;
}
void IDisposable.Dispose()
diff --git a/src/Avalonia.Base/Media/GlyphTypeface.cs b/src/Avalonia.Base/Media/GlyphTypeface.cs
deleted file mode 100644
index 45ef04e77f..0000000000
--- a/src/Avalonia.Base/Media/GlyphTypeface.cs
+++ /dev/null
@@ -1,125 +0,0 @@
-using System;
-using Avalonia.Platform;
-
-namespace Avalonia.Media
-{
- public sealed class GlyphTypeface : IDisposable
- {
- public GlyphTypeface(Typeface typeface)
- : this(FontManager.Current.PlatformImpl.CreateGlyphTypeface(typeface))
- {
- }
-
- public GlyphTypeface(IGlyphTypefaceImpl platformImpl)
- {
- PlatformImpl = platformImpl;
- }
-
- public IGlyphTypefaceImpl PlatformImpl { get; }
-
- ///
- /// Gets the font design units per em.
- ///
- public short DesignEmHeight => PlatformImpl.DesignEmHeight;
-
- ///
- /// Gets the recommended distance above the baseline in design em size.
- ///
- public int Ascent => PlatformImpl.Ascent;
-
- ///
- /// Gets the recommended distance under the baseline in design em size.
- ///
- public int Descent => PlatformImpl.Descent;
-
- ///
- /// Gets the recommended additional space between two lines of text in design em size.
- ///
- public int LineGap => PlatformImpl.LineGap;
-
- ///
- /// Gets the recommended line height.
- ///
- public int LineHeight => Descent - Ascent + LineGap;
-
- ///
- /// Gets a value that indicates the distance of the underline from the baseline in design em size.
- ///
- public int UnderlinePosition => PlatformImpl.UnderlinePosition;
-
- ///
- /// Gets a value that indicates the thickness of the underline in design em size.
- ///
- public int UnderlineThickness => PlatformImpl.UnderlineThickness;
-
- ///
- /// Gets a value that indicates the distance of the strikethrough from the baseline in design em size.
- ///
- public int StrikethroughPosition => PlatformImpl.StrikethroughPosition;
-
- ///
- /// Gets a value that indicates the thickness of the underline in design em size.
- ///
- public int StrikethroughThickness => PlatformImpl.StrikethroughThickness;
-
- ///
- /// A value indicating whether all glyphs in the font have the same advancement.
- ///
- public bool IsFixedPitch => PlatformImpl.IsFixedPitch;
-
- ///
- /// Returns an glyph index for the specified codepoint.
- ///
- ///
- /// Returns a replacement glyph if a glyph isn't found.
- ///
- /// The codepoint.
- ///
- /// A glyph index.
- ///
- public ushort GetGlyph(uint codepoint) => PlatformImpl.GetGlyph(codepoint);
-
- ///
- /// Tries to get an glyph index for specified codepoint.
- ///
- /// The codepoint.
- /// A glyph index.
- ///
- /// true if an glyph index was found, false otherwise.
- ///
- public bool TryGetGlyph(uint codepoint, out ushort glyph)
- {
- glyph = PlatformImpl.GetGlyph(codepoint);
-
- return glyph != 0;
- }
-
- ///
- /// Returns an array of glyph indices. Codepoints that are not represented by the font are returned as 0.
- ///
- /// The codepoints to map.
- ///
- public ushort[] GetGlyphs(ReadOnlySpan codepoints) => PlatformImpl.GetGlyphs(codepoints);
-
- ///
- /// Returns the glyph advance for the specified glyph.
- ///
- /// The glyph.
- ///
- /// The advance.
- ///
- public int GetGlyphAdvance(ushort glyph) => PlatformImpl.GetGlyphAdvance(glyph);
-
- ///
- /// Returns an array of glyph advances in design em size.
- ///
- /// The glyph indices.
- ///
- public int[] GetGlyphAdvances(ReadOnlySpan glyphs) => PlatformImpl.GetGlyphAdvances(glyphs);
-
- void IDisposable.Dispose()
- {
- PlatformImpl?.Dispose();
- }
- }
-}
diff --git a/src/Avalonia.Base/Media/HslColor.cs b/src/Avalonia.Base/Media/HslColor.cs
index 485bb1db16..425a3138c3 100644
--- a/src/Avalonia.Base/Media/HslColor.cs
+++ b/src/Avalonia.Base/Media/HslColor.cs
@@ -302,9 +302,9 @@ namespace Avalonia.Media
if (components.Length == 3) // HSL
{
- if (double.TryParse(components[0], NumberStyles.Number, CultureInfo.InvariantCulture, out double hue) &&
- TryInternalParse(components[1], out double saturation) &&
- TryInternalParse(components[2], out double lightness))
+ if (components[0].AsSpan().TryParseDouble(NumberStyles.Number, CultureInfo.InvariantCulture, out double hue) &&
+ TryInternalParse(components[1].AsSpan(), out double saturation) &&
+ TryInternalParse(components[2].AsSpan(), out double lightness))
{
hslColor = new HslColor(1.0, hue, saturation, lightness);
return true;
@@ -312,10 +312,10 @@ namespace Avalonia.Media
}
else if (components.Length == 4) // HSLA
{
- if (double.TryParse(components[0], NumberStyles.Number, CultureInfo.InvariantCulture, out double hue) &&
- TryInternalParse(components[1], out double saturation) &&
- TryInternalParse(components[2], out double lightness) &&
- TryInternalParse(components[3], out double alpha))
+ if (components[0].AsSpan().TryParseDouble(NumberStyles.Number, CultureInfo.InvariantCulture, out double hue) &&
+ TryInternalParse(components[1].AsSpan(), out double saturation) &&
+ TryInternalParse(components[2].AsSpan(), out double lightness) &&
+ TryInternalParse(components[3].AsSpan(), out double alpha))
{
hslColor = new HslColor(alpha, hue, saturation, lightness);
return true;
@@ -323,28 +323,22 @@ namespace Avalonia.Media
}
// Local function to specially parse a double value with an optional percentage sign
- bool TryInternalParse(string inString, out double outDouble)
+ bool TryInternalParse(ReadOnlySpan inString, out double outDouble)
{
// The percent sign, if it exists, must be at the end of the number
- int percentIndex = inString.IndexOf("%", StringComparison.Ordinal);
+ int percentIndex = inString.IndexOf("%".AsSpan(), StringComparison.Ordinal);
if (percentIndex >= 0)
{
- var result = double.TryParse(
- inString.Substring(0, percentIndex),
- NumberStyles.Number,
- CultureInfo.InvariantCulture,
- out double percentage);
+ var result = inString.Slice(0, percentIndex).TryParseDouble(NumberStyles.Number, CultureInfo.InvariantCulture,
+ out double percentage);
outDouble = percentage / 100.0;
return result;
}
else
{
- return double.TryParse(
- inString,
- NumberStyles.Number,
- CultureInfo.InvariantCulture,
+ return inString.TryParseDouble(NumberStyles.Number, CultureInfo.InvariantCulture,
out outDouble);
}
}
diff --git a/src/Avalonia.Base/Media/HsvColor.cs b/src/Avalonia.Base/Media/HsvColor.cs
index 512e57ae07..9f95b31518 100644
--- a/src/Avalonia.Base/Media/HsvColor.cs
+++ b/src/Avalonia.Base/Media/HsvColor.cs
@@ -302,9 +302,9 @@ namespace Avalonia.Media
if (components.Length == 3) // HSV
{
- if (double.TryParse(components[0], NumberStyles.Number, CultureInfo.InvariantCulture, out double hue) &&
- TryInternalParse(components[1], out double saturation) &&
- TryInternalParse(components[2], out double value))
+ if (components[0].AsSpan().TryParseDouble(NumberStyles.Number, CultureInfo.InvariantCulture, out double hue) &&
+ TryInternalParse(components[1].AsSpan(), out double saturation) &&
+ TryInternalParse(components[2].AsSpan(), out double value))
{
hsvColor = new HsvColor(1.0, hue, saturation, value);
return true;
@@ -312,10 +312,10 @@ namespace Avalonia.Media
}
else if (components.Length == 4) // HSVA
{
- if (double.TryParse(components[0], NumberStyles.Number, CultureInfo.InvariantCulture, out double hue) &&
- TryInternalParse(components[1], out double saturation) &&
- TryInternalParse(components[2], out double value) &&
- TryInternalParse(components[3], out double alpha))
+ if (components[0].AsSpan().TryParseDouble(NumberStyles.Number, CultureInfo.InvariantCulture, out double hue) &&
+ TryInternalParse(components[1].AsSpan(), out double saturation) &&
+ TryInternalParse(components[2].AsSpan(), out double value) &&
+ TryInternalParse(components[3].AsSpan(), out double alpha))
{
hsvColor = new HsvColor(alpha, hue, saturation, value);
return true;
@@ -323,28 +323,22 @@ namespace Avalonia.Media
}
// Local function to specially parse a double value with an optional percentage sign
- bool TryInternalParse(string inString, out double outDouble)
+ bool TryInternalParse(ReadOnlySpan inString, out double outDouble)
{
// The percent sign, if it exists, must be at the end of the number
- int percentIndex = inString.IndexOf("%", StringComparison.Ordinal);
+ int percentIndex = inString.IndexOf("%".AsSpan(), StringComparison.Ordinal);
if (percentIndex >= 0)
{
- var result = double.TryParse(
- inString.Substring(0, percentIndex),
- NumberStyles.Number,
- CultureInfo.InvariantCulture,
- out double percentage);
+ var result = inString.Slice(0, percentIndex).TryParseDouble(NumberStyles.Number, CultureInfo.InvariantCulture,
+ out double percentage);
outDouble = percentage / 100.0;
return result;
}
else
{
- return double.TryParse(
- inString,
- NumberStyles.Number,
- CultureInfo.InvariantCulture,
+ return inString.TryParseDouble(NumberStyles.Number, CultureInfo.InvariantCulture,
out outDouble);
}
}
diff --git a/src/Avalonia.Base/Platform/IGlyphTypefaceImpl.cs b/src/Avalonia.Base/Media/IGlyphTypeface.cs
similarity index 51%
rename from src/Avalonia.Base/Platform/IGlyphTypefaceImpl.cs
rename to src/Avalonia.Base/Media/IGlyphTypeface.cs
index 415f34fb29..9e1e52cb73 100644
--- a/src/Avalonia.Base/Platform/IGlyphTypefaceImpl.cs
+++ b/src/Avalonia.Base/Media/IGlyphTypeface.cs
@@ -1,56 +1,39 @@
using System;
using Avalonia.Metadata;
-namespace Avalonia.Platform
+namespace Avalonia.Media
{
[Unstable]
- public interface IGlyphTypefaceImpl : IDisposable
+ public interface IGlyphTypeface : IDisposable
{
///
- /// Gets the font design units per em.
+ /// Gets the number of glyphs held by this glyph typeface.
///
- short DesignEmHeight { get; }
+ int GlyphCount { get; }
///
- /// Gets the recommended distance above the baseline in design em size.
+ /// Gets the font metrics.
///
- int Ascent { get; }
-
- ///
- /// Gets the recommended distance under the baseline in design em size.
- ///
- int Descent { get; }
-
- ///
- /// Gets the recommended additional space between two lines of text in design em size.
- ///
- int LineGap { get; }
-
- ///
- /// Gets a value that indicates the distance of the underline from the baseline in design em size.
- ///
- int UnderlinePosition { get; }
-
- ///
- /// Gets a value that indicates the thickness of the underline in design em size.
- ///
- int UnderlineThickness { get; }
-
- ///
- /// Gets a value that indicates the distance of the strikethrough from the baseline in design em size.
- ///
- int StrikethroughPosition { get; }
+ ///
+ /// The font metrics.
+ ///
+ FontMetrics Metrics { get; }
///
- /// Gets a value that indicates the thickness of the underline in design em size.
+ /// Gets the algorithmic style simulations applied to this glyph typeface.
///
- int StrikethroughThickness { get; }
+ FontSimulations FontSimulations { get; }
///
- /// A value indicating whether all glyphs in the font have the same advancement.
+ /// Tries to get a glyph's metrics in em units.
///
- bool IsFixedPitch { get; }
-
+ /// The glyph id.
+ /// The glyph metrics.
+ ///
+ /// true if an glyph's metrics was found, false otherwise.
+ ///
+ bool TryGetGlyphMetrics(ushort glyph, out GlyphMetrics metrics);
+
///
/// Returns an glyph index for the specified codepoint.
///
@@ -63,6 +46,16 @@ namespace Avalonia.Platform
///
ushort GetGlyph(uint codepoint);
+ ///
+ /// Tries to get an glyph index for specified codepoint.
+ ///
+ /// The codepoint.
+ /// A glyph index.
+ ///
+ /// true if an glyph index was found, false otherwise.
+ ///
+ bool TryGetGlyph(uint codepoint, out ushort glyph);
+
///
/// Returns an array of glyph indices. Codepoints that are not represented by the font are returned as 0.
///
@@ -89,5 +82,13 @@ namespace Avalonia.Platform
/// An array of glyph advances.
///
int[] GetGlyphAdvances(ReadOnlySpan glyphs);
+
+ ///
+ /// Returns the contents of the table data for the specified tag.
+ ///
+ /// The table tag to get the data for.
+ /// The contents of the table data for the specified tag.
+ /// Returns true if the content exists, otherwise false.
+ bool TryGetTable(uint tag, out byte[] table);
}
}
diff --git a/src/Avalonia.Base/Media/PathMarkupParser.cs b/src/Avalonia.Base/Media/PathMarkupParser.cs
index 30c5206125..cf12bf5126 100644
--- a/src/Avalonia.Base/Media/PathMarkupParser.cs
+++ b/src/Avalonia.Base/Media/PathMarkupParser.cs
@@ -188,7 +188,7 @@ namespace Avalonia.Media
_isOpen = true;
}
- private void SetFillRule(ref ReadOnlySpan span)
+ private void SetFillRule(scoped ref ReadOnlySpan span)
{
ThrowIfDisposed();
@@ -452,7 +452,7 @@ namespace Avalonia.Media
return !span.IsEmpty && (span[0] == ',' || span[0] == '-' || span[0] == '.' || char.IsDigit(span[0]));
}
- private static bool ReadArgument(ref ReadOnlySpan remaining, out ReadOnlySpan argument)
+ private static bool ReadArgument(scoped ref ReadOnlySpan remaining, out ReadOnlySpan argument)
{
remaining = SkipWhitespace(remaining);
if (remaining.IsEmpty)
diff --git a/src/Avalonia.Base/Media/PreciseEllipticArcHelper.cs b/src/Avalonia.Base/Media/PreciseEllipticArcHelper.cs
index 5dd647e8ca..49dfe3c7b3 100644
--- a/src/Avalonia.Base/Media/PreciseEllipticArcHelper.cs
+++ b/src/Avalonia.Base/Media/PreciseEllipticArcHelper.cs
@@ -1081,7 +1081,7 @@ namespace Avalonia.Media
Point c = rest * (cs) + translation;
- // See "http://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter" to understand
+ // See "https://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter" to understand
// how the ellipse center is calculated
diff --git a/src/Avalonia.Base/Media/TextDecoration.cs b/src/Avalonia.Base/Media/TextDecoration.cs
index 4c9764af96..0a7328125a 100644
--- a/src/Avalonia.Base/Media/TextDecoration.cs
+++ b/src/Avalonia.Base/Media/TextDecoration.cs
@@ -155,9 +155,9 @@ namespace Avalonia.Media
///
/// The drawing context.
/// The decorated run.
- /// The font metrics of the decorated run.
+ /// The font metrics of the decorated run.
/// The default brush that is used to draw the decoration.
- internal void Draw(DrawingContext drawingContext, GlyphRun glyphRun, FontMetrics fontMetrics, IBrush defaultBrush)
+ internal void Draw(DrawingContext drawingContext, GlyphRun glyphRun, TextMetrics textMetrics, IBrush defaultBrush)
{
var baselineOrigin = glyphRun.BaselineOrigin;
var thickness = StrokeThickness;
@@ -168,16 +168,16 @@ namespace Avalonia.Media
switch (Location)
{
case TextDecorationLocation.Underline:
- thickness = fontMetrics.UnderlineThickness;
+ thickness = textMetrics.UnderlineThickness;
break;
case TextDecorationLocation.Strikethrough:
- thickness = fontMetrics.StrikethroughThickness;
+ thickness = textMetrics.StrikethroughThickness;
break;
}
break;
case TextDecorationUnit.FontRenderingEmSize:
- thickness = fontMetrics.FontRenderingEmSize * thickness;
+ thickness = textMetrics.FontRenderingEmSize * thickness;
break;
}
@@ -189,17 +189,17 @@ namespace Avalonia.Media
origin += glyphRun.BaselineOrigin;
break;
case TextDecorationLocation.Strikethrough:
- origin += new Point(baselineOrigin.X, baselineOrigin.Y + fontMetrics.StrikethroughPosition);
+ origin += new Point(baselineOrigin.X, baselineOrigin.Y + textMetrics.StrikethroughPosition);
break;
case TextDecorationLocation.Underline:
- origin += new Point(baselineOrigin.X, baselineOrigin.Y + fontMetrics.UnderlinePosition);
+ origin += new Point(baselineOrigin.X, baselineOrigin.Y + textMetrics.UnderlinePosition);
break;
}
switch (StrokeOffsetUnit)
{
case TextDecorationUnit.FontRenderingEmSize:
- origin += new Point(0, StrokeOffset * fontMetrics.FontRenderingEmSize);
+ origin += new Point(0, StrokeOffset * textMetrics.FontRenderingEmSize);
break;
case TextDecorationUnit.Pixel:
origin += new Point(0, StrokeOffset);
diff --git a/src/Avalonia.Base/Media/TextFormatting/ShapedBuffer.cs b/src/Avalonia.Base/Media/TextFormatting/ShapedBuffer.cs
index 47a6334e39..85924a3d32 100644
--- a/src/Avalonia.Base/Media/TextFormatting/ShapedBuffer.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/ShapedBuffer.cs
@@ -8,13 +8,13 @@ namespace Avalonia.Media.TextFormatting
{
private static readonly IComparer s_clusterComparer = new CompareClusters();
- public ShapedBuffer(ReadOnlySlice text, int length, GlyphTypeface glyphTypeface, double fontRenderingEmSize, sbyte bidiLevel)
+ public ShapedBuffer(ReadOnlySlice text, int length, IGlyphTypeface glyphTypeface, double fontRenderingEmSize, sbyte bidiLevel)
: this(text, new GlyphInfo[length], glyphTypeface, fontRenderingEmSize, bidiLevel)
{
}
- internal ShapedBuffer(ReadOnlySlice text, ArraySlice glyphInfos, GlyphTypeface glyphTypeface, double fontRenderingEmSize, sbyte bidiLevel)
+ internal ShapedBuffer(ReadOnlySlice text, ArraySlice glyphInfos, IGlyphTypeface glyphTypeface, double fontRenderingEmSize, sbyte bidiLevel)
{
Text = text;
GlyphInfos = glyphInfos;
@@ -29,7 +29,7 @@ namespace Avalonia.Media.TextFormatting
public int Length => GlyphInfos.Length;
- public GlyphTypeface GlyphTypeface { get; }
+ public IGlyphTypeface GlyphTypeface { get; }
public double FontRenderingEmSize { get; }
diff --git a/src/Avalonia.Base/Media/TextFormatting/ShapedTextCharacters.cs b/src/Avalonia.Base/Media/TextFormatting/ShapedTextCharacters.cs
index 53287a264d..21101f462c 100644
--- a/src/Avalonia.Base/Media/TextFormatting/ShapedTextCharacters.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/ShapedTextCharacters.cs
@@ -1,5 +1,4 @@
using System;
-using System.Diagnostics;
using Avalonia.Media.TextFormatting.Unicode;
using Avalonia.Utilities;
@@ -18,7 +17,7 @@ namespace Avalonia.Media.TextFormatting
Text = shapedBuffer.Text;
Properties = properties;
TextSourceLength = Text.Length;
- FontMetrics = new FontMetrics(properties.Typeface, properties.FontRenderingEmSize);
+ TextMetrics = new TextMetrics(properties.Typeface, properties.FontRenderingEmSize);
}
public bool IsReversed { get; private set; }
@@ -36,9 +35,9 @@ namespace Avalonia.Media.TextFormatting
///
public override int TextSourceLength { get; }
- public FontMetrics FontMetrics { get; }
+ public TextMetrics TextMetrics { get; }
- public override double Baseline => -FontMetrics.Ascent;
+ public override double Baseline => -TextMetrics.Ascent;
public override Size Size => GlyphRun.Size;
@@ -89,7 +88,7 @@ namespace Avalonia.Media.TextFormatting
foreach (var textDecoration in Properties.TextDecorations)
{
- textDecoration.Draw(drawingContext, GlyphRun, FontMetrics, Properties.ForegroundBrush);
+ textDecoration.Draw(drawingContext, GlyphRun, TextMetrics, Properties.ForegroundBrush);
}
}
}
diff --git a/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs
index 73dd3366aa..5c43a1c94f 100644
--- a/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs
@@ -477,25 +477,28 @@ namespace Avalonia.Media.TextFormatting
{
case ShapedTextCharacters shapedTextCharacters:
{
- var firstCluster = shapedTextCharacters.ShapedBuffer.GlyphClusters[0];
- var lastCluster = firstCluster;
-
- for (var i = 0; i < shapedTextCharacters.ShapedBuffer.Length; i++)
+ if(shapedTextCharacters.ShapedBuffer.Length > 0)
{
- var glyphInfo = shapedTextCharacters.ShapedBuffer[i];
+ var firstCluster = shapedTextCharacters.ShapedBuffer.GlyphClusters[0];
+ var lastCluster = firstCluster;
- if (currentWidth + glyphInfo.GlyphAdvance > paragraphWidth)
+ for (var i = 0; i < shapedTextCharacters.ShapedBuffer.Length; i++)
{
- measuredLength += Math.Max(0, lastCluster - firstCluster);
+ var glyphInfo = shapedTextCharacters.ShapedBuffer[i];
- goto found;
- }
+ if (currentWidth + glyphInfo.GlyphAdvance > paragraphWidth)
+ {
+ measuredLength += Math.Max(0, lastCluster - firstCluster);
- lastCluster = glyphInfo.GlyphCluster;
- currentWidth += glyphInfo.GlyphAdvance;
- }
+ goto found;
+ }
- measuredLength += currentRun.TextSourceLength;
+ lastCluster = glyphInfo.GlyphCluster;
+ currentWidth += glyphInfo.GlyphAdvance;
+ }
+
+ measuredLength += currentRun.TextSourceLength;
+ }
break;
}
diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs
index aba8008fb9..96f88d1f44 100644
--- a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs
@@ -1378,17 +1378,17 @@ namespace Avalonia.Media.TextFormatting
private TextLineMetrics CreateLineMetrics()
{
- var glyphTypeface = _paragraphProperties.DefaultTextRunProperties.Typeface.GlyphTypeface;
+ var fontMetrics = _paragraphProperties.DefaultTextRunProperties.Typeface.GlyphTypeface.Metrics;
var fontRenderingEmSize = _paragraphProperties.DefaultTextRunProperties.FontRenderingEmSize;
- var scale = fontRenderingEmSize / glyphTypeface.DesignEmHeight;
+ var scale = fontRenderingEmSize / fontMetrics.DesignEmHeight;
var width = 0d;
var widthIncludingWhitespace = 0d;
var trailingWhitespaceLength = 0;
var newLineLength = 0;
- var ascent = glyphTypeface.Ascent * scale;
- var descent = glyphTypeface.Descent * scale;
- var lineGap = glyphTypeface.LineGap * scale;
+ var ascent = fontMetrics.Ascent * scale;
+ var descent = fontMetrics.Descent * scale;
+ var lineGap = fontMetrics.LineGap * scale;
var height = descent - ascent + lineGap;
@@ -1400,26 +1400,26 @@ namespace Avalonia.Media.TextFormatting
{
case ShapedTextCharacters textRun:
{
- var fontMetrics =
- new FontMetrics(textRun.Properties.Typeface, textRun.Properties.FontRenderingEmSize);
+ var textMetrics =
+ new TextMetrics(textRun.Properties.Typeface, textRun.Properties.FontRenderingEmSize);
if (fontRenderingEmSize < textRun.Properties.FontRenderingEmSize)
{
fontRenderingEmSize = textRun.Properties.FontRenderingEmSize;
- if (ascent > fontMetrics.Ascent)
+ if (ascent > textMetrics.Ascent)
{
- ascent = fontMetrics.Ascent;
+ ascent = textMetrics.Ascent;
}
- if (descent < fontMetrics.Descent)
+ if (descent < textMetrics.Descent)
{
- descent = fontMetrics.Descent;
+ descent = textMetrics.Descent;
}
- if (lineGap < fontMetrics.LineGap)
+ if (lineGap < textMetrics.LineGap)
{
- lineGap = fontMetrics.LineGap;
+ lineGap = textMetrics.LineGap;
}
if (descent - ascent + lineGap > height)
diff --git a/src/Avalonia.Base/Media/TextFormatting/FontMetrics.cs b/src/Avalonia.Base/Media/TextFormatting/TextMetrics.cs
similarity index 69%
rename from src/Avalonia.Base/Media/TextFormatting/FontMetrics.cs
rename to src/Avalonia.Base/Media/TextFormatting/TextMetrics.cs
index e01bba00a4..0382e66b5a 100644
--- a/src/Avalonia.Base/Media/TextFormatting/FontMetrics.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/TextMetrics.cs
@@ -1,33 +1,33 @@
namespace Avalonia.Media.TextFormatting
{
///
- /// A metric that holds information about font specific measurements.
+ /// A metric that holds information about text specific measurements.
///
- public readonly struct FontMetrics
+ public readonly struct TextMetrics
{
- public FontMetrics(Typeface typeface, double fontRenderingEmSize)
+ public TextMetrics(Typeface typeface, double fontRenderingEmSize)
{
- var glyphTypeface = typeface.GlyphTypeface;
+ var fontMetrics = typeface.GlyphTypeface.Metrics;
- var scale = fontRenderingEmSize / glyphTypeface.DesignEmHeight;
+ var scale = fontRenderingEmSize / fontMetrics.DesignEmHeight;
FontRenderingEmSize = fontRenderingEmSize;
- Ascent = glyphTypeface.Ascent * scale;
+ Ascent = fontMetrics.Ascent * scale;
- Descent = glyphTypeface.Descent * scale;
+ Descent = fontMetrics.Descent * scale;
- LineGap = glyphTypeface.LineGap * scale;
+ LineGap = fontMetrics.LineGap * scale;
LineHeight = Descent - Ascent + LineGap;
- UnderlineThickness = glyphTypeface.UnderlineThickness * scale;
+ UnderlineThickness = fontMetrics.UnderlineThickness * scale;
- UnderlinePosition = glyphTypeface.UnderlinePosition * scale;
+ UnderlinePosition = fontMetrics.UnderlinePosition * scale;
- StrikethroughThickness = glyphTypeface.StrikethroughThickness * scale;
+ StrikethroughThickness = fontMetrics.StrikethroughThickness * scale;
- StrikethroughPosition = glyphTypeface.StrikethroughPosition * scale;
+ StrikethroughPosition = fontMetrics.StrikethroughPosition * scale;
}
///
diff --git a/src/Avalonia.Base/Media/TextFormatting/TextShaperOptions.cs b/src/Avalonia.Base/Media/TextFormatting/TextShaperOptions.cs
index 4e75bb921e..0d00bed51e 100644
--- a/src/Avalonia.Base/Media/TextFormatting/TextShaperOptions.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/TextShaperOptions.cs
@@ -8,7 +8,7 @@ namespace Avalonia.Media.TextFormatting
public readonly struct TextShaperOptions
{
public TextShaperOptions(
- GlyphTypeface typeface,
+ IGlyphTypeface typeface,
double fontRenderingEmSize = 12,
sbyte bidiLevel = 0,
CultureInfo? culture = null,
@@ -24,7 +24,7 @@ namespace Avalonia.Media.TextFormatting
///
/// Get the typeface.
///
- public GlyphTypeface Typeface { get; }
+ public IGlyphTypeface Typeface { get; }
///
/// Get the font rendering em size.
///
diff --git a/src/Avalonia.Base/Media/TextFormatting/Unicode/BinaryReaderExtensions.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/BinaryReaderExtensions.cs
index 412007c6e0..ccbceb6a7b 100644
--- a/src/Avalonia.Base/Media/TextFormatting/Unicode/BinaryReaderExtensions.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/Unicode/BinaryReaderExtensions.cs
@@ -5,7 +5,7 @@
// not use this product except in compliance with the License. You may obtain
// a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
diff --git a/src/Avalonia.Base/Media/TextFormatting/Unicode/Codepoint.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/Codepoint.cs
index ab17263806..de40839853 100644
--- a/src/Avalonia.Base/Media/TextFormatting/Unicode/Codepoint.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/Unicode/Codepoint.cs
@@ -1,4 +1,5 @@
-using System.Runtime.CompilerServices;
+using System;
+using System.Runtime.CompilerServices;
using Avalonia.Utilities;
namespace Avalonia.Media.TextFormatting.Unicode
@@ -104,7 +105,7 @@ namespace Avalonia.Media.TextFormatting.Unicode
///
/// Gets the canonical representation of a given codepoint.
- ///
+ ///
///
/// The code point to be mapped.
/// The mapped canonical code point, or the passed .
@@ -165,7 +166,7 @@ namespace Avalonia.Media.TextFormatting.Unicode
/// The index to read at.
/// The count of character that were read.
///
- public static Codepoint ReadAt(ReadOnlySlice text, int index, out int count)
+ public static Codepoint ReadAt(ReadOnlySpan text, int index, out int count)
{
count = 1;
diff --git a/src/Avalonia.Base/Media/TextFormatting/Unicode/GraphemeBreak.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/GraphemeBreak.cs
index 6986b908a1..d0ceba62f4 100644
--- a/src/Avalonia.Base/Media/TextFormatting/Unicode/GraphemeBreak.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/Unicode/GraphemeBreak.cs
@@ -2,6 +2,6 @@ namespace Avalonia.Media.TextFormatting.Unicode
{
internal static class GraphemeBreak
{
- public static byte[] Data => new byte[0];
+ public static byte[] Data => System.Array.Empty();
}
}
diff --git a/src/Avalonia.Base/Media/TextFormatting/Unicode/LineBreak.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/LineBreak.cs
index 34b14f008f..59c4df0a2e 100644
--- a/src/Avalonia.Base/Media/TextFormatting/Unicode/LineBreak.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/Unicode/LineBreak.cs
@@ -5,7 +5,7 @@
// not use this product except in compliance with the License. You may obtain
// a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
diff --git a/src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeTrie.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeTrie.cs
index cf03ed7cd3..079f830ddc 100644
--- a/src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeTrie.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeTrie.cs
@@ -5,7 +5,7 @@
// not use this product except in compliance with the License. You may obtain
// a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
diff --git a/src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeTrieBuilder.Constants.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeTrieBuilder.Constants.cs
index 29ee45acc2..de0304f4c9 100644
--- a/src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeTrieBuilder.Constants.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeTrieBuilder.Constants.cs
@@ -5,7 +5,7 @@
// not use this product except in compliance with the License. You may obtain
// a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
diff --git a/src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeTrieBuilder.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeTrieBuilder.cs
index 87f96984c5..755d603539 100644
--- a/src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeTrieBuilder.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeTrieBuilder.cs
@@ -5,7 +5,7 @@
// not use this product except in compliance with the License. You may obtain
// a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
diff --git a/src/Avalonia.Base/Media/Typeface.cs b/src/Avalonia.Base/Media/Typeface.cs
index f0daa841d9..e6047bf96c 100644
--- a/src/Avalonia.Base/Media/Typeface.cs
+++ b/src/Avalonia.Base/Media/Typeface.cs
@@ -81,7 +81,7 @@ namespace Avalonia.Media
///
/// The glyph typeface.
///
- public GlyphTypeface GlyphTypeface => FontManager.Current.GetOrAddGlyphTypeface(this);
+ public IGlyphTypeface GlyphTypeface => FontManager.Current.GetOrAddGlyphTypeface(this);
public static bool operator !=(Typeface a, Typeface b)
{
diff --git a/src/Avalonia.Base/Media/UnicodeRange.cs b/src/Avalonia.Base/Media/UnicodeRange.cs
index e2338b9b26..344b85bae9 100644
--- a/src/Avalonia.Base/Media/UnicodeRange.cs
+++ b/src/Avalonia.Base/Media/UnicodeRange.cs
@@ -163,7 +163,7 @@ namespace Avalonia.Media
throw new FormatException("Could not parse specified Unicode range segment.");
}
- if (!single.Value.Contains("?"))
+ if (!single.Value.Contains('?'))
{
start = int.Parse(single.Groups[1].Value, System.Globalization.NumberStyles.HexNumber);
end = start;
diff --git a/src/Avalonia.Base/Platform/IFontManagerImpl.cs b/src/Avalonia.Base/Platform/IFontManagerImpl.cs
index 932249bd52..cd6e64abaf 100644
--- a/src/Avalonia.Base/Platform/IFontManagerImpl.cs
+++ b/src/Avalonia.Base/Platform/IFontManagerImpl.cs
@@ -43,6 +43,6 @@ namespace Avalonia.Platform
/// 0
/// The created glyph typeface. Can be Null if it was not possible to create a glyph typeface.
///
- IGlyphTypefaceImpl CreateGlyphTypeface(Typeface typeface);
+ IGlyphTypeface CreateGlyphTypeface(Typeface typeface);
}
}
diff --git a/src/Avalonia.Base/Platform/IGlyphRunBuffer.cs b/src/Avalonia.Base/Platform/IGlyphRunBuffer.cs
new file mode 100644
index 0000000000..c1fc7a5967
--- /dev/null
+++ b/src/Avalonia.Base/Platform/IGlyphRunBuffer.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Drawing;
+
+namespace Avalonia.Platform
+{
+ public interface IGlyphRunBuffer
+ {
+ Span GlyphIndices { get; }
+
+ IGlyphRunImpl Build();
+ }
+
+ public interface IHorizontalGlyphRunBuffer : IGlyphRunBuffer
+ {
+ Span GlyphPositions { get; }
+ }
+
+ public interface IPositionedGlyphRunBuffer : IGlyphRunBuffer
+ {
+ Span GlyphPositions { get; }
+ }
+}
diff --git a/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs b/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs
index e39a4e23df..9d0d7974b4 100644
--- a/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs
+++ b/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs
@@ -171,11 +171,40 @@ namespace Avalonia.Platform
IBitmapImpl LoadBitmap(PixelFormat format, AlphaFormat alphaFormat, IntPtr data, PixelSize size, Vector dpi, int stride);
///
- /// Creates a platform implementation of a glyph run.
+ /// Allocates a platform glyph run buffer.
///
- /// The glyph run.
- ///
- IGlyphRunImpl CreateGlyphRun(GlyphRun glyphRun);
+ /// The glyph typeface.
+ /// The font rendering em size.
+ /// The length.
+ /// An .
+ ///
+ /// This buffer only holds glyph indices.
+ ///
+ IGlyphRunBuffer AllocateGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length);
+
+ ///
+ /// Allocates a horizontal platform glyph run buffer.
+ ///
+ /// The glyph typeface.
+ /// The font rendering em size.
+ /// The length.
+ /// An .
+ ///
+ /// This buffer holds glyph indices and glyph advances.
+ ///
+ IHorizontalGlyphRunBuffer AllocateHorizontalGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length);
+
+ ///
+ /// Allocates a positioned platform glyph run buffer.
+ ///
+ /// The glyph typeface.
+ /// The font rendering em size.
+ /// The length.
+ /// An .
+ ///
+ /// This buffer holds glyph indices, glyph advances and glyph positions.
+ ///
+ IPositionedGlyphRunBuffer AllocatePositionedGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length);
///
/// Gets a value indicating whether the platform directly supports rectangles with rounded corners.
diff --git a/src/Avalonia.Base/Rendering/Composition/Server/FpsCounter.cs b/src/Avalonia.Base/Rendering/Composition/Server/FpsCounter.cs
index b0b3982ed5..06fb526736 100644
--- a/src/Avalonia.Base/Rendering/Composition/Server/FpsCounter.cs
+++ b/src/Avalonia.Base/Rendering/Composition/Server/FpsCounter.cs
@@ -25,7 +25,7 @@ internal class FpsCounter
// ASCII chars
private GlyphRun[] _runs = new GlyphRun[LastChar - FirstChar + 1];
- public FpsCounter(GlyphTypeface typeface)
+ public FpsCounter(IGlyphTypeface typeface)
{
for (var c = FirstChar; c <= LastChar; c++)
{
diff --git a/src/Avalonia.Base/Utilities/BinarySearchExtension.cs b/src/Avalonia.Base/Utilities/BinarySearchExtension.cs
index a4f6ae89c1..b7060d2e21 100644
--- a/src/Avalonia.Base/Utilities/BinarySearchExtension.cs
+++ b/src/Avalonia.Base/Utilities/BinarySearchExtension.cs
@@ -5,7 +5,7 @@
// not use this product except in compliance with the License. You may obtain
// a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
diff --git a/src/Avalonia.Base/Utilities/IdentifierParser.cs b/src/Avalonia.Base/Utilities/IdentifierParser.cs
index b105d0746b..ee176a6b85 100644
--- a/src/Avalonia.Base/Utilities/IdentifierParser.cs
+++ b/src/Avalonia.Base/Utilities/IdentifierParser.cs
@@ -8,7 +8,7 @@ namespace Avalonia.Utilities
#endif
static class IdentifierParser
{
- public static ReadOnlySpan ParseIdentifier(this ref CharacterReader r)
+ public static ReadOnlySpan ParseIdentifier(this scoped ref CharacterReader r)
{
if (IsValidIdentifierStart(r.Peek))
{
diff --git a/src/Avalonia.Base/Utilities/MathUtilities.cs b/src/Avalonia.Base/Utilities/MathUtilities.cs
index 3c48c3469e..feb1097b5f 100644
--- a/src/Avalonia.Base/Utilities/MathUtilities.cs
+++ b/src/Avalonia.Base/Utilities/MathUtilities.cs
@@ -11,7 +11,7 @@ namespace Avalonia.Utilities
static class MathUtilities
{
// smallest such that 1.0+DoubleEpsilon != 1.0
- internal static readonly double DoubleEpsilon = 2.2204460492503131e-016;
+ internal const double DoubleEpsilon = 2.2204460492503131e-016;
private const float FloatEpsilon = 1.192092896e-07F;
diff --git a/src/Avalonia.Base/Utilities/ReadOnlySlice.cs b/src/Avalonia.Base/Utilities/ReadOnlySlice.cs
index ad545b2923..583a3139b9 100644
--- a/src/Avalonia.Base/Utilities/ReadOnlySlice.cs
+++ b/src/Avalonia.Base/Utilities/ReadOnlySlice.cs
@@ -214,6 +214,8 @@ namespace Avalonia.Utilities
return new ReadOnlySlice(memory);
}
+ public static implicit operator ReadOnlySpan(ReadOnlySlice slice) => slice.Span;
+
internal class ReadOnlySliceDebugView
{
private readonly ReadOnlySlice _readOnlySlice;
diff --git a/src/Avalonia.Base/Utilities/SpanHelpers.cs b/src/Avalonia.Base/Utilities/SpanHelpers.cs
new file mode 100644
index 0000000000..9a5dce9798
--- /dev/null
+++ b/src/Avalonia.Base/Utilities/SpanHelpers.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Globalization;
+using System.Runtime.CompilerServices;
+
+namespace Avalonia.Utilities
+{
+ public static class SpanHelpers
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool TryParseUInt(this ReadOnlySpan span, NumberStyles style, IFormatProvider provider, out uint value)
+ {
+#if NETSTANDARD2_0
+ return uint.TryParse(span.ToString(), style, provider, out value);
+#else
+ return uint.TryParse(span, style, provider, out value);
+#endif
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool TryParseInt(this ReadOnlySpan span, out int value)
+ {
+#if NETSTANDARD2_0
+ return int.TryParse(span.ToString(), out value);
+#else
+ return int.TryParse(span, out value);
+#endif
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool TryParseDouble(this ReadOnlySpan span, NumberStyles style, IFormatProvider provider, out double value)
+ {
+#if NETSTANDARD2_0
+ return double.TryParse(span.ToString(), style, provider, out value);
+#else
+ return double.TryParse(span, style, provider, out value);
+#endif
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool TryParseByte(this ReadOnlySpan span, NumberStyles style, IFormatProvider provider, out byte value)
+ {
+#if NETSTANDARD2_0
+ return byte.TryParse(span.ToString(), style, provider, out value);
+#else
+ return byte.TryParse(span, style, provider, out value);
+#endif
+ }
+ }
+}
diff --git a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
index 22c410198f..74debed828 100644
--- a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
+++ b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
@@ -9,6 +9,11 @@
NU1605;CS8632
+
+
+ false
+
+
Shared/AvaloniaResourcesIndex.cs
diff --git a/src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs b/src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs
index 62a9ed27be..13e8b14891 100644
--- a/src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs
+++ b/src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs
@@ -37,8 +37,7 @@ namespace Avalonia.Build.Tasks
var res = XamlCompilerTaskExecutor.Compile(BuildEngine, input,
File.ReadAllLines(ReferencesFilePath).Where(l => !string.IsNullOrWhiteSpace(l)).ToArray(),
ProjectDirectory, OutputPath, VerifyIl, outputImportance,
- (SignAssembly && !DelaySign) ? AssemblyOriginatorKeyFile : null,
- EnableComInteropPatching, SkipXamlCompilation, DebuggerLaunch);
+ (SignAssembly && !DelaySign) ? AssemblyOriginatorKeyFile : null, SkipXamlCompilation, DebuggerLaunch);
if (!res.Success)
return false;
if (!res.WrittenFile)
@@ -72,8 +71,6 @@ namespace Avalonia.Build.Tasks
public string OutputPath { get; set; }
public bool VerifyIl { get; set; }
-
- public bool EnableComInteropPatching { get; set; }
public bool SkipXamlCompilation { get; set; }
public string AssemblyOriginatorKeyFile { get; set; }
diff --git a/src/Avalonia.Build.Tasks/GenerateAvaloniaResourcesTask.cs b/src/Avalonia.Build.Tasks/GenerateAvaloniaResourcesTask.cs
index 75758d1315..38450ea2a2 100644
--- a/src/Avalonia.Build.Tasks/GenerateAvaloniaResourcesTask.cs
+++ b/src/Avalonia.Build.Tasks/GenerateAvaloniaResourcesTask.cs
@@ -18,8 +18,6 @@ namespace Avalonia.Build.Tasks
public string Root { get; set; }
[Required]
public string Output { get; set; }
- [Required]
- public ITaskItem[] EmbeddedResources { get; set; }
public string ReportImportance { get; set; }
@@ -148,10 +146,6 @@ namespace Avalonia.Build.Tasks
Enum.TryParse(ReportImportance, out _reportImportance);
BuildEngine.LogMessage($"GenerateAvaloniaResourcesTask -> Root: {Root}, {Resources?.Count()} resources, Output:{Output}", _reportImportance < MessageImportance.Low ? MessageImportance.High : _reportImportance);
-
- foreach (var r in EmbeddedResources.Where(r => r.ItemSpec.EndsWith(".xaml") || r.ItemSpec.EndsWith(".paml") || r.ItemSpec.EndsWith(".axaml")))
- BuildEngine.LogWarning(BuildEngineErrorCode.LegacyResmScheme, r.ItemSpec,
- "XAML file is packed using legacy EmbeddedResource/resm scheme, relative URIs won't work");
var resources = BuildResourceSources();
if (!PreProcessXamlFiles(resources))
diff --git a/src/Avalonia.Build.Tasks/Program.cs b/src/Avalonia.Build.Tasks/Program.cs
index e3f00968fd..f42fab5964 100644
--- a/src/Avalonia.Build.Tasks/Program.cs
+++ b/src/Avalonia.Build.Tasks/Program.cs
@@ -44,8 +44,7 @@ namespace Avalonia.Build.Tasks
OutputPath = args[2],
BuildEngine = new ConsoleBuildEngine(),
ProjectDirectory = Directory.GetCurrentDirectory(),
- VerifyIl = true,
- EnableComInteropPatching = true
+ VerifyIl = true
}.Execute() ?
0 :
2;
diff --git a/src/Avalonia.Build.Tasks/SpanCompat.cs b/src/Avalonia.Build.Tasks/SpanCompat.cs
index 25f8d0175a..be59ff8b6c 100644
--- a/src/Avalonia.Build.Tasks/SpanCompat.cs
+++ b/src/Avalonia.Build.Tasks/SpanCompat.cs
@@ -1,4 +1,7 @@
#if !NETCOREAPP3_1_OR_GREATER
+using System.Globalization;
+using System.Runtime.CompilerServices;
+
namespace System
{
// This is a hack to enable our span code to work inside MSBuild task without referencing System.Memory
@@ -9,6 +12,8 @@ namespace System
private int _length;
public int Length => _length;
+ public static implicit operator ReadOnlySpan(string s) => new ReadOnlySpan(s);
+
public ReadOnlySpan(string s) : this(s, 0, s.Length)
{
@@ -63,8 +68,75 @@ namespace System
return Slice(start);
}
+ public ReadOnlySpan TrimEnd()
+ {
+ int end = Length - 1;
+ for (; end >= 0; end--)
+ {
+ if (!char.IsWhiteSpace(this[end]))
+ {
+ break;
+ }
+ }
+ return Slice(0, end + 1);
+ }
+
+ public ReadOnlySpan Trim()
+ {
+ 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)
+ {
+ if(Length == 0 || v.IsEmpty)
+ {
+ return -1;
+ }
+
+ for (var c = start; c < _length; c++)
+ {
+ if (this[c] == v[0])
+ {
+ for(var i = 0; i < v.Length; i++)
+ {
+ if (this[c + i] != v[i])
+ {
+ break;
+ }
+ }
+ return c;
+ }
+ }
+
+ return -1;
+ }
+
public static implicit operator ReadOnlySpan(char[] arr) => new ReadOnlySpan(new string(arr));
}
diff --git a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.Helpers.cs b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.Helpers.cs
index 5a2c74e16f..f83e07bd74 100644
--- a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.Helpers.cs
+++ b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.Helpers.cs
@@ -24,38 +24,6 @@ namespace Avalonia.Build.Tasks
string Name { get; }
IEnumerable Resources { get; }
}
-
- class EmbeddedResources : IResourceGroup
- {
- private readonly AssemblyDefinition _asm;
- public string Name => "EmbeddedResource";
-
- public IEnumerable Resources => _asm.MainModule.Resources.OfType()
- .Select(r => new WrappedResource(_asm, r)).ToList();
-
- public EmbeddedResources(AssemblyDefinition asm)
- {
- _asm = asm;
- }
- class WrappedResource : IResource
- {
- private readonly AssemblyDefinition _asm;
- private readonly EmbeddedResource _res;
-
- public WrappedResource(AssemblyDefinition asm, EmbeddedResource res)
- {
- _asm = asm;
- _res = res;
- }
-
- public string Uri => $"resm:{Name}?assembly={_asm.Name.Name}";
- public string Name => _res.Name;
- public string FilePath => Name;
- public byte[] FileContents => _res.GetResourceData();
-
- public void Remove() => _asm.MainModule.Resources.Remove(_res);
- }
- }
class AvaloniaResources : IResourceGroup
{
diff --git a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
index 5915388822..d2ccea8fa4 100644
--- a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
+++ b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
@@ -39,15 +39,15 @@ namespace Avalonia.Build.Tasks
public static CompileResult Compile(IBuildEngine engine, string input, string[] references,
string projectDirectory,
- string output, bool verifyIl, MessageImportance logImportance, string strongNameKey, bool patchCom,
+ string output, bool verifyIl, MessageImportance logImportance, string strongNameKey,
bool skipXamlCompilation)
{
- return Compile(engine, input, references, projectDirectory, output, verifyIl, logImportance, strongNameKey, patchCom, skipXamlCompilation, debuggerLaunch:false);
+ return Compile(engine, input, references, projectDirectory, output, verifyIl, logImportance, strongNameKey, skipXamlCompilation, debuggerLaunch:false);
}
internal static CompileResult Compile(IBuildEngine engine, string input, string[] references,
string projectDirectory,
- string output, bool verifyIl, MessageImportance logImportance, string strongNameKey, bool patchCom, bool skipXamlCompilation, bool debuggerLaunch)
+ string output, bool verifyIl, MessageImportance logImportance, string strongNameKey, bool skipXamlCompilation, bool debuggerLaunch)
{
var typeSystem = new CecilTypeSystem(
references.Where(r => !r.ToLowerInvariant().EndsWith("avalonia.build.tasks.dll")),
@@ -58,15 +58,12 @@ namespace Avalonia.Build.Tasks
if (!skipXamlCompilation)
{
var compileRes = CompileCore(engine, typeSystem, projectDirectory, verifyIl, logImportance, debuggerLaunch);
- if (compileRes == null && !patchCom)
+ if (compileRes == null)
return new CompileResult(true);
if (compileRes == false)
return new CompileResult(false);
}
- if (patchCom)
- ComInteropHelper.PatchAssembly(asm, typeSystem);
-
var writerParameters = new WriterParameters { WriteSymbols = asm.MainModule.HasSymbols };
if (!string.IsNullOrWhiteSpace(strongNameKey))
writerParameters.StrongNameKeyBlob = File.ReadAllBytes(strongNameKey);
@@ -112,9 +109,8 @@ namespace Avalonia.Build.Tasks
}
}
var asm = typeSystem.TargetAssemblyDefinition;
- var emres = new EmbeddedResources(asm);
var avares = new AvaloniaResources(asm, projectDirectory);
- if (avares.Resources.Count(CheckXamlName) == 0 && emres.Resources.Count(CheckXamlName) == 0)
+ if (avares.Resources.Count(CheckXamlName) == 0)
// Nothing to do
return null;
@@ -436,9 +432,6 @@ namespace Avalonia.Build.Tasks
return true;
}
- if (emres.Resources.Count(CheckXamlName) != 0)
- if (!CompileGroup(emres))
- return false;
if (avares.Resources.Count(CheckXamlName) != 0)
{
if (!CompileGroup(avares))
diff --git a/src/Avalonia.Controls.ColorPicker/ColorPalettes/FlatColorPalette.cs b/src/Avalonia.Controls.ColorPicker/ColorPalettes/FlatColorPalette.cs
index 130d7e0edd..2cc5b99b2e 100644
--- a/src/Avalonia.Controls.ColorPicker/ColorPalettes/FlatColorPalette.cs
+++ b/src/Avalonia.Controls.ColorPicker/ColorPalettes/FlatColorPalette.cs
@@ -17,268 +17,603 @@ namespace Avalonia.Controls
///
public class FlatColorPalette : IColorPalette
{
- // The full Flat UI color chart has 10 rows and 20 columns
- // See: https://htmlcolorcodes.com/assets/downloads/flat-design-colors/flat-design-color-chart.png
- // This is a reduced palette for usability
- private static Color[,] colorChart = new Color[,]
+ ///
+ /// Defines all colors in the .
+ ///
+ ///
+ /// This is done in an enum to ensure it is compiled into the assembly improving
+ /// startup performance.
+ ///
+ public enum FlatColor : uint
{
// Pomegranate
- {
- Color.FromArgb(0xFF, 0xF9, 0xEB, 0xEA),
- Color.FromArgb(0xFF, 0xE6, 0xB0, 0xAA),
- Color.FromArgb(0xFF, 0xCD, 0x61, 0x55),
- Color.FromArgb(0xFF, 0xA9, 0x32, 0x26),
- Color.FromArgb(0xFF, 0x7B, 0x24, 0x1C),
- },
+ Pomegranate1 = 0xFFF9EBEA,
+ Pomegranate2 = 0xFFF2D7D5,
+ Pomegranate3 = 0xFFE6B0AA,
+ Pomegranate4 = 0xFFD98880,
+ Pomegranate5 = 0xFFCD6155,
+ Pomegranate6 = 0xFFC0392B,
+ Pomegranate7 = 0xFFA93226,
+ Pomegranate8 = 0xFF922B21,
+ Pomegranate9 = 0xFF7B241C,
+ Pomegranate10 = 0xFF641E16,
+
+ // Alizarin
+ Alizarin1 = 0xFFFDEDEC,
+ Alizarin2 = 0xFFFADBD8,
+ Alizarin3 = 0xFFF5B7B1,
+ Alizarin4 = 0xFFF1948A,
+ Alizarin5 = 0xFFEC7063,
+ Alizarin6 = 0xFFE74C3C,
+ Alizarin7 = 0xFFCB4335,
+ Alizarin8 = 0xFFB03A2E,
+ Alizarin9 = 0xFF943126,
+ Alizarin10 = 0xFF78281F,
// Amethyst
- {
- Color.FromArgb(0xFF, 0xF5, 0xEE, 0xF8),
- Color.FromArgb(0xFF, 0xD7, 0xBD, 0xE2),
- Color.FromArgb(0xFF, 0xAF, 0x7A, 0xC5),
- Color.FromArgb(0xFF, 0x88, 0x4E, 0xA0),
- Color.FromArgb(0xFF, 0x63, 0x39, 0x74),
- },
+ Amethyst1 = 0xFFF5EEF8,
+ Amethyst2 = 0xFFEBDEF0,
+ Amethyst3 = 0xFFD7BDE2,
+ Amethyst4 = 0xFFC39BD3,
+ Amethyst5 = 0xFFAF7AC5,
+ Amethyst6 = 0xFF9B59B6,
+ Amethyst7 = 0xFF884EA0,
+ Amethyst8 = 0xFF76448A,
+ Amethyst9 = 0xFF633974,
+ Amethyst10 = 0xFF512E5F,
+
+ // Wisteria
+ Wisteria1 = 0xFFF4ECF7,
+ Wisteria2 = 0xFFE8DAEF,
+ Wisteria3 = 0xFFD2B4DE,
+ Wisteria4 = 0xFFBB8FCE,
+ Wisteria5 = 0xFFA569BD,
+ Wisteria6 = 0xFF8E44AD,
+ Wisteria7 = 0xFF7D3C98,
+ Wisteria8 = 0xFF6C3483,
+ Wisteria9 = 0xFF5B2C6F,
+ Wisteria10 = 0xFF4A235A,
// Belize Hole
- {
- Color.FromArgb(0xFF, 0xEA, 0xF2, 0xF8),
- Color.FromArgb(0xFF, 0xA9, 0xCC, 0xE3),
- Color.FromArgb(0xFF, 0x54, 0x99, 0xC7),
- Color.FromArgb(0xFF, 0x24, 0x71, 0xA3),
- Color.FromArgb(0xFF, 0x1A, 0x52, 0x76),
- },
+ BelizeHole1 = 0xFFEAF2F8,
+ BelizeHole2 = 0xFFD4E6F1,
+ BelizeHole3 = 0xFFA9CCE3,
+ BelizeHole4 = 0xFF7FB3D5,
+ BelizeHole5 = 0xFF5499C7,
+ BelizeHole6 = 0xFF2980B9,
+ BelizeHole7 = 0xFF2471A3,
+ BelizeHole8 = 0xFF1F618D,
+ BelizeHole9 = 0xFF1A5276,
+ BelizeHole10 = 0xFF154360,
+
+ // Peter River
+ PeterRiver1 = 0xFFEBF5FB,
+ PeterRiver2 = 0xFFD6EAF8,
+ PeterRiver3 = 0xFFAED6F1,
+ PeterRiver4 = 0xFF85C1E9,
+ PeterRiver5 = 0xFF5DADE2,
+ PeterRiver6 = 0xFF3498DB,
+ PeterRiver7 = 0xFF2E86C1,
+ PeterRiver8 = 0xFF2874A6,
+ PeterRiver9 = 0xFF21618C,
+ PeterRiver10 = 0xFF1B4F72,
// Turquoise
- {
- Color.FromArgb(0xFF, 0xE8, 0xF8, 0xF5),
- Color.FromArgb(0xFF, 0xA3, 0xE4, 0xD7),
- Color.FromArgb(0xFF, 0x48, 0xC9, 0xB0),
- Color.FromArgb(0xFF, 0x17, 0xA5, 0x89),
- Color.FromArgb(0xFF, 0x11, 0x78, 0x64),
- },
+ Turquoise1 = 0xFFE8F8F5,
+ Turquoise2 = 0xFFD1F2EB,
+ Turquoise3 = 0xFFA3E4D7,
+ Turquoise4 = 0xFF76D7C4,
+ Turquoise5 = 0xFF48C9B0,
+ Turquoise6 = 0xFF1ABC9C,
+ Turquoise7 = 0xFF17A589,
+ Turquoise8 = 0xFF148F77,
+ Turquoise9 = 0xFF117864,
+ Turquoise10 = 0xFF0E6251,
+
+ // Green Sea
+ GreenSea1 = 0xFFE8F6F3,
+ GreenSea2 = 0xFFD0ECE7,
+ GreenSea3 = 0xFFA2D9CE,
+ GreenSea4 = 0xFF73C6B6,
+ GreenSea5 = 0xFF45B39D,
+ GreenSea6 = 0xFF16A085,
+ GreenSea7 = 0xFF138D75,
+ GreenSea8 = 0xFF117A65,
+ GreenSea9 = 0xFF0E6655,
+ GreenSea10 = 0xFF0B5345,
// Nephritis
- {
- Color.FromArgb(0xFF, 0xE9, 0xF7, 0xEF),
- Color.FromArgb(0xFF, 0xA9, 0xDF, 0xBF),
- Color.FromArgb(0xFF, 0x52, 0xBE, 0x80),
- Color.FromArgb(0xFF, 0x22, 0x99, 0x54),
- Color.FromArgb(0xFF, 0x19, 0x6F, 0x3D),
- },
+ Nephritis1 = 0xFFE9F7EF,
+ Nephritis2 = 0xFFD4EFDF,
+ Nephritis3 = 0xFFA9DFBF,
+ Nephritis4 = 0xFF7DCEA0,
+ Nephritis5 = 0xFF52BE80,
+ Nephritis6 = 0xFF27AE60,
+ Nephritis7 = 0xFF229954,
+ Nephritis8 = 0xFF1E8449,
+ Nephritis9 = 0xFF196F3D,
+ Nephritis10 = 0xFF145A32,
+
+ // Emerald
+ Emerald1 = 0xFFEAFAF1,
+ Emerald2 = 0xFFD5F5E3,
+ Emerald3 = 0xFFABEBC6,
+ Emerald4 = 0xFF82E0AA,
+ Emerald5 = 0xFF58D68D,
+ Emerald6 = 0xFF2ECC71,
+ Emerald7 = 0xFF28B463,
+ Emerald8 = 0xFF239B56,
+ Emerald9 = 0xFF1D8348,
+ Emerald10 = 0xFF186A3B,
// Sunflower
- {
- Color.FromArgb(0xFF, 0xFE, 0xF9, 0xE7),
- Color.FromArgb(0xFF, 0xF9, 0xE7, 0x9F),
- Color.FromArgb(0xFF, 0xF4, 0xD0, 0x3F),
- Color.FromArgb(0xFF, 0xD4, 0xAC, 0x0D),
- Color.FromArgb(0xFF, 0x9A, 0x7D, 0x0A),
- },
+ Sunflower1 = 0xFFFEF9E7,
+ Sunflower2 = 0xFFFCF3CF,
+ Sunflower3 = 0xFFF9E79F,
+ Sunflower4 = 0xFFF7DC6F,
+ Sunflower5 = 0xFFF4D03F,
+ Sunflower6 = 0xFFF1C40F,
+ Sunflower7 = 0xFFD4AC0D,
+ Sunflower8 = 0xFFB7950B,
+ Sunflower9 = 0xFF9A7D0A,
+ Sunflower10 = 0xFF7D6608,
+
+ // Orange
+ Orange1 = 0xFFFEF5E7,
+ Orange2 = 0xFFFDEBD0,
+ Orange3 = 0xFFFAD7A0,
+ Orange4 = 0xFFF8C471,
+ Orange5 = 0xFFF5B041,
+ Orange6 = 0xFFF39C12,
+ Orange7 = 0xFFD68910,
+ Orange8 = 0xFFB9770E,
+ Orange9 = 0xFF9C640C,
+ Orange10 = 0xFF7E5109,
// Carrot
- {
- Color.FromArgb(0xFF, 0xFD, 0xF2, 0xE9),
- Color.FromArgb(0xFF, 0xF5, 0xCB, 0xA7),
- Color.FromArgb(0xFF, 0xEB, 0x98, 0x4E),
- Color.FromArgb(0xFF, 0xCA, 0x6F, 0x1E),
- Color.FromArgb(0xFF, 0x93, 0x51, 0x16),
- },
+ Carrot1 = 0xFFFDF2E9,
+ Carrot2 = 0xFFFAE5D3,
+ Carrot3 = 0xFFF5CBA7,
+ Carrot4 = 0xFFF0B27A,
+ Carrot5 = 0xFFEB984E,
+ Carrot6 = 0xFFE67E22,
+ Carrot7 = 0xFFCA6F1E,
+ Carrot8 = 0xFFAF601A,
+ Carrot9 = 0xFF935116,
+ Carrot10 = 0xFF784212,
+
+ // Pumpkin
+ Pumpkin1 = 0xFFFBEEE6,
+ Pumpkin2 = 0xFFF6DDCC,
+ Pumpkin3 = 0xFFEDBB99,
+ Pumpkin4 = 0xFFE59866,
+ Pumpkin5 = 0xFFDC7633,
+ Pumpkin6 = 0xFFD35400,
+ Pumpkin7 = 0xFFBA4A00,
+ Pumpkin8 = 0xFFA04000,
+ Pumpkin9 = 0xFF873600,
+ Pumpkin10 = 0xFF6E2C00,
// Clouds
- {
- Color.FromArgb(0xFF, 0xFD, 0xFE, 0xFE),
- Color.FromArgb(0xFF, 0xF7, 0xF9, 0xF9),
- Color.FromArgb(0xFF, 0xF0, 0xF3, 0xF4),
- Color.FromArgb(0xFF, 0xD0, 0xD3, 0xD4),
- Color.FromArgb(0xFF, 0x97, 0x9A, 0x9A),
- },
+ Clouds1 = 0xFFFDFEFE,
+ Clouds2 = 0xFFFBFCFC,
+ Clouds3 = 0xFFF7F9F9,
+ Clouds4 = 0xFFF4F6F7,
+ Clouds5 = 0xFFF0F3F4,
+ Clouds6 = 0xFFECF0F1,
+ Clouds7 = 0xFFD0D3D4,
+ Clouds8 = 0xFFB3B6B7,
+ Clouds9 = 0xFF979A9A,
+ Clouds10 = 0xFF7B7D7D,
+
+ // Silver
+ Silver1 = 0xFFF8F9F9,
+ Silver2 = 0xFFF2F3F4,
+ Silver3 = 0xFFE5E7E9,
+ Silver4 = 0xFFD7DBDD,
+ Silver5 = 0xFFCACFD2,
+ Silver6 = 0xFFBDC3C7,
+ Silver7 = 0xFFA6ACAF,
+ Silver8 = 0xFF909497,
+ Silver9 = 0xFF797D7F,
+ Silver10 = 0xFF626567,
// Concrete
- {
- Color.FromArgb(0xFF, 0xF4, 0xF6, 0xF6),
- Color.FromArgb(0xFF, 0xD5, 0xDB, 0xDB),
- Color.FromArgb(0xFF, 0xAA, 0xB7, 0xB8),
- Color.FromArgb(0xFF, 0x83, 0x91, 0x92),
- Color.FromArgb(0xFF, 0x5F, 0x6A, 0x6A),
- },
+ Concrete1 = 0xFFF4F6F6,
+ Concrete2 = 0xFFEAEDED,
+ Concrete3 = 0xFFD5DBDB,
+ Concrete4 = 0xFFBFC9CA,
+ Concrete5 = 0xFFAAB7B8,
+ Concrete6 = 0xFF95A5A6,
+ Concrete7 = 0xFF839192,
+ Concrete8 = 0xFF717D7E,
+ Concrete9 = 0xFF5F6A6A,
+ Concrete10 = 0xFF4D5656,
+
+ // Asbestos
+ Asbestos1 = 0xFFF2F4F4,
+ Asbestos2 = 0xFFE5E8E8,
+ Asbestos3 = 0xFFCCD1D1,
+ Asbestos4 = 0xFFB2BABB,
+ Asbestos5 = 0xFF99A3A4,
+ Asbestos6 = 0xFF7F8C8D,
+ Asbestos7 = 0xFF707B7C,
+ Asbestos8 = 0xFF616A6B,
+ Asbestos9 = 0xFF515A5A,
+ Asbestos10 = 0xFF424949,
// Wet Asphalt
- {
- Color.FromArgb(0xFF, 0xEB, 0xED, 0xEF),
- Color.FromArgb(0xFF, 0xAE, 0xB6, 0xBF),
- Color.FromArgb(0xFF, 0x5D, 0x6D, 0x7E),
- Color.FromArgb(0xFF, 0x2E, 0x40, 0x53),
- Color.FromArgb(0xFF, 0x21, 0x2F, 0x3C),
- },
+ WetAsphalt1 = 0xFFEBEDEF,
+ WetAsphalt2 = 0xFFD6DBDF,
+ WetAsphalt3 = 0xFFAEB6BF,
+ WetAsphalt4 = 0xFF85929E,
+ WetAsphalt5 = 0xFF5D6D7E,
+ WetAsphalt6 = 0xFF34495E,
+ WetAsphalt7 = 0xFF2E4053,
+ WetAsphalt8 = 0xFF283747,
+ WetAsphalt9 = 0xFF212F3C,
+ WetAsphalt10 = 0xFF1B2631,
+
+ // Midnight Blue
+ MidnightBlue1 = 0xFFEAECEE,
+ MidnightBlue2 = 0xFFD5D8DC,
+ MidnightBlue3 = 0xFFABB2B9,
+ MidnightBlue4 = 0xFF808B96,
+ MidnightBlue5 = 0xFF566573,
+ MidnightBlue6 = 0xFF2C3E50,
+ MidnightBlue7 = 0xFF273746,
+ MidnightBlue8 = 0xFF212F3D,
+ MidnightBlue9 = 0xFF1C2833,
+ MidnightBlue10 = 0xFF17202A,
+
+ Pomegranate = Pomegranate3,
+ Alizarin = Alizarin3,
+ Amethyst = Amethyst3,
+ Wisteria = Wisteria3,
+ BelizeHole = BelizeHole3,
+ PeterRiver = PeterRiver3,
+ Turquoise = Turquoise3,
+ GreenSea = GreenSea3,
+ Nephritis = Nephritis3,
+ Emerald = Emerald3,
+ Sunflower = Sunflower3,
+ Orange = Orange3,
+ Carrot = Carrot3,
+ Pumpkin = Pumpkin3,
+ Clouds = Clouds3,
+ Silver = Silver3,
+ Concrete = Concrete3,
+ Asbestos = Asbestos3,
+ WetAsphalt = WetAsphalt3,
+ MidnightBlue = MidnightBlue3,
};
- ///
- /// Gets the index of the default shade of colors in this palette.
- ///
- public const int DefaultShadeIndex = 2;
-
- ///
- /// The index in the color palette of the 'Pomegranate' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int PomegranateIndex = 0;
-
- ///
- /// The index in the color palette of the 'Amethyst' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int AmethystIndex = 1;
-
- ///
- /// The index in the color palette of the 'BelizeHole' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int BelizeHoleIndex = 2;
-
- ///
- /// The index in the color palette of the 'Turquoise' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int TurquoiseIndex = 3;
-
- ///
- /// The index in the color palette of the 'Nephritis' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int NephritisIndex = 4;
-
- ///
- /// The index in the color palette of the 'Sunflower' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int SunflowerIndex = 5;
-
- ///
- /// The index in the color palette of the 'Carrot' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int CarrotIndex = 6;
-
- ///
- /// The index in the color palette of the 'Clouds' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int CloudsIndex = 7;
-
- ///
- /// The index in the color palette of the 'Concrete' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int ConcreteIndex = 8;
+ // See: https://htmlcolorcodes.com/assets/downloads/flat-design-colors/flat-design-color-chart.png
+ protected static Color[,]? _colorChart = null;
+ protected static object _colorChartMutex = new object();
///
- /// The index in the color palette of the 'WetAsphalt' color.
- /// This index can correspond to multiple color shades.
+ /// Initializes all color chart colors.
///
- public const int WetAsphaltIndex = 9;
+ protected void InitColorChart()
+ {
+ lock (_colorChartMutex)
+ {
+ if (_colorChart != null)
+ {
+ return;
+ }
+
+ _colorChart = new Color[,]
+ {
+ // Pomegranate
+ {
+ Color.FromUInt32((uint)FlatColor.Pomegranate1),
+ Color.FromUInt32((uint)FlatColor.Pomegranate2),
+ Color.FromUInt32((uint)FlatColor.Pomegranate3),
+ Color.FromUInt32((uint)FlatColor.Pomegranate4),
+ Color.FromUInt32((uint)FlatColor.Pomegranate5),
+ Color.FromUInt32((uint)FlatColor.Pomegranate6),
+ Color.FromUInt32((uint)FlatColor.Pomegranate7),
+ Color.FromUInt32((uint)FlatColor.Pomegranate8),
+ Color.FromUInt32((uint)FlatColor.Pomegranate9),
+ Color.FromUInt32((uint)FlatColor.Pomegranate10),
+ },
+
+ // Alizarin
+ {
+ Color.FromUInt32((uint)FlatColor.Alizarin1),
+ Color.FromUInt32((uint)FlatColor.Alizarin2),
+ Color.FromUInt32((uint)FlatColor.Alizarin3),
+ Color.FromUInt32((uint)FlatColor.Alizarin4),
+ Color.FromUInt32((uint)FlatColor.Alizarin5),
+ Color.FromUInt32((uint)FlatColor.Alizarin6),
+ Color.FromUInt32((uint)FlatColor.Alizarin7),
+ Color.FromUInt32((uint)FlatColor.Alizarin8),
+ Color.FromUInt32((uint)FlatColor.Alizarin9),
+ Color.FromUInt32((uint)FlatColor.Alizarin10),
+ },
+
+ // Amethyst
+ {
+ Color.FromUInt32((uint)FlatColor.Amethyst1),
+ Color.FromUInt32((uint)FlatColor.Amethyst2),
+ Color.FromUInt32((uint)FlatColor.Amethyst3),
+ Color.FromUInt32((uint)FlatColor.Amethyst4),
+ Color.FromUInt32((uint)FlatColor.Amethyst5),
+ Color.FromUInt32((uint)FlatColor.Amethyst6),
+ Color.FromUInt32((uint)FlatColor.Amethyst7),
+ Color.FromUInt32((uint)FlatColor.Amethyst8),
+ Color.FromUInt32((uint)FlatColor.Amethyst9),
+ Color.FromUInt32((uint)FlatColor.Amethyst10),
+ },
+
+ // Wisteria
+ {
+ Color.FromUInt32((uint)FlatColor.Wisteria1),
+ Color.FromUInt32((uint)FlatColor.Wisteria2),
+ Color.FromUInt32((uint)FlatColor.Wisteria3),
+ Color.FromUInt32((uint)FlatColor.Wisteria4),
+ Color.FromUInt32((uint)FlatColor.Wisteria5),
+ Color.FromUInt32((uint)FlatColor.Wisteria6),
+ Color.FromUInt32((uint)FlatColor.Wisteria7),
+ Color.FromUInt32((uint)FlatColor.Wisteria8),
+ Color.FromUInt32((uint)FlatColor.Wisteria9),
+ Color.FromUInt32((uint)FlatColor.Wisteria10),
+ },
+
+ // Belize Hole
+ {
+ Color.FromUInt32((uint)FlatColor.BelizeHole1),
+ Color.FromUInt32((uint)FlatColor.BelizeHole2),
+ Color.FromUInt32((uint)FlatColor.BelizeHole3),
+ Color.FromUInt32((uint)FlatColor.BelizeHole4),
+ Color.FromUInt32((uint)FlatColor.BelizeHole5),
+ Color.FromUInt32((uint)FlatColor.BelizeHole6),
+ Color.FromUInt32((uint)FlatColor.BelizeHole7),
+ Color.FromUInt32((uint)FlatColor.BelizeHole8),
+ Color.FromUInt32((uint)FlatColor.BelizeHole9),
+ Color.FromUInt32((uint)FlatColor.BelizeHole10),
+ },
+
+ // Peter River
+ {
+ Color.FromUInt32((uint)FlatColor.PeterRiver1),
+ Color.FromUInt32((uint)FlatColor.PeterRiver2),
+ Color.FromUInt32((uint)FlatColor.PeterRiver3),
+ Color.FromUInt32((uint)FlatColor.PeterRiver4),
+ Color.FromUInt32((uint)FlatColor.PeterRiver5),
+ Color.FromUInt32((uint)FlatColor.PeterRiver6),
+ Color.FromUInt32((uint)FlatColor.PeterRiver7),
+ Color.FromUInt32((uint)FlatColor.PeterRiver8),
+ Color.FromUInt32((uint)FlatColor.PeterRiver9),
+ Color.FromUInt32((uint)FlatColor.PeterRiver10),
+ },
+
+ // Turquoise
+ {
+ Color.FromUInt32((uint)FlatColor.Turquoise1),
+ Color.FromUInt32((uint)FlatColor.Turquoise2),
+ Color.FromUInt32((uint)FlatColor.Turquoise3),
+ Color.FromUInt32((uint)FlatColor.Turquoise4),
+ Color.FromUInt32((uint)FlatColor.Turquoise5),
+ Color.FromUInt32((uint)FlatColor.Turquoise6),
+ Color.FromUInt32((uint)FlatColor.Turquoise7),
+ Color.FromUInt32((uint)FlatColor.Turquoise8),
+ Color.FromUInt32((uint)FlatColor.Turquoise9),
+ Color.FromUInt32((uint)FlatColor.Turquoise10),
+ },
+
+ // Green Sea
+ {
+ Color.FromUInt32((uint)FlatColor.GreenSea1),
+ Color.FromUInt32((uint)FlatColor.GreenSea2),
+ Color.FromUInt32((uint)FlatColor.GreenSea3),
+ Color.FromUInt32((uint)FlatColor.GreenSea4),
+ Color.FromUInt32((uint)FlatColor.GreenSea5),
+ Color.FromUInt32((uint)FlatColor.GreenSea6),
+ Color.FromUInt32((uint)FlatColor.GreenSea7),
+ Color.FromUInt32((uint)FlatColor.GreenSea8),
+ Color.FromUInt32((uint)FlatColor.GreenSea9),
+ Color.FromUInt32((uint)FlatColor.GreenSea10),
+ },
+
+ // Nephritis
+ {
+ Color.FromUInt32((uint)FlatColor.Nephritis1),
+ Color.FromUInt32((uint)FlatColor.Nephritis2),
+ Color.FromUInt32((uint)FlatColor.Nephritis3),
+ Color.FromUInt32((uint)FlatColor.Nephritis4),
+ Color.FromUInt32((uint)FlatColor.Nephritis5),
+ Color.FromUInt32((uint)FlatColor.Nephritis6),
+ Color.FromUInt32((uint)FlatColor.Nephritis7),
+ Color.FromUInt32((uint)FlatColor.Nephritis8),
+ Color.FromUInt32((uint)FlatColor.Nephritis9),
+ Color.FromUInt32((uint)FlatColor.Nephritis10),
+ },
+
+ // Emerald
+ {
+ Color.FromUInt32((uint)FlatColor.Emerald1),
+ Color.FromUInt32((uint)FlatColor.Emerald2),
+ Color.FromUInt32((uint)FlatColor.Emerald3),
+ Color.FromUInt32((uint)FlatColor.Emerald4),
+ Color.FromUInt32((uint)FlatColor.Emerald5),
+ Color.FromUInt32((uint)FlatColor.Emerald6),
+ Color.FromUInt32((uint)FlatColor.Emerald7),
+ Color.FromUInt32((uint)FlatColor.Emerald8),
+ Color.FromUInt32((uint)FlatColor.Emerald9),
+ Color.FromUInt32((uint)FlatColor.Emerald10),
+ },
+
+ // Sunflower
+ {
+ Color.FromUInt32((uint)FlatColor.Sunflower1),
+ Color.FromUInt32((uint)FlatColor.Sunflower2),
+ Color.FromUInt32((uint)FlatColor.Sunflower3),
+ Color.FromUInt32((uint)FlatColor.Sunflower4),
+ Color.FromUInt32((uint)FlatColor.Sunflower5),
+ Color.FromUInt32((uint)FlatColor.Sunflower6),
+ Color.FromUInt32((uint)FlatColor.Sunflower7),
+ Color.FromUInt32((uint)FlatColor.Sunflower8),
+ Color.FromUInt32((uint)FlatColor.Sunflower9),
+ Color.FromUInt32((uint)FlatColor.Sunflower10),
+ },
+
+ // Orange
+ {
+ Color.FromUInt32((uint)FlatColor.Orange1),
+ Color.FromUInt32((uint)FlatColor.Orange2),
+ Color.FromUInt32((uint)FlatColor.Orange3),
+ Color.FromUInt32((uint)FlatColor.Orange4),
+ Color.FromUInt32((uint)FlatColor.Orange5),
+ Color.FromUInt32((uint)FlatColor.Orange6),
+ Color.FromUInt32((uint)FlatColor.Orange7),
+ Color.FromUInt32((uint)FlatColor.Orange8),
+ Color.FromUInt32((uint)FlatColor.Orange9),
+ Color.FromUInt32((uint)FlatColor.Orange10),
+ },
+
+ // Carrot
+ {
+ Color.FromUInt32((uint)FlatColor.Carrot1),
+ Color.FromUInt32((uint)FlatColor.Carrot2),
+ Color.FromUInt32((uint)FlatColor.Carrot3),
+ Color.FromUInt32((uint)FlatColor.Carrot4),
+ Color.FromUInt32((uint)FlatColor.Carrot5),
+ Color.FromUInt32((uint)FlatColor.Carrot6),
+ Color.FromUInt32((uint)FlatColor.Carrot7),
+ Color.FromUInt32((uint)FlatColor.Carrot8),
+ Color.FromUInt32((uint)FlatColor.Carrot9),
+ Color.FromUInt32((uint)FlatColor.Carrot10),
+ },
+
+ // Pumpkin
+ {
+ Color.FromUInt32((uint)FlatColor.Pumpkin1),
+ Color.FromUInt32((uint)FlatColor.Pumpkin2),
+ Color.FromUInt32((uint)FlatColor.Pumpkin3),
+ Color.FromUInt32((uint)FlatColor.Pumpkin4),
+ Color.FromUInt32((uint)FlatColor.Pumpkin5),
+ Color.FromUInt32((uint)FlatColor.Pumpkin6),
+ Color.FromUInt32((uint)FlatColor.Pumpkin7),
+ Color.FromUInt32((uint)FlatColor.Pumpkin8),
+ Color.FromUInt32((uint)FlatColor.Pumpkin9),
+ Color.FromUInt32((uint)FlatColor.Pumpkin10),
+ },
+
+ // Clouds
+ {
+ Color.FromUInt32((uint)FlatColor.Clouds1),
+ Color.FromUInt32((uint)FlatColor.Clouds2),
+ Color.FromUInt32((uint)FlatColor.Clouds3),
+ Color.FromUInt32((uint)FlatColor.Clouds4),
+ Color.FromUInt32((uint)FlatColor.Clouds5),
+ Color.FromUInt32((uint)FlatColor.Clouds6),
+ Color.FromUInt32((uint)FlatColor.Clouds7),
+ Color.FromUInt32((uint)FlatColor.Clouds8),
+ Color.FromUInt32((uint)FlatColor.Clouds9),
+ Color.FromUInt32((uint)FlatColor.Clouds10),
+ },
+
+ // Silver
+ {
+ Color.FromUInt32((uint)FlatColor.Silver1),
+ Color.FromUInt32((uint)FlatColor.Silver2),
+ Color.FromUInt32((uint)FlatColor.Silver3),
+ Color.FromUInt32((uint)FlatColor.Silver4),
+ Color.FromUInt32((uint)FlatColor.Silver5),
+ Color.FromUInt32((uint)FlatColor.Silver6),
+ Color.FromUInt32((uint)FlatColor.Silver7),
+ Color.FromUInt32((uint)FlatColor.Silver8),
+ Color.FromUInt32((uint)FlatColor.Silver9),
+ Color.FromUInt32((uint)FlatColor.Silver10),
+ },
+
+ // Concrete
+ {
+ Color.FromUInt32((uint)FlatColor.Concrete1),
+ Color.FromUInt32((uint)FlatColor.Concrete2),
+ Color.FromUInt32((uint)FlatColor.Concrete3),
+ Color.FromUInt32((uint)FlatColor.Concrete4),
+ Color.FromUInt32((uint)FlatColor.Concrete5),
+ Color.FromUInt32((uint)FlatColor.Concrete6),
+ Color.FromUInt32((uint)FlatColor.Concrete7),
+ Color.FromUInt32((uint)FlatColor.Concrete8),
+ Color.FromUInt32((uint)FlatColor.Concrete9),
+ Color.FromUInt32((uint)FlatColor.Concrete10),
+ },
+
+ // Asbestos
+ {
+ Color.FromUInt32((uint)FlatColor.Asbestos1),
+ Color.FromUInt32((uint)FlatColor.Asbestos2),
+ Color.FromUInt32((uint)FlatColor.Asbestos3),
+ Color.FromUInt32((uint)FlatColor.Asbestos4),
+ Color.FromUInt32((uint)FlatColor.Asbestos5),
+ Color.FromUInt32((uint)FlatColor.Asbestos6),
+ Color.FromUInt32((uint)FlatColor.Asbestos7),
+ Color.FromUInt32((uint)FlatColor.Asbestos8),
+ Color.FromUInt32((uint)FlatColor.Asbestos9),
+ Color.FromUInt32((uint)FlatColor.Asbestos10),
+ },
+
+ // Wet Asphalt
+ {
+ Color.FromUInt32((uint)FlatColor.WetAsphalt1),
+ Color.FromUInt32((uint)FlatColor.WetAsphalt2),
+ Color.FromUInt32((uint)FlatColor.WetAsphalt3),
+ Color.FromUInt32((uint)FlatColor.WetAsphalt4),
+ Color.FromUInt32((uint)FlatColor.WetAsphalt5),
+ Color.FromUInt32((uint)FlatColor.WetAsphalt6),
+ Color.FromUInt32((uint)FlatColor.WetAsphalt7),
+ Color.FromUInt32((uint)FlatColor.WetAsphalt8),
+ Color.FromUInt32((uint)FlatColor.WetAsphalt9),
+ Color.FromUInt32((uint)FlatColor.WetAsphalt10),
+ },
+
+ // Midnight Blue
+ {
+ Color.FromUInt32((uint)FlatColor.MidnightBlue1),
+ Color.FromUInt32((uint)FlatColor.MidnightBlue2),
+ Color.FromUInt32((uint)FlatColor.MidnightBlue3),
+ Color.FromUInt32((uint)FlatColor.MidnightBlue4),
+ Color.FromUInt32((uint)FlatColor.MidnightBlue5),
+ Color.FromUInt32((uint)FlatColor.MidnightBlue6),
+ Color.FromUInt32((uint)FlatColor.MidnightBlue7),
+ Color.FromUInt32((uint)FlatColor.MidnightBlue8),
+ Color.FromUInt32((uint)FlatColor.MidnightBlue9),
+ Color.FromUInt32((uint)FlatColor.MidnightBlue10),
+ },
+ };
+ }
+
+ return;
+ }
///
public int ColorCount
{
- // Table is transposed compared to the reference chart
- get => colorChart.GetLength(0);
+ get => 20;
}
///
public int ShadeCount
{
- // Table is transposed compared to the reference chart
- get => colorChart.GetLength(1);
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FFC0392B.
- ///
- public static Color Pomegranate
- {
- get => colorChart[PomegranateIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FF9B59B6.
- ///
- public static Color Amethyst
- {
- get => colorChart[AmethystIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FF2980B9.
- ///
- public static Color BelizeHole
- {
- get => colorChart[BelizeHoleIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FF1ABC9C.
- ///
- public static Color Turquoise
- {
- get => colorChart[TurquoiseIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FF27AE60.
- ///
- public static Color Nephritis
- {
- get => colorChart[NephritisIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FFF1C40F.
- ///
- public static Color Sunflower
- {
- get => colorChart[SunflowerIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FFE67E22.
- ///
- public static Color Carrot
- {
- get => colorChart[CarrotIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FFECF0F1.
- ///
- public static Color Clouds
- {
- get => colorChart[CloudsIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FF95A5A6.
- ///
- public static Color Concrete
- {
- get => colorChart[ConcreteIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FF34495E.
- ///
- public static Color WetAsphalt
- {
- get => colorChart[WetAsphaltIndex, DefaultShadeIndex];
+ get => 10;
}
///
public Color GetColor(int colorIndex, int shadeIndex)
{
- // Table is transposed compared to the reference chart
- return colorChart[
- MathUtilities.Clamp(colorIndex, 0, colorChart.GetLength(0) - 1),
- MathUtilities.Clamp(shadeIndex, 0, colorChart.GetLength(1) - 1)];
+ if (_colorChart == null)
+ {
+ InitColorChart();
+ }
+
+ return _colorChart![
+ MathUtilities.Clamp(colorIndex, 0, ColorCount - 1),
+ MathUtilities.Clamp(shadeIndex, 0, ShadeCount - 1)];
}
}
}
diff --git a/src/Avalonia.Controls.ColorPicker/ColorPalettes/FlatHalfColorPalette.cs b/src/Avalonia.Controls.ColorPicker/ColorPalettes/FlatHalfColorPalette.cs
new file mode 100644
index 0000000000..2758124fae
--- /dev/null
+++ b/src/Avalonia.Controls.ColorPicker/ColorPalettes/FlatHalfColorPalette.cs
@@ -0,0 +1,150 @@
+using Avalonia.Media;
+using Avalonia.Utilities;
+using FlatColor = Avalonia.Controls.FlatColorPalette.FlatColor;
+
+namespace Avalonia.Controls
+{
+ ///
+ /// Implements half of the for improved usability.
+ ///
+ ///
+ public class FlatHalfColorPalette : IColorPalette
+ {
+ protected static Color[,]? _colorChart = null;
+ protected static object _colorChartMutex = new object();
+
+ ///
+ /// Initializes all color chart colors.
+ ///
+ protected void InitColorChart()
+ {
+ lock (_colorChartMutex)
+ {
+ if (_colorChart != null)
+ {
+ return;
+ }
+
+ _colorChart = new Color[,]
+ {
+ // Pomegranate
+ {
+ Color.FromUInt32((uint)FlatColor.Pomegranate1),
+ Color.FromUInt32((uint)FlatColor.Pomegranate3),
+ Color.FromUInt32((uint)FlatColor.Pomegranate5),
+ Color.FromUInt32((uint)FlatColor.Pomegranate7),
+ Color.FromUInt32((uint)FlatColor.Pomegranate9),
+ },
+
+ // Amethyst
+ {
+ Color.FromUInt32((uint)FlatColor.Amethyst1),
+ Color.FromUInt32((uint)FlatColor.Amethyst3),
+ Color.FromUInt32((uint)FlatColor.Amethyst5),
+ Color.FromUInt32((uint)FlatColor.Amethyst7),
+ Color.FromUInt32((uint)FlatColor.Amethyst9),
+ },
+
+ // Belize Hole
+ {
+ Color.FromUInt32((uint)FlatColor.BelizeHole1),
+ Color.FromUInt32((uint)FlatColor.BelizeHole3),
+ Color.FromUInt32((uint)FlatColor.BelizeHole5),
+ Color.FromUInt32((uint)FlatColor.BelizeHole7),
+ Color.FromUInt32((uint)FlatColor.BelizeHole9),
+ },
+
+ // Turquoise
+ {
+ Color.FromUInt32((uint)FlatColor.Turquoise1),
+ Color.FromUInt32((uint)FlatColor.Turquoise3),
+ Color.FromUInt32((uint)FlatColor.Turquoise5),
+ Color.FromUInt32((uint)FlatColor.Turquoise7),
+ Color.FromUInt32((uint)FlatColor.Turquoise9),
+ },
+
+ // Nephritis
+ {
+ Color.FromUInt32((uint)FlatColor.Nephritis1),
+ Color.FromUInt32((uint)FlatColor.Nephritis3),
+ Color.FromUInt32((uint)FlatColor.Nephritis5),
+ Color.FromUInt32((uint)FlatColor.Nephritis7),
+ Color.FromUInt32((uint)FlatColor.Nephritis9),
+ },
+
+ // Sunflower
+ {
+ Color.FromUInt32((uint)FlatColor.Sunflower1),
+ Color.FromUInt32((uint)FlatColor.Sunflower3),
+ Color.FromUInt32((uint)FlatColor.Sunflower5),
+ Color.FromUInt32((uint)FlatColor.Sunflower7),
+ Color.FromUInt32((uint)FlatColor.Sunflower9),
+ },
+
+ // Carrot
+ {
+ Color.FromUInt32((uint)FlatColor.Carrot1),
+ Color.FromUInt32((uint)FlatColor.Carrot3),
+ Color.FromUInt32((uint)FlatColor.Carrot5),
+ Color.FromUInt32((uint)FlatColor.Carrot7),
+ Color.FromUInt32((uint)FlatColor.Carrot9),
+ },
+
+ // Clouds
+ {
+ Color.FromUInt32((uint)FlatColor.Clouds1),
+ Color.FromUInt32((uint)FlatColor.Clouds3),
+ Color.FromUInt32((uint)FlatColor.Clouds5),
+ Color.FromUInt32((uint)FlatColor.Clouds7),
+ Color.FromUInt32((uint)FlatColor.Clouds9),
+ },
+
+ // Concrete
+ {
+ Color.FromUInt32((uint)FlatColor.Concrete1),
+ Color.FromUInt32((uint)FlatColor.Concrete3),
+ Color.FromUInt32((uint)FlatColor.Concrete5),
+ Color.FromUInt32((uint)FlatColor.Concrete7),
+ Color.FromUInt32((uint)FlatColor.Concrete9),
+ },
+
+ // Wet Asphalt
+ {
+ Color.FromUInt32((uint)FlatColor.WetAsphalt1),
+ Color.FromUInt32((uint)FlatColor.WetAsphalt3),
+ Color.FromUInt32((uint)FlatColor.WetAsphalt5),
+ Color.FromUInt32((uint)FlatColor.WetAsphalt7),
+ Color.FromUInt32((uint)FlatColor.WetAsphalt9),
+ },
+ };
+ }
+
+ return;
+ }
+
+ ///
+ public int ColorCount
+ {
+ get => 10;
+ }
+
+ ///
+ public int ShadeCount
+ {
+ get => 5;
+ }
+
+ ///
+ public Color GetColor(int colorIndex, int shadeIndex)
+ {
+ if (_colorChart == null)
+ {
+ InitColorChart();
+ }
+
+ return _colorChart![
+ MathUtilities.Clamp(colorIndex, 0, ColorCount - 1),
+ MathUtilities.Clamp(shadeIndex, 0, ShadeCount - 1)];
+ }
+ }
+}
diff --git a/src/Avalonia.Controls.ColorPicker/ColorPalettes/MaterialColorPalette.cs b/src/Avalonia.Controls.ColorPicker/ColorPalettes/MaterialColorPalette.cs
new file mode 100644
index 0000000000..8fa7ede77e
--- /dev/null
+++ b/src/Avalonia.Controls.ColorPicker/ColorPalettes/MaterialColorPalette.cs
@@ -0,0 +1,577 @@
+using Avalonia.Media;
+using Avalonia.Utilities;
+
+namespace Avalonia.Controls
+{
+ ///
+ /// Implements a reduced version of the 2014 Material Design color palette.
+ ///
+ ///
+ /// This palette is based on the one outlined here:
+ ///
+ /// https://material.io/design/color/the-color-system.html#tools-for-picking-colors
+ ///
+ /// In order to make the palette uniform and rectangular the following
+ /// alterations were made:
+ ///
+ /// 1. The A100-A700 shades of each color are excluded.
+ /// These shades do not exist for all colors (brown/gray).
+ /// 2. Black/White are stand-alone and are also excluded.
+ ///
+ ///
+ public class MaterialColorPalette : IColorPalette
+ {
+ ///
+ /// Defines all colors in the .
+ ///
+ ///
+ /// This is done in an enum to ensure it is compiled into the assembly improving
+ /// startup performance.
+ ///
+ public enum MaterialColor : uint
+ {
+ // Red
+ Red50 = 0xFFFFEBEE,
+ Red100 = 0xFFFFCDD2,
+ Red200 = 0xFFEF9A9A,
+ Red300 = 0xFFE57373,
+ Red400 = 0xFFEF5350,
+ Red500 = 0xFFF44336,
+ Red600 = 0xFFE53935,
+ Red700 = 0xFFD32F2F,
+ Red800 = 0xFFC62828,
+ Red900 = 0xFFB71C1C,
+
+ // Pink
+ Pink50 = 0xFFFCE4EC,
+ Pink100 = 0xFFF8BBD0,
+ Pink200 = 0xFFF48FB1,
+ Pink300 = 0xFFF06292,
+ Pink400 = 0xFFEC407A,
+ Pink500 = 0xFFE91E63,
+ Pink600 = 0xFFD81B60,
+ Pink700 = 0xFFC2185B,
+ Pink800 = 0xFFAD1457,
+ Pink900 = 0xFF880E4F,
+
+ // Purple
+ Purple50 = 0xFFF3E5F5,
+ Purple100 = 0xFFE1BEE7,
+ Purple200 = 0xFFCE93D8,
+ Purple300 = 0xFFBA68C8,
+ Purple400 = 0xFFAB47BC,
+ Purple500 = 0xFF9C27B0,
+ Purple600 = 0xFF8E24AA,
+ Purple700 = 0xFF7B1FA2,
+ Purple800 = 0xFF6A1B9A,
+ Purple900 = 0xFF4A148C,
+
+ // Deep Purple
+ DeepPurple50 = 0xFFEDE7F6,
+ DeepPurple100 = 0xFFD1C4E9,
+ DeepPurple200 = 0xFFB39DDB,
+ DeepPurple300 = 0xFF9575CD,
+ DeepPurple400 = 0xFF7E57C2,
+ DeepPurple500 = 0xFF673AB7,
+ DeepPurple600 = 0xFF5E35B1,
+ DeepPurple700 = 0xFF512DA8,
+ DeepPurple800 = 0xFF4527A0,
+ DeepPurple900 = 0xFF311B92,
+
+ // Indigo
+ Indigo50 = 0xFFE8EAF6,
+ Indigo100 = 0xFFC5CAE9,
+ Indigo200 = 0xFF9FA8DA,
+ Indigo300 = 0xFF7986CB,
+ Indigo400 = 0xFF5C6BC0,
+ Indigo500 = 0xFF3F51B5,
+ Indigo600 = 0xFF3949AB,
+ Indigo700 = 0xFF303F9F,
+ Indigo800 = 0xFF283593,
+ Indigo900 = 0xFF1A237E,
+
+ // Blue
+ Blue50 = 0xFFE3F2FD,
+ Blue100 = 0xFFBBDEFB,
+ Blue200 = 0xFF90CAF9,
+ Blue300 = 0xFF64B5F6,
+ Blue400 = 0xFF42A5F5,
+ Blue500 = 0xFF2196F3,
+ Blue600 = 0xFF1E88E5,
+ Blue700 = 0xFF1976D2,
+ Blue800 = 0xFF1565C0,
+ Blue900 = 0xFF0D47A1,
+
+ // Light Blue
+ LightBlue50 = 0xFFE1F5FE,
+ LightBlue100 = 0xFFB3E5FC,
+ LightBlue200 = 0xFF81D4FA,
+ LightBlue300 = 0xFF4FC3F7,
+ LightBlue400 = 0xFF29B6F6,
+ LightBlue500 = 0xFF03A9F4,
+ LightBlue600 = 0xFF039BE5,
+ LightBlue700 = 0xFF0288D1,
+ LightBlue800 = 0xFF0277BD,
+ LightBlue900 = 0xFF01579B,
+
+ // Cyan
+ Cyan50 = 0xFFE0F7FA,
+ Cyan100 = 0xFFB2EBF2,
+ Cyan200 = 0xFF80DEEA,
+ Cyan300 = 0xFF4DD0E1,
+ Cyan400 = 0xFF26C6DA,
+ Cyan500 = 0xFF00BCD4,
+ Cyan600 = 0xFF00ACC1,
+ Cyan700 = 0xFF0097A7,
+ Cyan800 = 0xFF00838F,
+ Cyan900 = 0xFF006064,
+
+ // Teal
+ Teal50 = 0xFFE0F2F1,
+ Teal100 = 0xFFB2DFDB,
+ Teal200 = 0xFF80CBC4,
+ Teal300 = 0xFF4DB6AC,
+ Teal400 = 0xFF26A69A,
+ Teal500 = 0xFF009688,
+ Teal600 = 0xFF00897B,
+ Teal700 = 0xFF00796B,
+ Teal800 = 0xFF00695C,
+ Teal900 = 0xFF004D40,
+
+ // Green
+ Green50 = 0xFFE8F5E9,
+ Green100 = 0xFFC8E6C9,
+ Green200 = 0xFFA5D6A7,
+ Green300 = 0xFF81C784,
+ Green400 = 0xFF66BB6A,
+ Green500 = 0xFF4CAF50,
+ Green600 = 0xFF43A047,
+ Green700 = 0xFF388E3C,
+ Green800 = 0xFF2E7D32,
+ Green900 = 0xFF1B5E20,
+
+ // Light Green
+ LightGreen50 = 0xFFF1F8E9,
+ LightGreen100 = 0xFFDCEDC8,
+ LightGreen200 = 0xFFC5E1A5,
+ LightGreen300 = 0xFFAED581,
+ LightGreen400 = 0xFF9CCC65,
+ LightGreen500 = 0xFF8BC34A,
+ LightGreen600 = 0xFF7CB342,
+ LightGreen700 = 0xFF689F38,
+ LightGreen800 = 0xFF558B2F,
+ LightGreen900 = 0xFF33691E,
+
+ // Lime
+ Lime50 = 0xFFF9FBE7,
+ Lime100 = 0xFFF0F4C3,
+ Lime200 = 0xFFE6EE9C,
+ Lime300 = 0xFFDCE775,
+ Lime400 = 0xFFD4E157,
+ Lime500 = 0xFFCDDC39,
+ Lime600 = 0xFFC0CA33,
+ Lime700 = 0xFFAFB42B,
+ Lime800 = 0xFF9E9D24,
+ Lime900 = 0xFF827717,
+
+ // Yellow
+ Yellow50 = 0xFFFFFDE7,
+ Yellow100 = 0xFFFFF9C4,
+ Yellow200 = 0xFFFFF59D,
+ Yellow300 = 0xFFFFF176,
+ Yellow400 = 0xFFFFEE58,
+ Yellow500 = 0xFFFFEB3B,
+ Yellow600 = 0xFFFDD835,
+ Yellow700 = 0xFFFBC02D,
+ Yellow800 = 0xFFF9A825,
+ Yellow900 = 0xFFF57F17,
+
+ // Amber
+ Amber50 = 0xFFFFF8E1,
+ Amber100 = 0xFFFFECB3,
+ Amber200 = 0xFFFFE082,
+ Amber300 = 0xFFFFD54F,
+ Amber400 = 0xFFFFCA28,
+ Amber500 = 0xFFFFC107,
+ Amber600 = 0xFFFFB300,
+ Amber700 = 0xFFFFA000,
+ Amber800 = 0xFFFF8F00,
+ Amber900 = 0xFFFF6F00,
+
+ // Orange
+ Orange50 = 0xFFFFF3E0,
+ Orange100 = 0xFFFFE0B2,
+ Orange200 = 0xFFFFCC80,
+ Orange300 = 0xFFFFB74D,
+ Orange400 = 0xFFFFA726,
+ Orange500 = 0xFFFF9800,
+ Orange600 = 0xFFFB8C00,
+ Orange700 = 0xFFF57C00,
+ Orange800 = 0xFFEF6C00,
+ Orange900 = 0xFFE65100,
+
+ // Deep Orange
+ DeepOrange50 = 0xFFFBE9E7,
+ DeepOrange100 = 0xFFFFCCBC,
+ DeepOrange200 = 0xFFFFAB91,
+ DeepOrange300 = 0xFFFF8A65,
+ DeepOrange400 = 0xFFFF7043,
+ DeepOrange500 = 0xFFFF5722,
+ DeepOrange600 = 0xFFF4511E,
+ DeepOrange700 = 0xFFE64A19,
+ DeepOrange800 = 0xFFD84315,
+ DeepOrange900 = 0xFFBF360C,
+
+ // Brown
+ Brown50 = 0xFFEFEBE9,
+ Brown100 = 0xFFD7CCC8,
+ Brown200 = 0xFFBCAAA4,
+ Brown300 = 0xFFA1887F,
+ Brown400 = 0xFF8D6E63,
+ Brown500 = 0xFF795548,
+ Brown600 = 0xFF6D4C41,
+ Brown700 = 0xFF5D4037,
+ Brown800 = 0xFF4E342E,
+ Brown900 = 0xFF3E2723,
+
+ // Gray
+ Gray50 = 0xFFFAFAFA,
+ Gray100 = 0xFFF5F5F5,
+ Gray200 = 0xFFEEEEEE,
+ Gray300 = 0xFFE0E0E0,
+ Gray400 = 0xFFBDBDBD,
+ Gray500 = 0xFF9E9E9E,
+ Gray600 = 0xFF757575,
+ Gray700 = 0xFF616161,
+ Gray800 = 0xFF424242,
+ Gray900 = 0xFF212121,
+
+ // Blue Gray
+ BlueGray50 = 0xFFECEFF1,
+ BlueGray100 = 0xFFCFD8DC,
+ BlueGray200 = 0xFFB0BEC5,
+ BlueGray300 = 0xFF90A4AE,
+ BlueGray400 = 0xFF78909C,
+ BlueGray500 = 0xFF607D8B,
+ BlueGray600 = 0xFF546E7A,
+ BlueGray700 = 0xFF455A64,
+ BlueGray800 = 0xFF37474F,
+ BlueGray900 = 0xFF263238,
+ }
+
+ // See: https://material.io/design/color/the-color-system.html#tools-for-picking-colors
+ // This is a reduced palette for uniformity
+ protected static Color[,]? _colorChart = null;
+ protected static object _colorChartMutex = new object();
+
+ ///
+ /// Initializes all color chart colors.
+ ///
+ protected void InitColorChart()
+ {
+ lock (_colorChartMutex)
+ {
+ if (_colorChart != null)
+ {
+ return;
+ }
+
+ _colorChart = new Color[,]
+ {
+ // Red
+ {
+ Color.FromUInt32((uint)MaterialColor.Red50),
+ Color.FromUInt32((uint)MaterialColor.Red100),
+ Color.FromUInt32((uint)MaterialColor.Red200),
+ Color.FromUInt32((uint)MaterialColor.Red300),
+ Color.FromUInt32((uint)MaterialColor.Red400),
+ Color.FromUInt32((uint)MaterialColor.Red500),
+ Color.FromUInt32((uint)MaterialColor.Red600),
+ Color.FromUInt32((uint)MaterialColor.Red700),
+ Color.FromUInt32((uint)MaterialColor.Red800),
+ Color.FromUInt32((uint)MaterialColor.Red900),
+ },
+
+ // Pink
+ {
+ Color.FromUInt32((uint)MaterialColor.Pink50),
+ Color.FromUInt32((uint)MaterialColor.Pink100),
+ Color.FromUInt32((uint)MaterialColor.Pink200),
+ Color.FromUInt32((uint)MaterialColor.Pink300),
+ Color.FromUInt32((uint)MaterialColor.Pink400),
+ Color.FromUInt32((uint)MaterialColor.Pink500),
+ Color.FromUInt32((uint)MaterialColor.Pink600),
+ Color.FromUInt32((uint)MaterialColor.Pink700),
+ Color.FromUInt32((uint)MaterialColor.Pink800),
+ Color.FromUInt32((uint)MaterialColor.Pink900),
+ },
+
+ // Purple
+ {
+ Color.FromUInt32((uint)MaterialColor.Purple50),
+ Color.FromUInt32((uint)MaterialColor.Purple100),
+ Color.FromUInt32((uint)MaterialColor.Purple200),
+ Color.FromUInt32((uint)MaterialColor.Purple300),
+ Color.FromUInt32((uint)MaterialColor.Purple400),
+ Color.FromUInt32((uint)MaterialColor.Purple500),
+ Color.FromUInt32((uint)MaterialColor.Purple600),
+ Color.FromUInt32((uint)MaterialColor.Purple700),
+ Color.FromUInt32((uint)MaterialColor.Purple800),
+ Color.FromUInt32((uint)MaterialColor.Purple900),
+ },
+
+ // Deep Purple
+ {
+ Color.FromUInt32((uint)MaterialColor.DeepPurple50),
+ Color.FromUInt32((uint)MaterialColor.DeepPurple100),
+ Color.FromUInt32((uint)MaterialColor.DeepPurple200),
+ Color.FromUInt32((uint)MaterialColor.DeepPurple300),
+ Color.FromUInt32((uint)MaterialColor.DeepPurple400),
+ Color.FromUInt32((uint)MaterialColor.DeepPurple500),
+ Color.FromUInt32((uint)MaterialColor.DeepPurple600),
+ Color.FromUInt32((uint)MaterialColor.DeepPurple700),
+ Color.FromUInt32((uint)MaterialColor.DeepPurple800),
+ Color.FromUInt32((uint)MaterialColor.DeepPurple900),
+ },
+
+ // Indigo
+ {
+ Color.FromUInt32((uint)MaterialColor.Indigo50),
+ Color.FromUInt32((uint)MaterialColor.Indigo100),
+ Color.FromUInt32((uint)MaterialColor.Indigo200),
+ Color.FromUInt32((uint)MaterialColor.Indigo300),
+ Color.FromUInt32((uint)MaterialColor.Indigo400),
+ Color.FromUInt32((uint)MaterialColor.Indigo500),
+ Color.FromUInt32((uint)MaterialColor.Indigo600),
+ Color.FromUInt32((uint)MaterialColor.Indigo700),
+ Color.FromUInt32((uint)MaterialColor.Indigo800),
+ Color.FromUInt32((uint)MaterialColor.Indigo900),
+ },
+
+ // Blue
+ {
+ Color.FromUInt32((uint)MaterialColor.Blue50),
+ Color.FromUInt32((uint)MaterialColor.Blue100),
+ Color.FromUInt32((uint)MaterialColor.Blue200),
+ Color.FromUInt32((uint)MaterialColor.Blue300),
+ Color.FromUInt32((uint)MaterialColor.Blue400),
+ Color.FromUInt32((uint)MaterialColor.Blue500),
+ Color.FromUInt32((uint)MaterialColor.Blue600),
+ Color.FromUInt32((uint)MaterialColor.Blue700),
+ Color.FromUInt32((uint)MaterialColor.Blue800),
+ Color.FromUInt32((uint)MaterialColor.Blue900),
+ },
+
+ // Light Blue
+ {
+ Color.FromUInt32((uint)MaterialColor.LightBlue50),
+ Color.FromUInt32((uint)MaterialColor.LightBlue100),
+ Color.FromUInt32((uint)MaterialColor.LightBlue200),
+ Color.FromUInt32((uint)MaterialColor.LightBlue300),
+ Color.FromUInt32((uint)MaterialColor.LightBlue400),
+ Color.FromUInt32((uint)MaterialColor.LightBlue500),
+ Color.FromUInt32((uint)MaterialColor.LightBlue600),
+ Color.FromUInt32((uint)MaterialColor.LightBlue700),
+ Color.FromUInt32((uint)MaterialColor.LightBlue800),
+ Color.FromUInt32((uint)MaterialColor.LightBlue900),
+ },
+
+ // Cyan
+ {
+ Color.FromUInt32((uint)MaterialColor.Cyan50),
+ Color.FromUInt32((uint)MaterialColor.Cyan100),
+ Color.FromUInt32((uint)MaterialColor.Cyan200),
+ Color.FromUInt32((uint)MaterialColor.Cyan300),
+ Color.FromUInt32((uint)MaterialColor.Cyan400),
+ Color.FromUInt32((uint)MaterialColor.Cyan500),
+ Color.FromUInt32((uint)MaterialColor.Cyan600),
+ Color.FromUInt32((uint)MaterialColor.Cyan700),
+ Color.FromUInt32((uint)MaterialColor.Cyan800),
+ Color.FromUInt32((uint)MaterialColor.Cyan900),
+ },
+
+ // Teal
+ {
+ Color.FromUInt32((uint)MaterialColor.Teal50),
+ Color.FromUInt32((uint)MaterialColor.Teal100),
+ Color.FromUInt32((uint)MaterialColor.Teal200),
+ Color.FromUInt32((uint)MaterialColor.Teal300),
+ Color.FromUInt32((uint)MaterialColor.Teal400),
+ Color.FromUInt32((uint)MaterialColor.Teal500),
+ Color.FromUInt32((uint)MaterialColor.Teal600),
+ Color.FromUInt32((uint)MaterialColor.Teal700),
+ Color.FromUInt32((uint)MaterialColor.Teal800),
+ Color.FromUInt32((uint)MaterialColor.Teal900),
+ },
+
+ // Green
+ {
+ Color.FromUInt32((uint)MaterialColor.Green50),
+ Color.FromUInt32((uint)MaterialColor.Green100),
+ Color.FromUInt32((uint)MaterialColor.Green200),
+ Color.FromUInt32((uint)MaterialColor.Green300),
+ Color.FromUInt32((uint)MaterialColor.Green400),
+ Color.FromUInt32((uint)MaterialColor.Green500),
+ Color.FromUInt32((uint)MaterialColor.Green600),
+ Color.FromUInt32((uint)MaterialColor.Green700),
+ Color.FromUInt32((uint)MaterialColor.Green800),
+ Color.FromUInt32((uint)MaterialColor.Green900),
+ },
+
+ // Light Green
+ {
+ Color.FromUInt32((uint)MaterialColor.LightGreen50),
+ Color.FromUInt32((uint)MaterialColor.LightGreen100),
+ Color.FromUInt32((uint)MaterialColor.LightGreen200),
+ Color.FromUInt32((uint)MaterialColor.LightGreen300),
+ Color.FromUInt32((uint)MaterialColor.LightGreen400),
+ Color.FromUInt32((uint)MaterialColor.LightGreen500),
+ Color.FromUInt32((uint)MaterialColor.LightGreen600),
+ Color.FromUInt32((uint)MaterialColor.LightGreen700),
+ Color.FromUInt32((uint)MaterialColor.LightGreen800),
+ Color.FromUInt32((uint)MaterialColor.LightGreen900),
+ },
+
+ // Lime
+ {
+ Color.FromUInt32((uint)MaterialColor.Lime50),
+ Color.FromUInt32((uint)MaterialColor.Lime100),
+ Color.FromUInt32((uint)MaterialColor.Lime200),
+ Color.FromUInt32((uint)MaterialColor.Lime300),
+ Color.FromUInt32((uint)MaterialColor.Lime400),
+ Color.FromUInt32((uint)MaterialColor.Lime500),
+ Color.FromUInt32((uint)MaterialColor.Lime600),
+ Color.FromUInt32((uint)MaterialColor.Lime700),
+ Color.FromUInt32((uint)MaterialColor.Lime800),
+ Color.FromUInt32((uint)MaterialColor.Lime900),
+ },
+
+ // Yellow
+ {
+ Color.FromUInt32((uint)MaterialColor.Yellow50),
+ Color.FromUInt32((uint)MaterialColor.Yellow100),
+ Color.FromUInt32((uint)MaterialColor.Yellow200),
+ Color.FromUInt32((uint)MaterialColor.Yellow300),
+ Color.FromUInt32((uint)MaterialColor.Yellow400),
+ Color.FromUInt32((uint)MaterialColor.Yellow500),
+ Color.FromUInt32((uint)MaterialColor.Yellow600),
+ Color.FromUInt32((uint)MaterialColor.Yellow700),
+ Color.FromUInt32((uint)MaterialColor.Yellow800),
+ Color.FromUInt32((uint)MaterialColor.Yellow900),
+ },
+
+ // Amber
+ {
+ Color.FromUInt32((uint)MaterialColor.Amber50),
+ Color.FromUInt32((uint)MaterialColor.Amber100),
+ Color.FromUInt32((uint)MaterialColor.Amber200),
+ Color.FromUInt32((uint)MaterialColor.Amber300),
+ Color.FromUInt32((uint)MaterialColor.Amber400),
+ Color.FromUInt32((uint)MaterialColor.Amber500),
+ Color.FromUInt32((uint)MaterialColor.Amber600),
+ Color.FromUInt32((uint)MaterialColor.Amber700),
+ Color.FromUInt32((uint)MaterialColor.Amber800),
+ Color.FromUInt32((uint)MaterialColor.Amber900),
+ },
+
+ // Orange
+ {
+ Color.FromUInt32((uint)MaterialColor.Orange50),
+ Color.FromUInt32((uint)MaterialColor.Orange100),
+ Color.FromUInt32((uint)MaterialColor.Orange200),
+ Color.FromUInt32((uint)MaterialColor.Orange300),
+ Color.FromUInt32((uint)MaterialColor.Orange400),
+ Color.FromUInt32((uint)MaterialColor.Orange500),
+ Color.FromUInt32((uint)MaterialColor.Orange600),
+ Color.FromUInt32((uint)MaterialColor.Orange700),
+ Color.FromUInt32((uint)MaterialColor.Orange800),
+ Color.FromUInt32((uint)MaterialColor.Orange900),
+ },
+
+ // Deep Orange
+ {
+ Color.FromUInt32((uint)MaterialColor.DeepOrange50),
+ Color.FromUInt32((uint)MaterialColor.DeepOrange100),
+ Color.FromUInt32((uint)MaterialColor.DeepOrange200),
+ Color.FromUInt32((uint)MaterialColor.DeepOrange300),
+ Color.FromUInt32((uint)MaterialColor.DeepOrange400),
+ Color.FromUInt32((uint)MaterialColor.DeepOrange500),
+ Color.FromUInt32((uint)MaterialColor.DeepOrange600),
+ Color.FromUInt32((uint)MaterialColor.DeepOrange700),
+ Color.FromUInt32((uint)MaterialColor.DeepOrange800),
+ Color.FromUInt32((uint)MaterialColor.DeepOrange900),
+ },
+
+ // Brown
+ {
+ Color.FromUInt32((uint)MaterialColor.Brown50),
+ Color.FromUInt32((uint)MaterialColor.Brown100),
+ Color.FromUInt32((uint)MaterialColor.Brown200),
+ Color.FromUInt32((uint)MaterialColor.Brown300),
+ Color.FromUInt32((uint)MaterialColor.Brown400),
+ Color.FromUInt32((uint)MaterialColor.Brown500),
+ Color.FromUInt32((uint)MaterialColor.Brown600),
+ Color.FromUInt32((uint)MaterialColor.Brown700),
+ Color.FromUInt32((uint)MaterialColor.Brown800),
+ Color.FromUInt32((uint)MaterialColor.Brown900),
+ },
+
+ // Gray
+ {
+ Color.FromUInt32((uint)MaterialColor.Gray50),
+ Color.FromUInt32((uint)MaterialColor.Gray100),
+ Color.FromUInt32((uint)MaterialColor.Gray200),
+ Color.FromUInt32((uint)MaterialColor.Gray300),
+ Color.FromUInt32((uint)MaterialColor.Gray400),
+ Color.FromUInt32((uint)MaterialColor.Gray500),
+ Color.FromUInt32((uint)MaterialColor.Gray600),
+ Color.FromUInt32((uint)MaterialColor.Gray700),
+ Color.FromUInt32((uint)MaterialColor.Gray800),
+ Color.FromUInt32((uint)MaterialColor.Gray900),
+ },
+
+ // Blue Gray
+ {
+ Color.FromUInt32((uint)MaterialColor.BlueGray50),
+ Color.FromUInt32((uint)MaterialColor.BlueGray100),
+ Color.FromUInt32((uint)MaterialColor.BlueGray200),
+ Color.FromUInt32((uint)MaterialColor.BlueGray300),
+ Color.FromUInt32((uint)MaterialColor.BlueGray400),
+ Color.FromUInt32((uint)MaterialColor.BlueGray500),
+ Color.FromUInt32((uint)MaterialColor.BlueGray600),
+ Color.FromUInt32((uint)MaterialColor.BlueGray700),
+ Color.FromUInt32((uint)MaterialColor.BlueGray800),
+ Color.FromUInt32((uint)MaterialColor.BlueGray900),
+ },
+ };
+ }
+
+ return;
+ }
+
+ ///
+ public int ColorCount
+ {
+ get => 19;
+ }
+
+ ///
+ public int ShadeCount
+ {
+ get => 10;
+ }
+
+ ///
+ public Color GetColor(int colorIndex, int shadeIndex)
+ {
+ if (_colorChart == null)
+ {
+ InitColorChart();
+ }
+
+ return _colorChart![
+ MathUtilities.Clamp(colorIndex, 0, ColorCount - 1),
+ MathUtilities.Clamp(shadeIndex, 0, ShadeCount - 1)];
+ }
+ }
+}
diff --git a/src/Avalonia.Controls.ColorPicker/ColorPalettes/MaterialHalfColorPalette.cs b/src/Avalonia.Controls.ColorPicker/ColorPalettes/MaterialHalfColorPalette.cs
new file mode 100644
index 0000000000..01d44aa65d
--- /dev/null
+++ b/src/Avalonia.Controls.ColorPicker/ColorPalettes/MaterialHalfColorPalette.cs
@@ -0,0 +1,150 @@
+using Avalonia.Media;
+using Avalonia.Utilities;
+using MaterialColor = Avalonia.Controls.MaterialColorPalette.MaterialColor;
+
+namespace Avalonia.Controls
+{
+ ///
+ /// Implements half of the for improved usability.
+ ///
+ ///
+ public class MaterialHalfColorPalette : IColorPalette
+ {
+ protected static Color[,]? _colorChart = null;
+ protected static object _colorChartMutex = new object();
+
+ ///
+ /// Initializes all color chart colors.
+ ///
+ protected void InitColorChart()
+ {
+ lock (_colorChartMutex)
+ {
+ if (_colorChart != null)
+ {
+ return;
+ }
+
+ _colorChart = new Color[,]
+ {
+ // Red
+ {
+ Color.FromUInt32((uint)MaterialColor.Red50),
+ Color.FromUInt32((uint)MaterialColor.Red200),
+ Color.FromUInt32((uint)MaterialColor.Red400),
+ Color.FromUInt32((uint)MaterialColor.Red600),
+ Color.FromUInt32((uint)MaterialColor.Red800),
+ },
+
+ // Purple
+ {
+ Color.FromUInt32((uint)MaterialColor.Purple50),
+ Color.FromUInt32((uint)MaterialColor.Purple200),
+ Color.FromUInt32((uint)MaterialColor.Purple400),
+ Color.FromUInt32((uint)MaterialColor.Purple600),
+ Color.FromUInt32((uint)MaterialColor.Purple800),
+ },
+
+ // Indigo
+ {
+ Color.FromUInt32((uint)MaterialColor.Indigo50),
+ Color.FromUInt32((uint)MaterialColor.Indigo200),
+ Color.FromUInt32((uint)MaterialColor.Indigo400),
+ Color.FromUInt32((uint)MaterialColor.Indigo600),
+ Color.FromUInt32((uint)MaterialColor.Indigo800),
+ },
+
+ // Light Blue
+ {
+ Color.FromUInt32((uint)MaterialColor.LightBlue50),
+ Color.FromUInt32((uint)MaterialColor.LightBlue200),
+ Color.FromUInt32((uint)MaterialColor.LightBlue400),
+ Color.FromUInt32((uint)MaterialColor.LightBlue600),
+ Color.FromUInt32((uint)MaterialColor.LightBlue800),
+ },
+
+ // Teal
+ {
+ Color.FromUInt32((uint)MaterialColor.Teal50),
+ Color.FromUInt32((uint)MaterialColor.Teal200),
+ Color.FromUInt32((uint)MaterialColor.Teal400),
+ Color.FromUInt32((uint)MaterialColor.Teal600),
+ Color.FromUInt32((uint)MaterialColor.Teal800),
+ },
+
+ // Light Green
+ {
+ Color.FromUInt32((uint)MaterialColor.LightGreen50),
+ Color.FromUInt32((uint)MaterialColor.LightGreen200),
+ Color.FromUInt32((uint)MaterialColor.LightGreen400),
+ Color.FromUInt32((uint)MaterialColor.LightGreen600),
+ Color.FromUInt32((uint)MaterialColor.LightGreen800),
+ },
+
+ // Yellow
+ {
+ Color.FromUInt32((uint)MaterialColor.Yellow50),
+ Color.FromUInt32((uint)MaterialColor.Yellow200),
+ Color.FromUInt32((uint)MaterialColor.Yellow400),
+ Color.FromUInt32((uint)MaterialColor.Yellow600),
+ Color.FromUInt32((uint)MaterialColor.Yellow800),
+ },
+
+ // Orange
+ {
+ Color.FromUInt32((uint)MaterialColor.Orange50),
+ Color.FromUInt32((uint)MaterialColor.Orange200),
+ Color.FromUInt32((uint)MaterialColor.Orange400),
+ Color.FromUInt32((uint)MaterialColor.Orange600),
+ Color.FromUInt32((uint)MaterialColor.Orange800),
+ },
+
+ // Brown
+ {
+ Color.FromUInt32((uint)MaterialColor.Brown50),
+ Color.FromUInt32((uint)MaterialColor.Brown200),
+ Color.FromUInt32((uint)MaterialColor.Brown400),
+ Color.FromUInt32((uint)MaterialColor.Brown600),
+ Color.FromUInt32((uint)MaterialColor.Brown800),
+ },
+
+ // Blue Gray
+ {
+ Color.FromUInt32((uint)MaterialColor.BlueGray50),
+ Color.FromUInt32((uint)MaterialColor.BlueGray200),
+ Color.FromUInt32((uint)MaterialColor.BlueGray400),
+ Color.FromUInt32((uint)MaterialColor.BlueGray600),
+ Color.FromUInt32((uint)MaterialColor.BlueGray800),
+ },
+ };
+ }
+
+ return;
+ }
+
+ ///
+ public int ColorCount
+ {
+ get => 10;
+ }
+
+ ///
+ public int ShadeCount
+ {
+ get => 5;
+ }
+
+ ///
+ public Color GetColor(int colorIndex, int shadeIndex)
+ {
+ if (_colorChart == null)
+ {
+ InitColorChart();
+ }
+
+ return _colorChart![
+ MathUtilities.Clamp(colorIndex, 0, ColorCount - 1),
+ MathUtilities.Clamp(shadeIndex, 0, ShadeCount - 1)];
+ }
+ }
+}
diff --git a/src/Avalonia.Controls.ColorPicker/ColorPalettes/SixteenColorPalette.cs b/src/Avalonia.Controls.ColorPicker/ColorPalettes/SixteenColorPalette.cs
index f3abfdfd7f..e8e82eac91 100644
--- a/src/Avalonia.Controls.ColorPicker/ColorPalettes/SixteenColorPalette.cs
+++ b/src/Avalonia.Controls.ColorPicker/ColorPalettes/SixteenColorPalette.cs
@@ -50,107 +50,6 @@ namespace Avalonia.Controls
}
};
- ///
- /// Gets the index of the default shade of colors in this palette.
- ///
- public const int DefaultShadeIndex = 0;
-
- ///
- /// The index in the color palette of the 'White' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int WhiteIndex = 0;
-
- ///
- /// The index in the color palette of the 'Silver' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int SilverIndex = 1;
-
- ///
- /// The index in the color palette of the 'Gray' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int GrayIndex = 2;
-
- ///
- /// The index in the color palette of the 'Black' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int BlackIndex = 3;
-
- ///
- /// The index in the color palette of the 'Red' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int RedIndex = 4;
-
- ///
- /// The index in the color palette of the 'Maroon' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int MaroonIndex = 5;
-
- ///
- /// The index in the color palette of the 'Yellow' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int YellowIndex = 6;
-
- ///
- /// The index in the color palette of the 'Olive' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int OliveIndex = 7;
-
- ///
- /// The index in the color palette of the 'Lime' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int LimeIndex = 8;
-
- ///
- /// The index in the color palette of the 'Green' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int GreenIndex = 9;
-
- ///
- /// The index in the color palette of the 'Aqua' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int AquaIndex = 10;
-
- ///
- /// The index in the color palette of the 'Teal' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int TealIndex = 11;
-
- ///
- /// The index in the color palette of the 'Blue' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int BlueIndex = 12;
-
- ///
- /// The index in the color palette of the 'Navy' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int NavyIndex = 13;
-
- ///
- /// The index in the color palette of the 'Fuchsia' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int FuchsiaIndex = 14;
-
- ///
- /// The index in the color palette of the 'Purple' color.
- /// This index can correspond to multiple color shades.
- ///
- public const int PurpleIndex = 15;
-
///
public int ColorCount
{
@@ -163,134 +62,6 @@ namespace Avalonia.Controls
get => colorChart.GetLength(1);
}
- ///
- /// Gets the palette defined color that has an ARGB value of #FFFFFFFF.
- ///
- public static Color White
- {
- get => colorChart[WhiteIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FFC0C0C0.
- ///
- public static Color Silver
- {
- get => colorChart[SilverIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FF808080.
- ///
- public static Color Gray
- {
- get => colorChart[GrayIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FF000000.
- ///
- public static Color Black
- {
- get => colorChart[BlackIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FFFF0000.
- ///
- public static Color Red
- {
- get => colorChart[RedIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FF800000.
- ///
- public static Color Maroon
- {
- get => colorChart[MaroonIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FFFFFF00.
- ///
- public static Color Yellow
- {
- get => colorChart[YellowIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FF808000.
- ///
- public static Color Olive
- {
- get => colorChart[OliveIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FF00FF00.
- ///
- public static Color Lime
- {
- get => colorChart[LimeIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FF008000.
- ///
- public static Color Green
- {
- get => colorChart[GreenIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FF00FFFF.
- ///
- public static Color Aqua
- {
- get => colorChart[AquaIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FF008080.
- ///
- public static Color Teal
- {
- get => colorChart[TealIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FF0000FF.
- ///
- public static Color Blue
- {
- get => colorChart[BlueIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FF000080.
- ///
- public static Color Navy
- {
- get => colorChart[NavyIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FFFF00FF.
- ///
- public static Color Fuchsia
- {
- get => colorChart[FuchsiaIndex, DefaultShadeIndex];
- }
-
- ///
- /// Gets the palette defined color that has an ARGB value of #FF800080.
- ///
- public static Color Purple
- {
- get => colorChart[PurpleIndex, DefaultShadeIndex];
- }
-
///
public Color GetColor(int colorIndex, int shadeIndex)
{
diff --git a/src/Avalonia.Controls.ColorPicker/ColorPicker/ColorPicker.cs b/src/Avalonia.Controls.ColorPicker/ColorPicker/ColorPicker.cs
index 39369bcbdb..29f9f3c571 100644
--- a/src/Avalonia.Controls.ColorPicker/ColorPicker/ColorPicker.cs
+++ b/src/Avalonia.Controls.ColorPicker/ColorPicker/ColorPicker.cs
@@ -1,4 +1,6 @@
-namespace Avalonia.Controls
+using Avalonia.Controls.Primitives;
+
+namespace Avalonia.Controls
{
///
/// Presents a color for user editing using a spectrum, palette and component sliders within a drop down.
@@ -11,8 +13,33 @@
///
public ColorPicker() : base()
{
- // Completely ignore property changes here
- // The ColorView in the control template is responsible to manage this
+ }
+
+ ///
+ protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
+ {
+ base.OnApplyTemplate(e);
+
+ // Until this point the ColorPicker itself is responsible to process property updates.
+ // This, for example, syncs Color with HsvColor and updates primitive controls.
+ //
+ // However, when the template is created, hand-off this change processing to the
+ // ColorView within the control template itself. Remember ColorPicker derives from
+ // ColorView so we don't want two instances of the same logic fighting each other.
+ // It is best to hand-off to the ColorView in the control template because that is the
+ // primary point of user-interaction for the overall control. It also simplifies binding.
+ //
+ // Keep in mind this hand-off is not possible until the template controls are created
+ // which is done after the ColorPicker is instantiated. The ColorPicker must still
+ // process updates before the template is applied to ensure all property changes in
+ // XAML or object initializers are handled correctly. Otherwise, there can be bugs
+ // such as setting the Color property doesn't work because the HsvColor is never updated
+ // and then the Color value is lost once the template loads (and the template ColorView
+ // takes over).
+ //
+ // In order to complete this hand-off, completely ignore property changes here in the
+ // ColorPicker. This means the ColorView in the control template is now responsible to
+ // process property changes and handle primary calculations.
base.ignorePropertyChanged = true;
}
}
diff --git a/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs b/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs
index b662d20223..ec08e96d87 100644
--- a/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs
+++ b/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs
@@ -2,6 +2,7 @@
using Avalonia.Controls.Metadata;
using Avalonia.Layout;
using Avalonia.Media;
+using Avalonia.Media.Imaging;
using Avalonia.Utilities;
namespace Avalonia.Controls.Primitives
@@ -31,6 +32,8 @@ namespace Avalonia.Controls.Primitives
protected bool ignorePropertyChanged = false;
+ private WriteableBitmap? _backgroundBitmap;
+
///
/// Initializes a new instance of the class.
///
@@ -38,6 +41,18 @@ namespace Avalonia.Controls.Primitives
{
}
+ ///
+ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
+ {
+ base.OnAttachedToVisualTree(e);
+ }
+
+ ///
+ protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
+ {
+ base.OnDetachedFromVisualTree(e);
+ }
+
///
/// Updates the visual state of the control by applying latest PseudoClasses.
///
@@ -98,7 +113,7 @@ namespace Avalonia.Controls.Primitives
if (pixelWidth != 0 && pixelHeight != 0)
{
- var bitmap = await ColorPickerHelpers.CreateComponentBitmapAsync(
+ ArrayList bgraPixelData = await ColorPickerHelpers.CreateComponentBitmapAsync(
pixelWidth,
pixelHeight,
Orientation,
@@ -108,9 +123,27 @@ namespace Avalonia.Controls.Primitives
IsAlphaMaxForced,
IsSaturationValueMaxForced);
- if (bitmap != null)
+ if (bgraPixelData != null)
{
- Background = new ImageBrush(ColorPickerHelpers.CreateBitmapFromPixelData(bitmap, pixelWidth, pixelHeight));
+ if (_backgroundBitmap != null)
+ {
+ // TODO: CURRENTLY DISABLED DUE TO INTERMITTENT CRASHES IN SKIA/RENDERER
+ //
+ // Re-use the existing WriteableBitmap
+ // This assumes the height, width and byte counts are the same and must be set to null
+ // elsewhere if that assumption is ever not true.
+ // ColorPickerHelpers.UpdateBitmapFromPixelData(_backgroundBitmap, bgraPixelData);
+
+ // TODO: ALSO DISABLED DISPOSE DUE TO INTERMITTENT CRASHES
+ //_backgroundBitmap?.Dispose();
+ _backgroundBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraPixelData, pixelWidth, pixelHeight);
+ }
+ else
+ {
+ _backgroundBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraPixelData, pixelWidth, pixelHeight);
+ }
+
+ Background = new ImageBrush(_backgroundBitmap);
}
}
}
@@ -350,11 +383,11 @@ namespace Avalonia.Controls.Primitives
return;
}
- // Always keep the two color properties in sync
if (change.Property == ColorProperty)
{
ignorePropertyChanged = true;
+ // Always keep the two color properties in sync
HsvColor = Color.ToHsv();
SetColorToSliderValues();
@@ -367,7 +400,10 @@ namespace Avalonia.Controls.Primitives
ignorePropertyChanged = false;
}
- else if (change.Property == ColorModelProperty)
+ else if (change.Property == ColorComponentProperty ||
+ change.Property == ColorModelProperty ||
+ change.Property == IsAlphaMaxForcedProperty ||
+ change.Property == IsSaturationValueMaxForcedProperty)
{
ignorePropertyChanged = true;
@@ -381,6 +417,7 @@ namespace Avalonia.Controls.Primitives
{
ignorePropertyChanged = true;
+ // Always keep the two color properties in sync
Color = HsvColor.ToRgb();
SetColorToSliderValues();
@@ -399,7 +436,13 @@ namespace Avalonia.Controls.Primitives
}
else if (change.Property == BoundsProperty)
{
+ // If the control's overall dimensions have changed the background bitmap size also needs to change.
+ // This means the existing bitmap must be released to be recreated correctly in UpdateBackground().
+ _backgroundBitmap?.Dispose();
+ _backgroundBitmap = null;
+
UpdateBackground();
+ UpdatePseudoClasses();
}
else if (change.Property == ValueProperty ||
change.Property == MinimumProperty ||
diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs
index 00d84f5dd3..39b7b7f660 100644
--- a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs
+++ b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs
@@ -93,6 +93,14 @@ namespace Avalonia.Controls.Primitives
nameof(Shape),
ColorSpectrumShape.Box);
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty ThirdComponentProperty =
+ AvaloniaProperty.Register(
+ nameof(ThirdComponent),
+ ColorComponent.Component3); // Value
+
///
/// Gets or sets the currently selected color in the RGB color model.
///
@@ -218,5 +226,21 @@ namespace Avalonia.Controls.Primitives
get => GetValue(ShapeProperty);
set => SetValue(ShapeProperty, value);
}
+
+ ///
+ /// Gets the third HSV color component that is NOT displayed by the spectrum.
+ /// This is automatically calculated from the property.
+ ///
+ ///
+ /// This property should be used for any external color slider that represents the
+ /// third component of the color. Note that this property uses the generic
+ /// type instead of the more accurate
+ /// to allow direct usage by the generalized color sliders.
+ ///
+ public ColorComponent ThirdComponent
+ {
+ get => GetValue(ThirdComponentProperty);
+ private set => SetValue(ThirdComponentProperty, value);
+ }
}
}
diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs
index bd44161a42..f0ed89fb3a 100644
--- a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs
+++ b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs
@@ -73,6 +73,8 @@ namespace Avalonia.Controls.Primitives
private WriteableBitmap? _saturationMaximumBitmap;
private WriteableBitmap? _valueBitmap;
+ private WriteableBitmap? _minBitmap;
+ private WriteableBitmap? _maxBitmap;
// Fields used by UpdateEllipse() to ensure that it's using the data
// associated with the last call to CreateBitmapsAndColorMap(),
@@ -95,7 +97,7 @@ namespace Avalonia.Controls.Primitives
///
/// Initializes a new instance of the class.
///
- public ColorSpectrum()
+ public ColorSpectrum() : base()
{
_shapeFromLastBitmapCreation = Shape;
_componentsFromLastBitmapCreation = Components;
@@ -171,6 +173,18 @@ namespace Avalonia.Controls.Primitives
{
base.OnAttachedToVisualTree(e);
+ // If the color was updated while this ColorSpectrum was not part of the visual tree,
+ // the selection ellipse may be in an incorrect position. This is because the spectrum
+ // renders based on layout scaling to avoid color banding; however, layout scale is only
+ // available when the control is attached to the visual tree. The ColorSpectrum's color
+ // may be updated from code-behind or from binding with another control when it's not
+ // part of the visual tree.
+ //
+ // See discussion: https://github.com/AvaloniaUI/Avalonia/discussions/9077
+ //
+ // To work-around this issue the selection ellipse is refreshed here.
+ UpdateEllipse();
+
// OnAttachedToVisualTree is called after OnApplyTemplate so events cannot be connected here
}
@@ -489,6 +503,23 @@ namespace Avalonia.Controls.Primitives
}
else if (change.Property == ComponentsProperty)
{
+ // Calculate and update the ThirdComponent value
+ switch (Components)
+ {
+ case ColorSpectrumComponents.HueSaturation:
+ case ColorSpectrumComponents.SaturationHue:
+ ThirdComponent = (ColorComponent)HsvComponent.Value;
+ break;
+ case ColorSpectrumComponents.HueValue:
+ case ColorSpectrumComponents.ValueHue:
+ ThirdComponent = (ColorComponent)HsvComponent.Saturation;
+ break;
+ case ColorSpectrumComponents.SaturationValue:
+ case ColorSpectrumComponents.ValueSaturation:
+ ThirdComponent = (ColorComponent)HsvComponent.Hue;
+ break;
+ }
+
CreateBitmapsAndColorMap();
}
@@ -588,6 +619,10 @@ namespace Avalonia.Controls.Primitives
RaiseColorChanged();
}
+ ///
+ /// Updates the selected and based on a point within the color spectrum.
+ ///
+ /// The point on the spectrum representing the color.
private void UpdateColorFromPoint(PointerPoint point)
{
// If we haven't initialized our HSV value array yet, then we should just ignore any user input -
@@ -664,6 +699,9 @@ namespace Avalonia.Controls.Primitives
UpdateColor(hsvAtPoint);
}
+ ///
+ /// Updates the position of the selection ellipse on the spectrum which indicates the selected color.
+ ///
private void UpdateEllipse()
{
if (_selectionEllipsePanel == null)
@@ -832,6 +870,8 @@ namespace Avalonia.Controls.Primitives
}
// Remember the bitmap size follows physical device pixels
+ // Warning: LayoutHelper.GetLayoutScale() doesn't work unless the control is visible
+ // This will not be true in all cases if the color is updated from another control or code-behind
var scale = LayoutHelper.GetLayoutScale(this);
Canvas.SetLeft(_selectionEllipsePanel, (xPosition / scale) - (_selectionEllipsePanel.Width / 2));
Canvas.SetTop(_selectionEllipsePanel, (yPosition / scale) - (_selectionEllipsePanel.Height / 2));
@@ -973,13 +1013,13 @@ namespace Avalonia.Controls.Primitives
// The middle 4 are only needed and used in the case of hue as the third dimension.
// Saturation and luminosity need only a min and max.
- List bgraMinPixelData = new List();
- List bgraMiddle1PixelData = new List();
- List bgraMiddle2PixelData = new List();
- List bgraMiddle3PixelData = new List();
- List bgraMiddle4PixelData = new List();
- List bgraMaxPixelData = new List();
- List newHsvValues = new List();
+ ArrayList bgraMinPixelData;
+ ArrayList bgraMiddle1PixelData;
+ ArrayList bgraMiddle2PixelData;
+ ArrayList bgraMiddle3PixelData;
+ ArrayList bgraMiddle4PixelData;
+ ArrayList bgraMaxPixelData;
+ List newHsvValues;
// In Avalonia, Bounds returns the actual device-independent pixel size of a control.
// However, this is not necessarily the size of the control rendered on a display.
@@ -990,20 +1030,27 @@ namespace Avalonia.Controls.Primitives
int pixelDimension = (int)Math.Round(minDimension * scale);
var pixelCount = pixelDimension * pixelDimension;
var pixelDataSize = pixelCount * 4;
- bgraMinPixelData.Capacity = pixelDataSize;
+
+ bgraMinPixelData = new ArrayList(pixelDataSize);
+ bgraMaxPixelData = new ArrayList(pixelDataSize);
+ newHsvValues = new List(pixelCount);
// We'll only save pixel data for the middle bitmaps if our third dimension is hue.
if (components == ColorSpectrumComponents.ValueSaturation ||
components == ColorSpectrumComponents.SaturationValue)
{
- bgraMiddle1PixelData.Capacity = pixelDataSize;
- bgraMiddle2PixelData.Capacity = pixelDataSize;
- bgraMiddle3PixelData.Capacity = pixelDataSize;
- bgraMiddle4PixelData.Capacity = pixelDataSize;
+ bgraMiddle1PixelData = new ArrayList(pixelDataSize);
+ bgraMiddle2PixelData = new ArrayList(pixelDataSize);
+ bgraMiddle3PixelData = new ArrayList(pixelDataSize);
+ bgraMiddle4PixelData = new ArrayList(pixelDataSize);
+ }
+ else
+ {
+ bgraMiddle1PixelData = new ArrayList(0);
+ bgraMiddle2PixelData = new ArrayList(0);
+ bgraMiddle3PixelData = new ArrayList(0);
+ bgraMiddle4PixelData = new ArrayList(0);
}
-
- bgraMaxPixelData.Capacity = pixelDataSize;
- newHsvValues.Capacity = pixelCount;
await Task.Run(() =>
{
@@ -1056,28 +1103,28 @@ namespace Avalonia.Controls.Primitives
ColorSpectrumComponents components2 = Components;
- WriteableBitmap minBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraMinPixelData, pixelWidth, pixelHeight);
- WriteableBitmap maxBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraMaxPixelData, pixelWidth, pixelHeight);
+ _minBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraMinPixelData, pixelWidth, pixelHeight);
+ _maxBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraMaxPixelData, pixelWidth, pixelHeight);
switch (components2)
{
case ColorSpectrumComponents.HueValue:
case ColorSpectrumComponents.ValueHue:
- _saturationMinimumBitmap = minBitmap;
- _saturationMaximumBitmap = maxBitmap;
+ _saturationMinimumBitmap = _minBitmap;
+ _saturationMaximumBitmap = _maxBitmap;
break;
case ColorSpectrumComponents.HueSaturation:
case ColorSpectrumComponents.SaturationHue:
- _valueBitmap = maxBitmap;
+ _valueBitmap = _maxBitmap;
break;
case ColorSpectrumComponents.ValueSaturation:
case ColorSpectrumComponents.SaturationValue:
- _hueRedBitmap = minBitmap;
+ _hueRedBitmap = _minBitmap;
_hueYellowBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraMiddle1PixelData, pixelWidth, pixelHeight);
_hueGreenBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraMiddle2PixelData, pixelWidth, pixelHeight);
_hueCyanBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraMiddle3PixelData, pixelWidth, pixelHeight);
_hueBlueBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraMiddle4PixelData, pixelWidth, pixelHeight);
- _huePurpleBitmap = maxBitmap;
+ _huePurpleBitmap = _maxBitmap;
break;
}
@@ -1111,12 +1158,12 @@ namespace Avalonia.Controls.Primitives
double maxSaturation,
double minValue,
double maxValue,
- List bgraMinPixelData,
- List bgraMiddle1PixelData,
- List bgraMiddle2PixelData,
- List bgraMiddle3PixelData,
- List bgraMiddle4PixelData,
- List bgraMaxPixelData,
+ ArrayList bgraMinPixelData,
+ ArrayList bgraMiddle1PixelData,
+ ArrayList bgraMiddle2PixelData,
+ ArrayList bgraMiddle3PixelData,
+ ArrayList bgraMiddle4PixelData,
+ ArrayList bgraMaxPixelData,
List newHsvValues)
{
double hMin = minHue;
@@ -1271,12 +1318,12 @@ namespace Avalonia.Controls.Primitives
double maxSaturation,
double minValue,
double maxValue,
- List bgraMinPixelData,
- List bgraMiddle1PixelData,
- List bgraMiddle2PixelData,
- List bgraMiddle3PixelData,
- List bgraMiddle4PixelData,
- List bgraMaxPixelData,
+ ArrayList bgraMinPixelData,
+ ArrayList bgraMiddle1PixelData,
+ ArrayList bgraMiddle2PixelData,
+ ArrayList bgraMiddle3PixelData,
+ ArrayList bgraMiddle4PixelData,
+ ArrayList bgraMaxPixelData,
List newHsvValues)
{
double hMin = minHue;
diff --git a/src/Avalonia.Controls.ColorPicker/Converters/ThirdComponentConverter.cs b/src/Avalonia.Controls.ColorPicker/Converters/ThirdComponentConverter.cs
deleted file mode 100644
index 11e33c74f0..0000000000
--- a/src/Avalonia.Controls.ColorPicker/Converters/ThirdComponentConverter.cs
+++ /dev/null
@@ -1,53 +0,0 @@
-using System;
-using System.Globalization;
-using Avalonia.Data.Converters;
-
-namespace Avalonia.Controls.Primitives.Converters
-{
- ///
- /// Gets the third corresponding with a given
- /// that represents the other two components.
- ///
- ///
- /// This is a highly-specialized converter for the color picker.
- ///
- public class ThirdComponentConverter : IValueConverter
- {
- ///
- public object? Convert(
- object? value,
- Type targetType,
- object? parameter,
- CultureInfo culture)
- {
- if (value is ColorSpectrumComponents components)
- {
- // Note: Alpha is not relevant here
- switch (components)
- {
- case ColorSpectrumComponents.HueSaturation:
- case ColorSpectrumComponents.SaturationHue:
- return (ColorComponent)HsvComponent.Value;
- case ColorSpectrumComponents.HueValue:
- case ColorSpectrumComponents.ValueHue:
- return (ColorComponent)HsvComponent.Saturation;
- case ColorSpectrumComponents.SaturationValue:
- case ColorSpectrumComponents.ValueSaturation:
- return (ColorComponent)HsvComponent.Hue;
- }
- }
-
- return AvaloniaProperty.UnsetValue;
- }
-
- ///
- public object? ConvertBack(
- object? value,
- Type targetType,
- object? parameter,
- CultureInfo culture)
- {
- return AvaloniaProperty.UnsetValue;
- }
- }
-}
diff --git a/src/Avalonia.Controls.ColorPicker/Helpers/ArrayList.cs b/src/Avalonia.Controls.ColorPicker/Helpers/ArrayList.cs
new file mode 100644
index 0000000000..0b4c2f8579
--- /dev/null
+++ b/src/Avalonia.Controls.ColorPicker/Helpers/ArrayList.cs
@@ -0,0 +1,71 @@
+namespace Avalonia.Controls.Primitives
+{
+ ///
+ /// A thin wrapper over an that allows some additional list-like functionality.
+ ///
+ ///
+ /// This is only for internal ColorPicker-related functionality and should not be used elsewhere.
+ /// It is added for performance to enjoy the simplicity of the IList.Add() method without requiring
+ /// an additional copy to turn a list into an array for bitmaps.
+ ///
+ /// The type of items in the array.
+ internal class ArrayList
+ {
+ private int _nextIndex = 0;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ArrayList(int capacity)
+ {
+ Capacity = capacity;
+ Array = new T[capacity];
+ }
+
+ ///
+ /// Provides access to the underlying array by index.
+ /// This exists for simplification and the property
+ /// may also be used.
+ ///
+ /// The index of the item to get or set.
+ /// The item at the given index.
+ public T this[int i]
+ {
+ get => Array[i];
+ set => Array[i] = value;
+ }
+
+ ///
+ /// Gets the underlying array.
+ ///
+ public T[] Array { get; private set; }
+
+ ///
+ /// Gets the fixed capacity/size of the array.
+ /// This must be set during construction.
+ ///
+ public int Capacity { get; private set; }
+
+ ///
+ /// Adds the given item to the array at the next available index.
+ /// WARNING: This must be used carefully and only once, in sequence.
+ ///
+ /// The item to add.
+ public void Add(T item)
+ {
+ if (_nextIndex >= 0 &&
+ _nextIndex < Capacity)
+ {
+ Array[_nextIndex] = item;
+ _nextIndex++;
+ }
+ else
+ {
+ // If necessary an exception could be thrown here
+ // throw new IndexOutOfRangeException();
+ }
+
+ return;
+ }
+ }
+}
diff --git a/src/Avalonia.Controls.ColorPicker/Helpers/ColorPickerHelpers.cs b/src/Avalonia.Controls.ColorPicker/Helpers/ColorPickerHelpers.cs
index c1904a3c30..819d745772 100644
--- a/src/Avalonia.Controls.ColorPicker/Helpers/ColorPickerHelpers.cs
+++ b/src/Avalonia.Controls.ColorPicker/Helpers/ColorPickerHelpers.cs
@@ -37,7 +37,7 @@ namespace Avalonia.Controls.Primitives
/// during calculation with the HSVA color model.
/// This will ensure colors are always discernible regardless of saturation/value.
/// A new bitmap representing a gradient of color component values.
- public static async Task CreateComponentBitmapAsync(
+ public static async Task> CreateComponentBitmapAsync(
int width,
int height,
Orientation orientation,
@@ -49,14 +49,14 @@ namespace Avalonia.Controls.Primitives
{
if (width == 0 || height == 0)
{
- return new byte[0];
+ return new ArrayList(0);
}
- var bitmap = await Task.Run(() =>
+ var bitmap = await Task.Run>(() =>
{
int pixelDataIndex = 0;
double componentStep;
- byte[] bgraPixelData;
+ ArrayList bgraPixelData;
Color baseRgbColor = Colors.White;
Color rgbColor;
int bgraPixelDataHeight;
@@ -64,7 +64,7 @@ namespace Avalonia.Controls.Primitives
// Allocate the buffer
// BGRA formatted color components 1 byte each (4 bytes in a pixel)
- bgraPixelData = new byte[width * height * 4];
+ bgraPixelData = new ArrayList(width * height * 4);
bgraPixelDataHeight = height * 4;
bgraPixelDataWidth = width * 4;
@@ -604,7 +604,7 @@ namespace Avalonia.Controls.Primitives
/// The pixel height of the bitmap.
/// A new .
public static WriteableBitmap CreateBitmapFromPixelData(
- IList bgraPixelData,
+ ArrayList bgraPixelData,
int pixelWidth,
int pixelHeight)
{
@@ -617,13 +617,31 @@ namespace Avalonia.Controls.Primitives
PixelFormat.Bgra8888,
AlphaFormat.Premul);
- // Warning: This is highly questionable
using (var frameBuffer = bitmap.Lock())
{
- Marshal.Copy(bgraPixelData.ToArray(), 0, frameBuffer.Address, bgraPixelData.Count);
+ Marshal.Copy(bgraPixelData.Array, 0, frameBuffer.Address, bgraPixelData.Array.Length);
}
return bitmap;
}
+
+ ///
+ /// Updates the given with new, raw BGRA pre-multiplied alpha pixel data.
+ /// TODO: THIS METHOD IS CURRENTLY PROVIDED AS REFERENCE BUT CAUSES INTERMITTENT CRASHES IF USED.
+ /// WARNING: The bitmap's width, height and byte count MUST not have changed and MUST be enforced externally.
+ ///
+ /// The existing to update.
+ /// The bitmap (in raw BGRA pre-multiplied alpha pixels).
+ public static void UpdateBitmapFromPixelData(
+ WriteableBitmap bitmap,
+ ArrayList bgraPixelData)
+ {
+ using (var frameBuffer = bitmap.Lock())
+ {
+ Marshal.Copy(bgraPixelData.Array, 0, frameBuffer.Address, bgraPixelData.Array.Length);
+ }
+
+ return;
+ }
}
}
diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorView.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorView.xaml
index 16bc2acdd1..b3c6d1a430 100644
--- a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorView.xaml
+++ b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorView.xaml
@@ -8,7 +8,6 @@
x:CompileBindings="True">
-
@@ -241,23 +240,21 @@
-
+
-
+
-
@@ -215,23 +214,21 @@
-
+
-
+
()
- where attribute.ForWindowingSubsystem == ""
+ where string.IsNullOrEmpty(attribute.ForWindowingSubsystem)
|| attribute.ForWindowingSubsystem == WindowingSubsystemName
- where attribute.ForRenderingSubsystem == ""
+ where string.IsNullOrEmpty(attribute.ForRenderingSubsystem)
|| attribute.ForRenderingSubsystem == RenderingSubsystemName
group attribute by attribute.Name into exports
select (from export in exports
diff --git a/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs
new file mode 100644
index 0000000000..ecbe01d8b7
--- /dev/null
+++ b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs
@@ -0,0 +1,463 @@
+// (c) Copyright Microsoft Corporation.
+// This source is subject to the Microsoft Public License (Ms-PL).
+// Please see https://go.microsoft.com/fwlink/?LinkID=131993 for details.
+// All other rights reserved.
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using Avalonia.Controls.Templates;
+using Avalonia.Data;
+
+namespace Avalonia.Controls
+{
+ public partial class AutoCompleteBox
+ {
+ public static readonly StyledProperty WatermarkProperty =
+ TextBox.WatermarkProperty.AddOwner();
+
+ ///
+ /// Identifies the property.
+ ///
+ /// The identifier for the property.
+ public static readonly StyledProperty MinimumPrefixLengthProperty =
+ AvaloniaProperty.Register(
+ nameof(MinimumPrefixLength), 1,
+ validate: IsValidMinimumPrefixLength);
+
+ ///
+ /// Identifies the property.
+ ///
+ /// The identifier for the property.
+ public static readonly StyledProperty MinimumPopulateDelayProperty =
+ AvaloniaProperty.Register(
+ nameof(MinimumPopulateDelay),
+ TimeSpan.Zero,
+ validate: IsValidMinimumPopulateDelay);
+
+ ///
+ /// Identifies the property.
+ ///
+ /// The identifier for the property.
+ public static readonly StyledProperty MaxDropDownHeightProperty =
+ AvaloniaProperty.Register(
+ nameof(MaxDropDownHeight),
+ double.PositiveInfinity,
+ validate: IsValidMaxDropDownHeight);
+
+ ///
+ /// Identifies the property.
+ ///
+ /// The identifier for the property.
+ public static readonly StyledProperty IsTextCompletionEnabledProperty =
+ AvaloniaProperty.Register(nameof(IsTextCompletionEnabled));
+
+ ///
+ /// Identifies the property.
+ ///
+ /// The identifier for the property.
+ public static readonly StyledProperty ItemTemplateProperty =
+ AvaloniaProperty.Register(nameof(ItemTemplate));
+
+ ///
+ /// Identifies the property.
+ ///
+ /// The identifier for the property.
+ public static readonly DirectProperty IsDropDownOpenProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(IsDropDownOpen),
+ o => o.IsDropDownOpen,
+ (o, v) => o.IsDropDownOpen = v);
+
+ ///
+ /// Identifies the property.
+ ///
+ /// The identifier the property.
+ public static readonly DirectProperty SelectedItemProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(SelectedItem),
+ o => o.SelectedItem,
+ (o, v) => o.SelectedItem = v,
+ defaultBindingMode: BindingMode.TwoWay,
+ enableDataValidation: true);
+
+ ///
+ /// Identifies the property.
+ ///
+ /// The identifier for the property.
+ public static readonly DirectProperty TextProperty =
+ TextBlock.TextProperty.AddOwnerWithDataValidation(
+ o => o.Text,
+ (o, v) => o.Text = v,
+ defaultBindingMode: BindingMode.TwoWay,
+ enableDataValidation: true);
+
+ ///
+ /// Identifies the property.
+ ///
+ /// The identifier for the property.
+ public static readonly DirectProperty SearchTextProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(SearchText),
+ o => o.SearchText,
+ unsetValue: string.Empty);
+
+ ///
+ /// Gets the identifier for the property.
+ ///
+ public static readonly StyledProperty FilterModeProperty =
+ AvaloniaProperty.Register(
+ nameof(FilterMode),
+ defaultValue: AutoCompleteFilterMode.StartsWith,
+ validate: IsValidFilterMode);
+
+ ///
+ /// Identifies the property.
+ ///
+ /// The identifier for the property.
+ public static readonly DirectProperty?> ItemFilterProperty =
+ AvaloniaProperty.RegisterDirect?>(
+ nameof(ItemFilter),
+ o => o.ItemFilter,
+ (o, v) => o.ItemFilter = v);
+
+ ///
+ /// Identifies the property.
+ ///
+ /// The identifier for the property.
+ public static readonly DirectProperty?> TextFilterProperty =
+ AvaloniaProperty.RegisterDirect?>(
+ nameof(TextFilter),
+ o => o.TextFilter,
+ (o, v) => o.TextFilter = v,
+ unsetValue: AutoCompleteSearch.GetFilter(AutoCompleteFilterMode.StartsWith));
+
+ ///
+ /// Identifies the property.
+ ///
+ /// The identifier for the property.
+ public static readonly DirectProperty?> ItemSelectorProperty =
+ AvaloniaProperty.RegisterDirect?>(
+ nameof(ItemSelector),
+ o => o.ItemSelector,
+ (o, v) => o.ItemSelector = v);
+
+ ///
+ /// Identifies the property.
+ ///
+ /// The identifier for the property.
+ public static readonly DirectProperty?> TextSelectorProperty =
+ AvaloniaProperty.RegisterDirect?>(
+ nameof(TextSelector),
+ o => o.TextSelector,
+ (o, v) => o.TextSelector = v);
+
+ ///
+ /// Identifies the property.
+ ///
+ /// The identifier for the property.
+ public static readonly DirectProperty ItemsProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(Items),
+ o => o.Items,
+ (o, v) => o.Items = v);
+
+ public static readonly DirectProperty>>?> AsyncPopulatorProperty =
+ AvaloniaProperty.RegisterDirect>>?>(
+ nameof(AsyncPopulator),
+ o => o.AsyncPopulator,
+ (o, v) => o.AsyncPopulator = v);
+
+ ///
+ /// Gets or sets the minimum number of characters required to be entered
+ /// in the text box before the displays possible matches.
+ ///
+ ///
+ /// The minimum number of characters to be entered in the text box
+ /// before the
+ /// displays possible matches. The default is 1.
+ ///
+ ///
+ /// If you set MinimumPrefixLength to -1, the AutoCompleteBox will
+ /// not provide possible matches. There is no maximum value, but
+ /// setting MinimumPrefixLength to value that is too large will
+ /// prevent the AutoCompleteBox from providing possible matches as well.
+ ///
+ public int MinimumPrefixLength
+ {
+ get => GetValue(MinimumPrefixLengthProperty);
+ set => SetValue(MinimumPrefixLengthProperty, value);
+ }
+
+ ///
+ /// Gets or sets a value indicating whether the first possible match
+ /// found during the filtering process will be displayed automatically
+ /// in the text box.
+ ///
+ ///
+ /// True if the first possible match found will be displayed
+ /// automatically in the text box; otherwise, false. The default is
+ /// false.
+ ///
+ public bool IsTextCompletionEnabled
+ {
+ get => GetValue(IsTextCompletionEnabledProperty);
+ set => SetValue(IsTextCompletionEnabledProperty, value);
+ }
+
+ ///
+ /// Gets or sets the used
+ /// to display each item in the drop-down portion of the control.
+ ///
+ /// The used to
+ /// display each item in the drop-down. The default is null.
+ ///
+ /// You use the ItemTemplate property to specify the visualization
+ /// of the data objects in the drop-down portion of the AutoCompleteBox
+ /// control. If your AutoCompleteBox is bound to a collection and you
+ /// do not provide specific display instructions by using a
+ /// DataTemplate, the resulting UI of each item is a string
+ /// representation of each object in the underlying collection.
+ ///
+ public IDataTemplate ItemTemplate
+ {
+ get => GetValue(ItemTemplateProperty);
+ set => SetValue(ItemTemplateProperty, value);
+ }
+
+ ///
+ /// Gets or sets the minimum delay, after text is typed
+ /// in the text box before the
+ /// control
+ /// populates the list of possible matches in the drop-down.
+ ///
+ /// The minimum delay, after text is typed in
+ /// the text box, but before the
+ /// populates
+ /// the list of possible matches in the drop-down. The default is 0.
+ public TimeSpan MinimumPopulateDelay
+ {
+ get => GetValue(MinimumPopulateDelayProperty);
+ set => SetValue(MinimumPopulateDelayProperty, value);
+ }
+
+ ///
+ /// Gets or sets the maximum height of the drop-down portion of the
+ /// control.
+ ///
+ /// The maximum height of the drop-down portion of the
+ /// control.
+ /// The default is .
+ /// The specified value is less than 0.
+ public double MaxDropDownHeight
+ {
+ get => GetValue(MaxDropDownHeightProperty);
+ set => SetValue(MaxDropDownHeightProperty, value);
+ }
+
+ ///
+ /// Gets or sets a value indicating whether the drop-down portion of
+ /// the control is open.
+ ///
+ ///
+ /// True if the drop-down is open; otherwise, false. The default is
+ /// false.
+ ///
+ public bool IsDropDownOpen
+ {
+ get => _isDropDownOpen;
+ set => SetAndRaise(IsDropDownOpenProperty, ref _isDropDownOpen, value);
+ }
+
+ ///
+ /// Gets or sets the that
+ /// is used to get the values for display in the text portion of
+ /// the
+ /// control.
+ ///
+ /// The object used
+ /// when binding to a collection property.
+ [AssignBinding]
+ public IBinding? ValueMemberBinding
+ {
+ get => _valueBindingEvaluator?.ValueBinding;
+ set
+ {
+ if (ValueMemberBinding != value)
+ {
+ _valueBindingEvaluator = new BindingEvaluator(value);
+ OnValueMemberBindingChanged(value);
+ }
+ }
+ }
+
+ ///
+ /// Gets or sets the selected item in the drop-down.
+ ///
+ /// The selected item in the drop-down.
+ ///
+ /// If the IsTextCompletionEnabled property is true and text typed by
+ /// the user matches an item in the ItemsSource collection, which is
+ /// then displayed in the text box, the SelectedItem property will be
+ /// a null reference.
+ ///
+ public object? SelectedItem
+ {
+ get => _selectedItem;
+ set => SetAndRaise(SelectedItemProperty, ref _selectedItem, value);
+ }
+
+ ///
+ /// Gets or sets the text in the text box portion of the
+ /// control.
+ ///
+ /// The text in the text box portion of the
+ /// control.
+ public string? Text
+ {
+ get => _text;
+ set => SetAndRaise(TextProperty, ref _text, value);
+ }
+
+ ///
+ /// Gets the text that is used to filter items in the
+ /// item collection.
+ ///
+ /// The text that is used to filter items in the
+ /// item collection.
+ ///
+ /// The SearchText value is typically the same as the
+ /// Text property, but is set after the TextChanged event occurs
+ /// and before the Populating event.
+ ///
+ public string? SearchText
+ {
+ get => _searchText;
+ private set
+ {
+ try
+ {
+ _allowWrite = true;
+ SetAndRaise(SearchTextProperty, ref _searchText, value);
+ }
+ finally
+ {
+ _allowWrite = false;
+ }
+ }
+ }
+
+ ///
+ /// Gets or sets how the text in the text box is used to filter items
+ /// specified by the
+ /// property for display in the drop-down.
+ ///
+ /// One of the
+ /// values The default is .
+ /// The specified value is not a valid
+ /// .
+ ///
+ /// Use the FilterMode property to specify how possible matches are
+ /// filtered. For example, possible matches can be filtered in a
+ /// predefined or custom way. The search mode is automatically set to
+ /// Custom if you set the ItemFilter property.
+ ///
+ public AutoCompleteFilterMode FilterMode
+ {
+ get => GetValue(FilterModeProperty);
+ set => SetValue(FilterModeProperty, value);
+ }
+
+ public string? Watermark
+ {
+ get => GetValue(WatermarkProperty);
+ set => SetValue(WatermarkProperty, value);
+ }
+
+ ///
+ /// Gets or sets the custom method that uses user-entered text to filter
+ /// 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
+ /// property. The default is null.
+ ///
+ /// The filter mode is automatically set to Custom if you set the
+ /// ItemFilter property.
+ ///
+ public AutoCompleteFilterPredicate
-
+
+
diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml
index 1270dbaa62..d2b31fdb20 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml
+++ b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml
@@ -3,8 +3,7 @@
xmlns:views="clr-namespace:Avalonia.Diagnostics.Views"
xmlns:diag="clr-namespace:Avalonia.Diagnostics"
Title="Avalonia DevTools"
- x:Class="Avalonia.Diagnostics.Views.MainWindow"
- Theme="{StaticResource {x:Type Window}}">
+ x:Class="Avalonia.Diagnostics.Views.MainWindow">
diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs
index c81997f2cb..e6e630112b 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs
+++ b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs
@@ -28,6 +28,11 @@ namespace Avalonia.Diagnostics.Views
{
InitializeComponent();
+ // Apply the SimpleTheme.Window theme; this must be done after the XAML is parsed as
+ // the theme is included in the MainWindow's XAML.
+ if (Theme is null && this.FindResource(typeof(Window)) is ControlTheme windowTheme)
+ Theme = windowTheme;
+
_keySubscription = InputManager.Instance?.Process
.OfType()
.Where(x => x.Type == RawKeyEventType.KeyDown)
diff --git a/src/Avalonia.FreeDesktop/DBusMenuExporter.cs b/src/Avalonia.FreeDesktop/DBusMenuExporter.cs
index c0511420a6..657e324010 100644
--- a/src/Avalonia.FreeDesktop/DBusMenuExporter.cs
+++ b/src/Avalonia.FreeDesktop/DBusMenuExporter.cs
@@ -340,7 +340,7 @@ namespace Avalonia.FreeDesktop
{
var id = item == null ? 0 : GetId(item);
var props = GetProperties((item, menu), propertyNames);
- var children = (depth == 0 || menu == null) ? new object[0] : new object[menu.Items.Count];
+ var children = (depth == 0 || menu == null) ? Array.Empty() : new object[menu.Items.Count];
if(menu != null)
for (var c = 0; c < children.Length; c++)
{
@@ -397,7 +397,7 @@ namespace Avalonia.FreeDesktop
{
foreach (var e in Events)
HandleEvent(e.id, e.eventId, e.data, e.timestamp);
- return Task.FromResult(new int[0]);
+ return Task.FromResult(Array.Empty());
}
public async Task AboutToShowAsync(int Id)
@@ -407,7 +407,7 @@ namespace Avalonia.FreeDesktop
public async Task<(int[] updatesNeeded, int[] idErrors)> AboutToShowGroupAsync(int[] Ids)
{
- return (new int[0], new int[0]);
+ return (Array.Empty(), Array.Empty());
}
#region Events
diff --git a/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs b/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs
index cb23c6c336..fcd7f1e31f 100644
--- a/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs
+++ b/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs
@@ -110,14 +110,24 @@ namespace Avalonia.Headless
return new HeadlessBitmapStub(destinationSize, new Vector(96, 96));
}
- public IGlyphRunImpl CreateGlyphRun(GlyphRun glyphRun)
+ public IGeometryImpl BuildGlyphRunGeometry(GlyphRun glyphRun)
{
- return new HeadlessGlyphRunStub();
+ return new HeadlessGeometryStub(new Rect(glyphRun.Size));
}
- public IGeometryImpl BuildGlyphRunGeometry(GlyphRun glyphRun)
+ public IGlyphRunBuffer AllocateGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
{
- return new HeadlessGeometryStub(new Rect(glyphRun.Size));
+ return new HeadlessGlyphRunBufferStub();
+ }
+
+ public IHorizontalGlyphRunBuffer AllocateHorizontalGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
+ {
+ return new HeadlessHorizontalGlyphRunBufferStub();
+ }
+
+ public IPositionedGlyphRunBuffer AllocatePositionedGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
+ {
+ return new HeadlessPositionedGlyphRunBufferStub();
}
class HeadlessGeometryStub : IGeometryImpl
@@ -203,6 +213,26 @@ namespace Avalonia.Headless
public Matrix Transform { get; }
}
+ class HeadlessGlyphRunBufferStub : IGlyphRunBuffer
+ {
+ public Span GlyphIndices => Span.Empty;
+
+ public IGlyphRunImpl Build()
+ {
+ return new HeadlessGlyphRunStub();
+ }
+ }
+
+ class HeadlessHorizontalGlyphRunBufferStub : HeadlessGlyphRunBufferStub, IHorizontalGlyphRunBuffer
+ {
+ public Span GlyphPositions => Span.Empty;
+ }
+
+ class HeadlessPositionedGlyphRunBufferStub : HeadlessGlyphRunBufferStub, IPositionedGlyphRunBuffer
+ {
+ public Span GlyphPositions => Span.Empty;
+ }
+
class HeadlessGlyphRunStub : IGlyphRunImpl
{
public void Dispose()
diff --git a/src/Avalonia.Headless/HeadlessPlatformStubs.cs b/src/Avalonia.Headless/HeadlessPlatformStubs.cs
index ed51529a96..76948e9286 100644
--- a/src/Avalonia.Headless/HeadlessPlatformStubs.cs
+++ b/src/Avalonia.Headless/HeadlessPlatformStubs.cs
@@ -75,8 +75,13 @@ namespace Avalonia.Headless
public TimeSpan TouchDoubleClickTime => DoubleClickTime;
}
- class HeadlessGlyphTypefaceImpl : IGlyphTypefaceImpl
+ class HeadlessGlyphTypefaceImpl : IGlyphTypeface
{
+ public FontMetrics Metrics => new FontMetrics
+ {
+
+ };
+
public short DesignEmHeight => 10;
public int Ascent => 5;
@@ -95,6 +100,10 @@ namespace Avalonia.Headless
public bool IsFixedPitch => true;
+ public int GlyphCount => 1337;
+
+ public FontSimulations FontSimulations { get; }
+
public void Dispose()
{
}
@@ -104,6 +113,13 @@ namespace Avalonia.Headless
return 1;
}
+ public bool TryGetGlyph(uint codepoint, out ushort glyph)
+ {
+ glyph = 1;
+
+ return true;
+ }
+
public int GetGlyphAdvance(ushort glyph)
{
return 1;
@@ -118,6 +134,23 @@ namespace Avalonia.Headless
{
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 = 10
+ };
+
+ return true;
+ }
}
class HeadlessTextShaperStub : ITextShaperImpl
@@ -134,7 +167,7 @@ namespace Avalonia.Headless
class HeadlessFontManagerStub : IFontManagerImpl
{
- public IGlyphTypefaceImpl CreateGlyphTypeface(Typeface typeface)
+ public IGlyphTypeface CreateGlyphTypeface(Typeface typeface)
{
return new HeadlessGlyphTypefaceImpl();
}
diff --git a/src/Avalonia.MicroCom/Avalonia.MicroCom.csproj b/src/Avalonia.MicroCom/Avalonia.MicroCom.csproj
index d7f39f6642..e784bda105 100644
--- a/src/Avalonia.MicroCom/Avalonia.MicroCom.csproj
+++ b/src/Avalonia.MicroCom/Avalonia.MicroCom.csproj
@@ -3,10 +3,8 @@
netstandard2.0
true
-
- <_AvaloniaPatchComInterop>true
-
+
false
all
diff --git a/src/Avalonia.MicroCom/CallbackBase.cs b/src/Avalonia.MicroCom/CallbackBase.cs
index 6783ebe3dc..49d589a5fc 100644
--- a/src/Avalonia.MicroCom/CallbackBase.cs
+++ b/src/Avalonia.MicroCom/CallbackBase.cs
@@ -1,4 +1,6 @@
-namespace Avalonia.MicroCom
+using MicroCom.Runtime;
+
+namespace Avalonia.MicroCom
{
public abstract class CallbackBase : IUnknown, IMicroComShadowContainer
{
diff --git a/src/Avalonia.MicroCom/IMicroComExceptionCallback.cs b/src/Avalonia.MicroCom/IMicroComExceptionCallback.cs
deleted file mode 100644
index 08f20339ec..0000000000
--- a/src/Avalonia.MicroCom/IMicroComExceptionCallback.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using System;
-
-namespace Avalonia.MicroCom
-{
- public interface IMicroComExceptionCallback
- {
- void RaiseException(Exception e);
- }
-}
diff --git a/src/Avalonia.MicroCom/IMicroComShadowContainer.cs b/src/Avalonia.MicroCom/IMicroComShadowContainer.cs
deleted file mode 100644
index a33d3a9811..0000000000
--- a/src/Avalonia.MicroCom/IMicroComShadowContainer.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace Avalonia.MicroCom
-{
- public interface IMicroComShadowContainer
- {
- MicroComShadow Shadow { get; set; }
- void OnReferencedFromNative();
- void OnUnreferencedFromNative();
- }
-}
diff --git a/src/Avalonia.MicroCom/IUnknown.cs b/src/Avalonia.MicroCom/IUnknown.cs
deleted file mode 100644
index 0dc4106423..0000000000
--- a/src/Avalonia.MicroCom/IUnknown.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-using System;
-
-namespace Avalonia.MicroCom
-{
- public interface IUnknown : IDisposable
- {
- }
-}
diff --git a/src/Avalonia.MicroCom/LocalInterop.cs b/src/Avalonia.MicroCom/LocalInterop.cs
deleted file mode 100644
index 785f4e03a5..0000000000
--- a/src/Avalonia.MicroCom/LocalInterop.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using System;
-
-namespace Avalonia.MicroCom
-{
- unsafe class LocalInterop
- {
- public static unsafe void CalliStdCallvoid(void* thisObject, void* methodPtr)
- {
- throw null;
- }
-
- public static unsafe int CalliStdCallint(void* thisObject, Guid* guid, IntPtr* ppv, void* methodPtr)
- {
- throw null;
- }
- }
-}
diff --git a/src/Avalonia.MicroCom/MicroComProxyBase.cs b/src/Avalonia.MicroCom/MicroComProxyBase.cs
deleted file mode 100644
index fe8a2bf9cf..0000000000
--- a/src/Avalonia.MicroCom/MicroComProxyBase.cs
+++ /dev/null
@@ -1,110 +0,0 @@
-using System;
-using System.Runtime.ConstrainedExecution;
-using System.Runtime.InteropServices;
-using System.Threading;
-
-namespace Avalonia.MicroCom
-{
- public unsafe class MicroComProxyBase : CriticalFinalizerObject, IUnknown
- {
- private IntPtr _nativePointer;
- private bool _ownsHandle;
- private SynchronizationContext _synchronizationContext;
-
- public IntPtr NativePointer
- {
- get
- {
- if (_nativePointer == IntPtr.Zero)
- throw new ObjectDisposedException(this.GetType().FullName);
- return _nativePointer;
- }
- }
-
- public void*** PPV => (void***)NativePointer;
-
- public MicroComProxyBase(IntPtr nativePointer, bool ownsHandle)
- {
- _nativePointer = nativePointer;
- _ownsHandle = ownsHandle;
- _synchronizationContext = SynchronizationContext.Current;
- if(!_ownsHandle)
- GC.SuppressFinalize(this);
- }
-
- protected virtual int VTableSize => 3;
-
- public void AddRef()
- {
- LocalInterop.CalliStdCallvoid(PPV, (*PPV)[1]);
- }
-
- public void Release()
- {
- LocalInterop.CalliStdCallvoid(PPV, (*PPV)[2]);
- }
-
- public int QueryInterface(Guid guid, out IntPtr ppv)
- {
- IntPtr r = default;
- var rv = LocalInterop.CalliStdCallint(PPV, &guid, &r, (*PPV)[0]);
- ppv = r;
- return rv;
- }
-
- public T QueryInterface() where T : IUnknown
- {
- var guid = MicroComRuntime.GetGuidFor(typeof(T));
- var rv = QueryInterface(guid, out var ppv);
- if (rv == 0)
- return (T)MicroComRuntime.CreateProxyFor(typeof(T), ppv, true);
- throw new COMException("QueryInterface failed", rv);
- }
-
- public bool IsDisposed => _nativePointer == IntPtr.Zero;
-
- protected virtual void Dispose(bool disposing)
- {
- if(_nativePointer == IntPtr.Zero)
- return;
- if (_ownsHandle)
- {
- Release();
- _ownsHandle = false;
- }
- _nativePointer = IntPtr.Zero;
- GC.SuppressFinalize(this);
- }
-
- public void Dispose() => Dispose(true);
-
- public bool OwnsHandle => _ownsHandle;
-
- public void EnsureOwned()
- {
- if (!_ownsHandle)
- {
- GC.ReRegisterForFinalize(true);
- AddRef();
- _ownsHandle = true;
- }
- }
-
- private static readonly SendOrPostCallback _disposeDelegate = DisposeOnContext;
-
- private static void DisposeOnContext(object state)
- {
- (state as MicroComProxyBase)?.Dispose(false);
- }
-
- ~MicroComProxyBase()
- {
- if(!_ownsHandle)
- return;
- if (_synchronizationContext == null)
- Dispose();
- else
- _synchronizationContext.Post(_disposeDelegate, this);
- }
- }
-}
diff --git a/src/Avalonia.MicroCom/MicroComRuntime.cs b/src/Avalonia.MicroCom/MicroComRuntime.cs
deleted file mode 100644
index b9a56a69ba..0000000000
--- a/src/Avalonia.MicroCom/MicroComRuntime.cs
+++ /dev/null
@@ -1,143 +0,0 @@
-using System;
-using System.Collections.Concurrent;
-using System.Runtime.InteropServices;
-
-namespace Avalonia.MicroCom
-{
- public static unsafe class MicroComRuntime
- {
- private static ConcurrentDictionary _vtables = new ConcurrentDictionary();
-
- private static ConcurrentDictionary> _factories =
- new ConcurrentDictionary>();
- private static ConcurrentDictionary _guids = new ConcurrentDictionary();
- private static ConcurrentDictionary _guidsToTypes = new ConcurrentDictionary();
-
- internal static readonly Guid ManagedObjectInterfaceGuid = Guid.Parse("cd7687c0-a9c2-4563-b08e-a399df50c633");
-
- static MicroComRuntime()
- {
- Register(typeof(IUnknown), new Guid("00000000-0000-0000-C000-000000000046"),
- (ppv, owns) => new MicroComProxyBase(ppv, owns));
- RegisterVTable(typeof(IUnknown), MicroComVtblBase.Vtable);
- }
-
- public static void RegisterVTable(Type t, IntPtr vtable)
- {
- _vtables[t] = vtable;
- }
-
- public static void Register(Type t, Guid guid, Func proxyFactory)
- {
- _factories[t] = proxyFactory;
- _guids[t] = guid;
- _guidsToTypes[guid] = t;
- }
-
- public static Guid GetGuidFor(Type type) => _guids[type];
-
- public static T CreateProxyFor(void* pObject, bool ownsHandle) => (T)CreateProxyFor(typeof(T), new IntPtr(pObject), ownsHandle);
- public static T CreateProxyFor(IntPtr pObject, bool ownsHandle) => (T)CreateProxyFor(typeof(T), pObject, ownsHandle);
-
- public static T CreateProxyOrNullFor(void* pObject, bool ownsHandle) where T : class =>
- pObject == null ? null : (T)CreateProxyFor(typeof(T), new IntPtr(pObject), ownsHandle);
-
- public static T CreateProxyOrNullFor(IntPtr pObject, bool ownsHandle) where T : class =>
- pObject == IntPtr.Zero ? null : (T)CreateProxyFor(typeof(T), pObject, ownsHandle);
-
- public static object CreateProxyFor(Type type, IntPtr pObject, bool ownsHandle) => _factories[type](pObject, ownsHandle);
-
- public static IntPtr GetNativeIntPtr(this T obj, bool owned = false) where T : IUnknown
- => new IntPtr(GetNativePointer(obj, owned));
- public static void* GetNativePointer(T obj, bool owned = false) where T : IUnknown
- {
- if (obj == null)
- return null;
- if (obj is MicroComProxyBase proxy)
- {
- if(owned)
- proxy.AddRef();
- return (void*)proxy.NativePointer;
- }
-
- if (obj is IMicroComShadowContainer container)
- {
- container.Shadow ??= new MicroComShadow(container);
- void* ptr = null;
- var res = container.Shadow.GetOrCreateNativePointer(typeof(T), &ptr);
- if (res != 0)
- throw new COMException(
- "Unable to create native callable wrapper for type " + typeof(T) + " for instance of type " +
- obj.GetType(),
- res);
- if (owned)
- container.Shadow.AddRef((Ccw*)ptr);
- return ptr;
- }
- throw new ArgumentException("Unable to get a native pointer for " + obj);
- }
-
- public static object GetObjectFromCcw(IntPtr ccw)
- {
- var ptr = (Ccw*)ccw;
- var shadow = (MicroComShadow)GCHandle.FromIntPtr(ptr->GcShadowHandle).Target;
- return shadow.Target;
- }
-
- public static bool IsComWrapper(IUnknown obj) => obj is MicroComProxyBase;
-
- public static object TryUnwrapManagedObject(IUnknown obj)
- {
- if (obj is not MicroComProxyBase proxy)
- return null;
- if (proxy.QueryInterface(ManagedObjectInterfaceGuid, out _) != 0)
- return null;
- // Successful QueryInterface always increments ref counter
- proxy.Release();
- return GetObjectFromCcw(proxy.NativePointer);
- }
-
- public static bool TryGetTypeForGuid(Guid guid, out Type t) => _guidsToTypes.TryGetValue(guid, out t);
-
- public static bool GetVtableFor(Type type, out IntPtr ptr) => _vtables.TryGetValue(type, out ptr);
-
- public static void UnhandledException(object target, Exception e)
- {
- if (target is IMicroComExceptionCallback cb)
- {
- try
- {
- cb.RaiseException(e);
- }
- catch
- {
- // We've tried
- }
- }
-
- }
-
- public static T CloneReference(this T iface) where T : IUnknown
- {
- var proxy = (MicroComProxyBase)(object)iface;
- var ownedPointer = GetNativePointer(iface, true);
- return CreateProxyFor(ownedPointer, true);
- }
-
- public static T QueryInterface(this IUnknown unknown) where T : IUnknown
- {
- var proxy = (MicroComProxyBase)unknown;
- return proxy.QueryInterface();
- }
-
- public static void UnsafeAddRef(this IUnknown unknown)
- {
- ((MicroComProxyBase)unknown).AddRef();
- }
-
- public static void UnsafeRelease(this IUnknown unknown)
- {
- ((MicroComProxyBase)unknown).Release();
- }
- }
-}
diff --git a/src/Avalonia.MicroCom/MicroComShadow.cs b/src/Avalonia.MicroCom/MicroComShadow.cs
deleted file mode 100644
index 765ff3b9ad..0000000000
--- a/src/Avalonia.MicroCom/MicroComShadow.cs
+++ /dev/null
@@ -1,177 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Runtime.InteropServices;
-using System.Threading;
-
-namespace Avalonia.MicroCom
-{
- public unsafe class MicroComShadow : IDisposable
- {
- private readonly object _lock = new object();
- private readonly Dictionary _shadows = new Dictionary();
- private readonly Dictionary _backShadows = new Dictionary();
- private GCHandle? _handle;
- private volatile int _refCount;
- internal IMicroComShadowContainer Target { get; }
- internal MicroComShadow(IMicroComShadowContainer target)
- {
- Target = target;
- Target.Shadow = this;
- }
-
- internal int QueryInterface(Ccw* ccw, Guid* guid, void** ppv)
- {
- if (MicroComRuntime.TryGetTypeForGuid(*guid, out var type))
- return QueryInterface(type, ppv);
- else if (*guid == MicroComRuntime.ManagedObjectInterfaceGuid)
- {
- ccw->RefCount++;
- *ppv = ccw;
- return 0;
- }
- else
- return unchecked((int)0x80004002u);
- }
-
- internal int QueryInterface(Type type, void** ppv)
- {
- if (!type.IsInstanceOfType(Target))
- return unchecked((int)0x80004002u);
-
- var rv = GetOrCreateNativePointer(type, ppv);
- if (rv == 0)
- AddRef((Ccw*)*ppv);
- return rv;
- }
-
- internal int GetOrCreateNativePointer(Type type, void** ppv)
- {
- if (!MicroComRuntime.GetVtableFor(type, out var vtable))
- return unchecked((int)0x80004002u);
- lock (_lock)
- {
-
- if (_shadows.TryGetValue(type, out var shadow))
- {
- var targetCcw = (Ccw*)shadow;
- AddRef(targetCcw);
- *ppv = targetCcw;
- return 0;
- }
- else
- {
- var intPtr = Marshal.AllocHGlobal(Marshal.SizeOf());
- var targetCcw = (Ccw*)intPtr;
- *targetCcw = default;
- targetCcw->RefCount = 0;
- targetCcw->VTable = vtable;
- if (_handle == null)
- _handle = GCHandle.Alloc(this);
- targetCcw->GcShadowHandle = GCHandle.ToIntPtr(_handle.Value);
- _shadows[type] = intPtr;
- _backShadows[intPtr] = type;
- *ppv = targetCcw;
-
- return 0;
- }
- }
- }
-
- internal int AddRef(Ccw* ccw)
- {
- if (Interlocked.Increment(ref _refCount) == 1)
- {
- try
- {
- Target.OnReferencedFromNative();
- }
- catch (Exception e)
- {
- MicroComRuntime.UnhandledException(Target, e);
- }
- }
-
- return Interlocked.Increment(ref ccw->RefCount);
- }
-
- internal int Release(Ccw* ccw)
- {
- Interlocked.Decrement(ref _refCount);
- var cnt = Interlocked.Decrement(ref ccw->RefCount);
- if (cnt == 0)
- return FreeCcw(ccw);
-
- return cnt;
- }
-
- int FreeCcw(Ccw* ccw)
- {
- lock (_lock)
- {
- // Shadow got resurrected by a call to QueryInterface from another thread
- if (ccw->RefCount != 0)
- return ccw->RefCount;
-
- var intPtr = new IntPtr(ccw);
- var type = _backShadows[intPtr];
- _backShadows.Remove(intPtr);
- _shadows.Remove(type);
- Marshal.FreeHGlobal(intPtr);
- if (_shadows.Count == 0)
- {
- _handle?.Free();
- _handle = null;
- try
- {
- Target.OnUnreferencedFromNative();
- }
- catch(Exception e)
- {
- MicroComRuntime.UnhandledException(Target, e);
- }
- }
- }
-
- return 0;
- }
-
- /*
- Needs to be called to support the following scenario:
- 1) Object created
- 2) Object passed to native code, shadow is created, CCW is created
- 3) Native side has never called AddRef
-
- In that case the GC handle to the shadow object is still alive
- */
-
- public void Dispose()
- {
- lock (_lock)
- {
- List toRemove = null;
- foreach (var kv in _backShadows)
- {
- var ccw = (Ccw*)kv.Key;
- if (ccw->RefCount == 0)
- {
- toRemove ??= new List();
- toRemove.Add(kv.Key);
- }
- }
-
- if(toRemove != null)
- foreach (var intPtr in toRemove)
- FreeCcw((Ccw*)intPtr);
- }
- }
- }
-
- [StructLayout(LayoutKind.Sequential)]
- struct Ccw
- {
- public IntPtr VTable;
- public IntPtr GcShadowHandle;
- public volatile int RefCount;
- public MicroComShadow GetShadow() => (MicroComShadow)GCHandle.FromIntPtr(GcShadowHandle).Target;
- }
-}
diff --git a/src/Avalonia.MicroCom/MicroComVtblBase.cs b/src/Avalonia.MicroCom/MicroComVtblBase.cs
deleted file mode 100644
index 7092f8131a..0000000000
--- a/src/Avalonia.MicroCom/MicroComVtblBase.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Runtime.InteropServices;
-
-namespace Avalonia.MicroCom
-{
- public unsafe class MicroComVtblBase
- {
- private List _methods = new List();
- [UnmanagedFunctionPointerAttribute(CallingConvention.StdCall)]
- private delegate int AddRefDelegate(Ccw* ccw);
-
- [UnmanagedFunctionPointerAttribute(CallingConvention.StdCall)]
- private delegate int QueryInterfaceDelegate(Ccw* ccw, Guid* guid, void** ppv);
-
- public static IntPtr Vtable { get; } = new MicroComVtblBase().CreateVTable();
- public MicroComVtblBase()
- {
- AddMethod((QueryInterfaceDelegate)QueryInterface);
- AddMethod((AddRefDelegate)AddRef);
- AddMethod((AddRefDelegate)Release);
- }
-
- protected void AddMethod(void* f)
- {
- _methods.Add(new IntPtr(f));
- }
-
- protected void AddMethod(Delegate d)
- {
- GCHandle.Alloc(d);
- _methods.Add(Marshal.GetFunctionPointerForDelegate(d));
- }
-
- protected unsafe IntPtr CreateVTable()
- {
- var ptr = (IntPtr*)Marshal.AllocHGlobal((IntPtr.Size + 1) * _methods.Count);
- for (var c = 0; c < _methods.Count; c++)
- ptr[c] = _methods[c];
- return new IntPtr(ptr);
- }
-
- static int QueryInterface(Ccw* ccw, Guid* guid, void** ppv) => ccw->GetShadow().QueryInterface(ccw, guid, ppv);
- static int AddRef(Ccw* ccw) => ccw->GetShadow().AddRef(ccw);
- static int Release(Ccw* ccw) => ccw->GetShadow().Release(ccw);
- }
-}
diff --git a/src/Avalonia.Native/Avalonia.Native.csproj b/src/Avalonia.Native/Avalonia.Native.csproj
index 2001a2fcbc..4ceb1be340 100644
--- a/src/Avalonia.Native/Avalonia.Native.csproj
+++ b/src/Avalonia.Native/Avalonia.Native.csproj
@@ -6,7 +6,6 @@
true
net6.0;netstandard2.0
true
- Avalonia.MicroCom
diff --git a/src/Avalonia.Native/AvaloniaNativePlatform.cs b/src/Avalonia.Native/AvaloniaNativePlatform.cs
index b45fe5559b..532893884a 100644
--- a/src/Avalonia.Native/AvaloniaNativePlatform.cs
+++ b/src/Avalonia.Native/AvaloniaNativePlatform.cs
@@ -10,6 +10,7 @@ using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Rendering.Composition;
using JetBrains.Annotations;
+using MicroCom.Runtime;
namespace Avalonia.Native
{
diff --git a/src/Avalonia.Native/CallbackBase.cs b/src/Avalonia.Native/CallbackBase.cs
index 56f9505cb4..2d875dbc0e 100644
--- a/src/Avalonia.Native/CallbackBase.cs
+++ b/src/Avalonia.Native/CallbackBase.cs
@@ -2,6 +2,7 @@
using System.Runtime.ExceptionServices;
using Avalonia.MicroCom;
using Avalonia.Platform;
+using MicroCom.Runtime;
namespace Avalonia.Native
{
diff --git a/src/Avalonia.Native/IconLoader.cs b/src/Avalonia.Native/IconLoader.cs
index edb8b94e83..04779a43aa 100644
--- a/src/Avalonia.Native/IconLoader.cs
+++ b/src/Avalonia.Native/IconLoader.cs
@@ -6,7 +6,7 @@ namespace Avalonia.Native
// OSX doesn't have a concept of *window* icon.
// Icons in the title bar are only shown if there is
// an opened file (on disk) associated with the current window
- // see http://stackoverflow.com/a/7038671/2231814
+ // see https://stackoverflow.com/a/7038671/2231814
class IconLoader : IPlatformIconLoader
{
class IconStub : IWindowIconImpl
diff --git a/src/Avalonia.Native/NativeControlHostImpl.cs b/src/Avalonia.Native/NativeControlHostImpl.cs
index 2c9c1728d3..8a3488d95e 100644
--- a/src/Avalonia.Native/NativeControlHostImpl.cs
+++ b/src/Avalonia.Native/NativeControlHostImpl.cs
@@ -4,6 +4,7 @@ using Avalonia.MicroCom;
using Avalonia.Native.Interop;
using Avalonia.Platform;
using Avalonia.VisualTree;
+using MicroCom.Runtime;
namespace Avalonia.Native
{
diff --git a/src/Avalonia.Native/ScreenImpl.cs b/src/Avalonia.Native/ScreenImpl.cs
index 83db2e8a28..53bd12cde1 100644
--- a/src/Avalonia.Native/ScreenImpl.cs
+++ b/src/Avalonia.Native/ScreenImpl.cs
@@ -30,10 +30,10 @@ namespace Avalonia.Native
var screen = _native.GetScreen(i);
result[i] = new Screen(
- screen.PixelDensity,
+ screen.Scaling,
screen.Bounds.ToAvaloniaPixelRect(),
screen.WorkingArea.ToAvaloniaPixelRect(),
- screen.Primary.FromComBool());
+ screen.IsPrimary.FromComBool());
}
return result;
diff --git a/src/Avalonia.Native/WindowImplBase.cs b/src/Avalonia.Native/WindowImplBase.cs
index 34de439c94..381741cea9 100644
--- a/src/Avalonia.Native/WindowImplBase.cs
+++ b/src/Avalonia.Native/WindowImplBase.cs
@@ -92,7 +92,7 @@ namespace Avalonia.Native
_savedScaling = RenderScaling;
_nativeControlHost = new NativeControlHostImpl(_native.CreateNativeControlHost());
- var monitor = Screen.AllScreens.OrderBy(x => x.PixelDensity)
+ 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);
diff --git a/src/Avalonia.Native/avn.idl b/src/Avalonia.Native/avn.idl
index a98d213887..b0bff07146 100644
--- a/src/Avalonia.Native/avn.idl
+++ b/src/Avalonia.Native/avn.idl
@@ -256,8 +256,8 @@ struct AvnScreen
{
AvnRect Bounds;
AvnRect WorkingArea;
- float PixelDensity;
- bool Primary;
+ float Scaling;
+ bool IsPrimary;
}
enum AvnPixelFormat
diff --git a/src/Avalonia.Remote.Protocol/MetsysBson.cs b/src/Avalonia.Remote.Protocol/MetsysBson.cs
index 524ec09ca9..6abece6bf3 100644
--- a/src/Avalonia.Remote.Protocol/MetsysBson.cs
+++ b/src/Avalonia.Remote.Protocol/MetsysBson.cs
@@ -1364,13 +1364,13 @@ namespace Metsys.Bson
var optionsString = ReadName();
var options = RegexOptions.None;
- if (optionsString.Contains("e")) options = options | RegexOptions.ECMAScript;
- if (optionsString.Contains("i")) options = options | RegexOptions.IgnoreCase;
- if (optionsString.Contains("l")) options = options | RegexOptions.CultureInvariant;
- if (optionsString.Contains("m")) options = options | RegexOptions.Multiline;
- if (optionsString.Contains("s")) options = options | RegexOptions.Singleline;
- if (optionsString.Contains("w")) options = options | RegexOptions.IgnorePatternWhitespace;
- if (optionsString.Contains("x")) options = options | RegexOptions.ExplicitCapture;
+ if (optionsString.Contains('e')) options = options | RegexOptions.ECMAScript;
+ if (optionsString.Contains('i')) options = options | RegexOptions.IgnoreCase;
+ if (optionsString.Contains('l')) options = options | RegexOptions.CultureInvariant;
+ if (optionsString.Contains('m')) options = options | RegexOptions.Multiline;
+ if (optionsString.Contains('s')) options = options | RegexOptions.Singleline;
+ if (optionsString.Contains('w')) options = options | RegexOptions.IgnorePatternWhitespace;
+ if (optionsString.Contains('x')) options = options | RegexOptions.ExplicitCapture;
return new Regex(pattern, options);
}
diff --git a/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml b/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml
index 88b9f18eee..7e3c8673f5 100644
--- a/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml
+++ b/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml
@@ -281,7 +281,6 @@
-
@@ -305,7 +304,39 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -358,7 +389,6 @@
4
2
0
- Normal
@@ -375,8 +405,6 @@
-
-
@@ -424,7 +452,6 @@
-
@@ -455,8 +482,6 @@
-
-
@@ -595,7 +620,6 @@
-
diff --git a/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml b/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml
index b51a0bf259..7917315e19 100644
--- a/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml
+++ b/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml
@@ -278,7 +278,7 @@
-
+
@@ -301,7 +301,39 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -354,7 +386,6 @@
4
2
0
- Normal
@@ -371,8 +402,6 @@
-
-
@@ -420,7 +449,6 @@
-
@@ -450,8 +478,6 @@
-
-
@@ -589,7 +615,6 @@
-
diff --git a/src/Avalonia.Themes.Fluent/Assets/Inter-Bold.ttf b/src/Avalonia.Themes.Fluent/Assets/Inter-Bold.ttf
index 847ffd191b..a372c5fcca 100644
Binary files a/src/Avalonia.Themes.Fluent/Assets/Inter-Bold.ttf and b/src/Avalonia.Themes.Fluent/Assets/Inter-Bold.ttf differ
diff --git a/src/Avalonia.Themes.Fluent/Assets/Inter-ExtraLight.ttf b/src/Avalonia.Themes.Fluent/Assets/Inter-ExtraLight.ttf
index 33267cd799..13ef261fff 100644
Binary files a/src/Avalonia.Themes.Fluent/Assets/Inter-ExtraLight.ttf and b/src/Avalonia.Themes.Fluent/Assets/Inter-ExtraLight.ttf differ
diff --git a/src/Avalonia.Themes.Fluent/Assets/Inter-Light.ttf b/src/Avalonia.Themes.Fluent/Assets/Inter-Light.ttf
index c22eafe9fb..9f75ff2780 100644
Binary files a/src/Avalonia.Themes.Fluent/Assets/Inter-Light.ttf and b/src/Avalonia.Themes.Fluent/Assets/Inter-Light.ttf differ
diff --git a/src/Avalonia.Themes.Fluent/Assets/Inter-Medium.ttf b/src/Avalonia.Themes.Fluent/Assets/Inter-Medium.ttf
index f782894eea..2c5e25453c 100644
Binary files a/src/Avalonia.Themes.Fluent/Assets/Inter-Medium.ttf and b/src/Avalonia.Themes.Fluent/Assets/Inter-Medium.ttf differ
diff --git a/src/Avalonia.Themes.Fluent/Assets/Inter-Regular.ttf b/src/Avalonia.Themes.Fluent/Assets/Inter-Regular.ttf
index 3b7e686e54..c28fe4d744 100644
Binary files a/src/Avalonia.Themes.Fluent/Assets/Inter-Regular.ttf and b/src/Avalonia.Themes.Fluent/Assets/Inter-Regular.ttf differ
diff --git a/src/Avalonia.Themes.Fluent/Assets/Inter-SemiBold.ttf b/src/Avalonia.Themes.Fluent/Assets/Inter-SemiBold.ttf
index 556e972f48..d085eb4994 100644
Binary files a/src/Avalonia.Themes.Fluent/Assets/Inter-SemiBold.ttf and b/src/Avalonia.Themes.Fluent/Assets/Inter-SemiBold.ttf differ
diff --git a/src/Avalonia.Themes.Fluent/Assets/Inter-Thin.ttf b/src/Avalonia.Themes.Fluent/Assets/Inter-Thin.ttf
index e49058e33d..016a13482b 100644
Binary files a/src/Avalonia.Themes.Fluent/Assets/Inter-Thin.ttf and b/src/Avalonia.Themes.Fluent/Assets/Inter-Thin.ttf differ
diff --git a/src/Avalonia.Themes.Fluent/Controls/Calendar.xaml b/src/Avalonia.Themes.Fluent/Controls/Calendar.xaml
index 7042f51c71..9c66ea9b84 100644
--- a/src/Avalonia.Themes.Fluent/Controls/Calendar.xaml
+++ b/src/Avalonia.Themes.Fluent/Controls/Calendar.xaml
@@ -1,7 +1,7 @@
diff --git a/src/Avalonia.Themes.Fluent/Controls/CalendarButton.xaml b/src/Avalonia.Themes.Fluent/Controls/CalendarButton.xaml
index d1aee7ee9a..06b6cf30c2 100644
--- a/src/Avalonia.Themes.Fluent/Controls/CalendarButton.xaml
+++ b/src/Avalonia.Themes.Fluent/Controls/CalendarButton.xaml
@@ -1,7 +1,7 @@
-
-
-
diff --git a/src/Avalonia.Themes.Fluent/Controls/DatePicker.xaml b/src/Avalonia.Themes.Fluent/Controls/DatePicker.xaml
index 0cfd491b21..1585dada90 100644
--- a/src/Avalonia.Themes.Fluent/Controls/DatePicker.xaml
+++ b/src/Avalonia.Themes.Fluent/Controls/DatePicker.xaml
@@ -1,7 +1,7 @@
@@ -28,7 +28,6 @@
- 0,0,0,4
40
40
41
@@ -84,18 +83,8 @@
-
-
-
+
-
-
-
diff --git a/src/Avalonia.X11/X11Atoms.cs b/src/Avalonia.X11/X11Atoms.cs
index 424db94e0a..b00879bd1d 100644
--- a/src/Avalonia.X11/X11Atoms.cs
+++ b/src/Avalonia.X11/X11Atoms.cs
@@ -17,7 +17,7 @@
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
-// Copyright (c) 2006 Novell, Inc. (http://www.novell.com)
+// Copyright (c) 2006 Novell, Inc. (https://www.novell.com)
//
//
diff --git a/src/Avalonia.X11/X11Screens.cs b/src/Avalonia.X11/X11Screens.cs
index bcaafb6a53..ba6029b350 100644
--- a/src/Avalonia.X11/X11Screens.cs
+++ b/src/Avalonia.X11/X11Screens.cs
@@ -9,7 +9,7 @@ using JetBrains.Annotations;
namespace Avalonia.X11
{
- class X11Screens : IScreenImpl
+ class X11Screens : IScreenImpl
{
private IX11Screens _impl;
@@ -218,7 +218,7 @@ namespace Avalonia.X11
public int ScreenCount => _impl.Screens.Length;
public IReadOnlyList AllScreens =>
- _impl.Screens.Select(s => new Screen(s.PixelDensity, s.Bounds, s.WorkingArea, s.Primary)).ToArray();
+ _impl.Screens.Select(s => new Screen(s.Scaling, s.Bounds, s.WorkingArea, s.IsPrimary)).ToArray();
}
interface IX11Screens
@@ -281,30 +281,34 @@ namespace Avalonia.X11
{
private const int FullHDWidth = 1920;
private const int FullHDHeight = 1080;
- public bool Primary { get; }
+ public bool IsPrimary { get; }
public string Name { get; set; }
public PixelRect Bounds { get; set; }
public Size? PhysicalSize { get; set; }
- public double PixelDensity { get; set; }
+ public double Scaling { get; set; }
public PixelRect WorkingArea { get; set; }
- public X11Screen(PixelRect bounds, bool primary,
- string name, Size? physicalSize, double? pixelDensity)
+ public X11Screen(
+ PixelRect bounds,
+ bool isPrimary,
+ string name,
+ Size? physicalSize,
+ double? scaling)
{
- Primary = primary;
+ IsPrimary = isPrimary;
Name = name;
Bounds = bounds;
- if (physicalSize == null && pixelDensity == null)
+ if (physicalSize == null && scaling == null)
{
- PixelDensity = 1;
+ Scaling = 1;
}
- else if (pixelDensity == null)
+ else if (scaling == null)
{
- PixelDensity = GuessPixelDensity(bounds, physicalSize.Value);
+ Scaling = GuessPixelDensity(bounds, physicalSize.Value);
}
else
{
- PixelDensity = pixelDensity.Value;
+ Scaling = scaling.Value;
PhysicalSize = physicalSize;
}
}
diff --git a/src/Avalonia.X11/X11Structs.cs b/src/Avalonia.X11/X11Structs.cs
index 23abd31b2c..3f0a6aeb67 100644
--- a/src/Avalonia.X11/X11Structs.cs
+++ b/src/Avalonia.X11/X11Structs.cs
@@ -661,7 +661,7 @@ namespace Avalonia.X11 {
Type type = ev.GetType ();
FieldInfo [] fields = type.GetFields (System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Instance);
for (int i = 0; i < fields.Length; i++) {
- if (result != string.Empty) {
+ if (!string.IsNullOrEmpty(result)) {
result += ", ";
}
object value = fields [i].GetValue (ev);
diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs
index f24c33cafa..690ac0ebce 100644
--- a/src/Avalonia.X11/X11Window.cs
+++ b/src/Avalonia.X11/X11Window.cs
@@ -120,7 +120,7 @@ namespace Avalonia.X11
if (!_popup && Screen != null)
{
- var monitor = Screen.AllScreens.OrderBy(x => x.PixelDensity)
+ var monitor = Screen.AllScreens.OrderBy(x => x.Scaling)
.FirstOrDefault(m => m.Bounds.Contains(Position));
if (monitor != null)
@@ -570,9 +570,9 @@ namespace Avalonia.X11
newScaling = _scalingOverride.Value;
else
{
- var monitor = _platform.X11Screens.Screens.OrderBy(x => x.PixelDensity)
+ var monitor = _platform.X11Screens.Screens.OrderBy(x => x.Scaling)
.FirstOrDefault(m => m.Bounds.Contains(Position));
- newScaling = monitor?.PixelDensity ?? RenderScaling;
+ newScaling = monitor?.Scaling ?? RenderScaling;
}
if (RenderScaling != newScaling)
@@ -994,7 +994,7 @@ namespace Avalonia.X11
public IScreenImpl Screen => _platform.Screens;
- public Size MaxAutoSizeHint => _platform.X11Screens.Screens.Select(s => s.Bounds.Size.ToSize(s.PixelDensity))
+ public Size MaxAutoSizeHint => _platform.X11Screens.Screens.Select(s => s.Bounds.Size.ToSize(s.Scaling))
.OrderByDescending(x => x.Width + x.Height).FirstOrDefault();
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 869602e452..55a1014188 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -2,6 +2,7 @@
+
Shared\_ModuleInitializer.cs
diff --git a/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs b/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs
index 9bc9d8c7e6..a4d7d8b1eb 100644
--- a/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs
+++ b/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs
@@ -108,7 +108,7 @@ namespace Avalonia.LinuxFramebuffer.Output
// prepare for the new ioctl call
var handles = new uint[] {handle, 0, 0, 0};
var pitches = new uint[] {stride, 0, 0, 0};
- var offsets = new uint[] {};
+ var offsets = Array.Empty();
var ret = drmModeAddFB2(_card.Fd, w, h, format, handles, pitches,
offsets, out var fbHandle, 0);
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj b/src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj
index ce931b9c14..1dc7ce5e99 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj
@@ -6,6 +6,10 @@
Avalonia.Markup.Xaml.Loader
$(DefineConstants);XAMLX_INTERNAL
+
+
+ false
+
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs
index f325e6e2d6..4ece433530 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Linq;
+
using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
using XamlX;
using XamlX.Ast;
@@ -51,6 +52,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
new AvaloniaXamlIlControlTemplateTargetTypeMetadataTransformer(),
new AvaloniaXamlIlBindingPathParser(),
new AvaloniaXamlIlPropertyPathTransformer(),
+ new AvaloniaXamlIlSetterTargetTypeMetadataTransformer(),
new AvaloniaXamlIlSetterTransformer(),
new AvaloniaXamlIlConstructorServiceProviderTransformer(),
new AvaloniaXamlIlTransitionsTypeMetadataTransformer(),
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTargetTypeMetadataTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTargetTypeMetadataTransformer.cs
new file mode 100644
index 0000000000..ebc6c01ba8
--- /dev/null
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTargetTypeMetadataTransformer.cs
@@ -0,0 +1,34 @@
+using System.Linq;
+using XamlX;
+using XamlX.Ast;
+using XamlX.Transform;
+using XamlX.Transform.Transformers;
+
+namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
+
+internal class AvaloniaXamlIlSetterTargetTypeMetadataTransformer : IXamlAstTransformer
+{
+ public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
+ {
+ if (node is XamlAstObjectNode on
+ && on.Children.FirstOrDefault(c => c is XamlAstXmlDirective
+ {
+ Namespace: XamlNamespaces.Xaml2006,
+ Name: "SetterTargetType"
+ }) is { } typeDirective)
+ {
+ var value = ((XamlAstXmlDirective)typeDirective).Values.Single();
+ var type = value is XamlTypeExtensionNode typeNode ? typeNode.Value
+ : value is XamlAstTextNode tn ? TypeReferenceResolver.ResolveType(context, tn.Text, false, tn, true)
+ : null;
+ on.Children.Remove(typeDirective);
+
+ if (type is null)
+ {
+ throw new XamlParseException("Unable to resolve SetterTargetType type", typeDirective);
+ }
+ return new AvaloniaXamlIlTargetTypeMetadataNode(on, type, AvaloniaXamlIlTargetTypeMetadataNode.ScopeTypes.Style);
+ }
+ return node;
+ }
+}
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs
index ceaec972f6..5a6fd8246d 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Linq;
+using XamlX;
using XamlX.Ast;
using XamlX.Emit;
using XamlX.IL;
@@ -8,7 +9,6 @@ using XamlX.TypeSystem;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
{
- using XamlParseException = XamlX.XamlParseException;
class AvaloniaXamlIlSetterTransformer : IXamlAstTransformer
{
public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
@@ -17,10 +17,24 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
&& on.Type.GetClrType().FullName == "Avalonia.Styling.Setter"))
return node;
- var targetTypeNode = context.ParentNodes()
+ IXamlType targetType = null;
+ IXamlLineInfo lineInfo = null;
+
+ var styleParent = context.ParentNodes()
.OfType()
- .FirstOrDefault(x => x.ScopeType == AvaloniaXamlIlTargetTypeMetadataNode.ScopeTypes.Style) ??
- throw new XamlParseException("Can not find parent Style Selector or ControlTemplate TargetType", node);
+ .FirstOrDefault(x => x.ScopeType == AvaloniaXamlIlTargetTypeMetadataNode.ScopeTypes.Style);
+
+ if (styleParent != null)
+ {
+ targetType = styleParent.TargetType.GetClrType()
+ ?? throw new XamlParseException("Can not find parent Style Selector or ControlTemplate TargetType. If setter is not part of the style, you can set x:SetterTargetType directive on its parent.", node);
+ lineInfo = on;
+ }
+
+ if (targetType == null)
+ {
+ throw new XamlParseException("Could not determine target type of Setter", node);
+ }
IXamlType propType = null;
var property = @on.Children.OfType()
@@ -31,9 +45,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
if (propertyName == null)
throw new XamlParseException("Setter.Property must be a string", node);
-
var avaloniaPropertyNode = XamlIlAvaloniaPropertyHelper.CreateNode(context, propertyName,
- new XamlAstClrTypeReference(targetTypeNode, targetTypeNode.TargetType.GetClrType(), false), property.Values[0]);
+ new XamlAstClrTypeReference(lineInfo, targetType, false), property.Values[0]);
property.Values = new List {avaloniaPropertyNode};
propType = avaloniaPropertyNode.AvaloniaPropertyType;
}
diff --git a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
index d0c39f0289..75746273c2 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
+++ b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
@@ -9,6 +9,7 @@
CS1591
+
@@ -68,4 +69,8 @@
+
+
+
+
diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/TimeSpanTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/TimeSpanTypeConverter.cs
index 1e42a46875..1b6fbcef5c 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Converters/TimeSpanTypeConverter.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/Converters/TimeSpanTypeConverter.cs
@@ -15,7 +15,7 @@ namespace Avalonia.Markup.Xaml.Converters
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
var valueStr = (string)value;
- if (!valueStr.Contains(":"))
+ if (!valueStr.Contains(':'))
{
// shorthand seconds format (ie. "0.25")
var secs = double.Parse(valueStr, CultureInfo.InvariantCulture);
@@ -25,4 +25,4 @@ namespace Avalonia.Markup.Xaml.Converters
return base.ConvertFrom(context, culture, value);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs
index d462a2210e..f28f7bc626 100644
--- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs
@@ -56,7 +56,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
// We need to implement compile-time merging of resource dictionaries and this
// hack can be removed.
if (previousWasControlTheme &&
- parent is ResourceDictionary hack &&
+ parent is IResourceProvider hack &&
hack.Owner?.GetType().FullName == "Avalonia.Diagnostics.Views.MainWindow" &&
hack.Owner.TryGetResource(ResourceKey, out value))
{
diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs
index 439bc15243..0a9fbcfacb 100644
--- a/src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs
+++ b/src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs
@@ -168,7 +168,7 @@ namespace Avalonia.Markup.Parsers
}
}
- private static State ParseAttachedProperty(ref CharacterReader r, List nodes)
+ private static State ParseAttachedProperty(scoped ref CharacterReader r, List nodes)
{
var (ns, owner) = ParseTypeName(ref r);
@@ -318,7 +318,7 @@ namespace Avalonia.Markup.Parsers
return State.AfterMember;
}
- private static TypeName ParseTypeName(ref CharacterReader r)
+ private static TypeName ParseTypeName(scoped ref CharacterReader r)
{
ReadOnlySpan ns, typeName;
ns = ReadOnlySpan.Empty;
diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs
index 16856e674d..4d6d16a3ce 100644
--- a/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs
+++ b/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs
@@ -376,28 +376,28 @@ namespace Avalonia.Markup.Parsers
if (r.Peek == 'o')
{
- var constArg = r.TakeUntil(')').ToString().Trim();
- if (constArg.Equals("odd", StringComparison.Ordinal))
+ var constArg = r.TakeUntil(')').Trim();
+ if (constArg.SequenceEqual("odd".AsSpan()))
{
step = 2;
offset = 1;
}
else
{
- throw new ExpressionParseException(r.Position, $"Expected nth-child(odd). Actual '{constArg}'.");
+ throw new ExpressionParseException(r.Position, $"Expected nth-child(odd). Actual '{constArg.ToString()}'.");
}
}
else if (r.Peek == 'e')
{
- var constArg = r.TakeUntil(')').ToString().Trim();
- if (constArg.Equals("even", StringComparison.Ordinal))
+ var constArg = r.TakeUntil(')').Trim();
+ if (constArg.SequenceEqual("even".AsSpan()))
{
step = 2;
offset = 0;
}
else
{
- throw new ExpressionParseException(r.Position, $"Expected nth-child(even). Actual '{constArg}'.");
+ throw new ExpressionParseException(r.Position, $"Expected nth-child(even). Actual '{constArg.ToString()}'.");
}
}
else
@@ -405,7 +405,7 @@ namespace Avalonia.Markup.Parsers
r.SkipWhitespace();
var stepOrOffset = 0;
- var stepOrOffsetStr = r.TakeWhile(c => char.IsDigit(c) || c == '-' || c == '+').ToString();
+ var stepOrOffsetStr = r.TakeWhile(c => char.IsDigit(c) || c == '-' || c == '+');
if (stepOrOffsetStr.Length == 0
|| (stepOrOffsetStr.Length == 1
&& stepOrOffsetStr[0] == '+'))
@@ -417,7 +417,7 @@ namespace Avalonia.Markup.Parsers
{
stepOrOffset = -1;
}
- else if (!int.TryParse(stepOrOffsetStr.ToString(), out stepOrOffset))
+ else if (!stepOrOffsetStr.TryParseInt(out stepOrOffset))
{
throw new ExpressionParseException(r.Position, "Couldn't parse nth-child step or offset value. Integer was expected.");
}
@@ -462,7 +462,7 @@ namespace Avalonia.Markup.Parsers
r.SkipWhitespace();
if (sign != 0
- && !int.TryParse(r.TakeUntil(')').ToString(), out offset))
+ && !r.TakeUntil(')').TryParseInt(out offset))
{
throw new ExpressionParseException(r.Position, "Couldn't parse nth-child offset value. Integer was expected.");
}
diff --git a/src/Shared/StringCompatibilityExtensions.cs b/src/Shared/StringCompatibilityExtensions.cs
new file mode 100644
index 0000000000..7c894dba97
--- /dev/null
+++ b/src/Shared/StringCompatibilityExtensions.cs
@@ -0,0 +1,12 @@
+using System.Runtime.CompilerServices;
+
+namespace System;
+
+#if !NET6_0_OR_GREATER
+internal static class StringCompatibilityExtensions
+{
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool Contains(this string str, char search) =>
+ str.Contains(search.ToString());
+}
+#endif
diff --git a/src/Skia/Avalonia.Skia/FontManagerImpl.cs b/src/Skia/Avalonia.Skia/FontManagerImpl.cs
index 125dd0e455..90ff9652d8 100644
--- a/src/Skia/Avalonia.Skia/FontManagerImpl.cs
+++ b/src/Skia/Avalonia.Skia/FontManagerImpl.cs
@@ -102,7 +102,7 @@ namespace Avalonia.Skia
return false;
}
- public IGlyphTypefaceImpl CreateGlyphTypeface(Typeface typeface)
+ public IGlyphTypeface CreateGlyphTypeface(Typeface typeface)
{
SKTypeface skTypeface = null;
@@ -148,11 +148,19 @@ namespace Avalonia.Skia
$"Could not create glyph typeface for: {typeface.FontFamily.Name}.");
}
- var isFakeBold = (int)typeface.Weight >= 600 && !skTypeface.IsBold;
+ var fontSimulations = FontSimulations.None;
- var isFakeItalic = typeface.Style == FontStyle.Italic && !skTypeface.IsItalic;
-
- return new GlyphTypefaceImpl(skTypeface, isFakeBold, isFakeItalic);
+ if((int)typeface.Weight >= 600 && !skTypeface.IsBold)
+ {
+ fontSimulations |= FontSimulations.Bold;
+ }
+
+ if(typeface.Style == FontStyle.Italic && !skTypeface.IsItalic)
+ {
+ fontSimulations |= FontSimulations.Oblique;
+ }
+
+ return new GlyphTypefaceImpl(skTypeface, fontSimulations);
}
}
}
diff --git a/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs b/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs
index dcb4eac7ca..d11f4aa7d3 100644
--- a/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs
+++ b/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs
@@ -1,18 +1,18 @@
using System;
using System.Runtime.InteropServices;
+using Avalonia.Media;
using Avalonia.Metadata;
-using Avalonia.Platform;
using HarfBuzzSharp;
using SkiaSharp;
namespace Avalonia.Skia
{
[Unstable]
- public class GlyphTypefaceImpl : IGlyphTypefaceImpl
+ public class GlyphTypefaceImpl : IGlyphTypeface
{
private bool _isDisposed;
- public GlyphTypefaceImpl(SKTypeface typeface, bool isFakeBold = false, bool isFakeItalic = false)
+ public GlyphTypefaceImpl(SKTypeface typeface, FontSimulations fontSimulations)
{
Typeface = typeface ?? throw new ArgumentNullException(nameof(typeface));
@@ -25,39 +25,34 @@ namespace Avalonia.Skia
Font.SetFunctionsOpenType();
- DesignEmHeight = (short)Typeface.UnitsPerEm;
-
var metrics = Typeface.ToFont().Metrics;
const double defaultFontRenderingEmSize = 12.0;
- Ascent = (int)(metrics.Ascent / defaultFontRenderingEmSize * Typeface.UnitsPerEm);
-
- Descent = (int)(metrics.Descent / defaultFontRenderingEmSize * Typeface.UnitsPerEm);
-
- LineGap = (int)(metrics.Leading / defaultFontRenderingEmSize * Typeface.UnitsPerEm);
-
- UnderlinePosition = metrics.UnderlinePosition != null ?
+ Metrics = new FontMetrics
+ {
+ DesignEmHeight = (short)Typeface.UnitsPerEm,
+ Ascent = (int)(metrics.Ascent / defaultFontRenderingEmSize * Typeface.UnitsPerEm),
+ Descent = (int)(metrics.Descent / defaultFontRenderingEmSize * Typeface.UnitsPerEm),
+ LineGap = (int)(metrics.Leading / defaultFontRenderingEmSize * Typeface.UnitsPerEm),
+ UnderlinePosition = metrics.UnderlinePosition != null ?
(int)(metrics.UnderlinePosition / defaultFontRenderingEmSize * Typeface.UnitsPerEm) :
- 0;
-
- UnderlineThickness = metrics.UnderlineThickness != null ?
+ 0,
+ UnderlineThickness = metrics.UnderlineThickness != null ?
(int)(metrics.UnderlineThickness / defaultFontRenderingEmSize * Typeface.UnitsPerEm) :
- 0;
-
- StrikethroughPosition = metrics.StrikeoutPosition != null ?
+ 0,
+ StrikethroughPosition = metrics.StrikeoutPosition != null ?
(int)(metrics.StrikeoutPosition / defaultFontRenderingEmSize * Typeface.UnitsPerEm) :
- 0;
-
- StrikethroughThickness = metrics.StrikeoutThickness != null ?
+ 0,
+ StrikethroughThickness = metrics.StrikeoutThickness != null ?
(int)(metrics.StrikeoutThickness / defaultFontRenderingEmSize * Typeface.UnitsPerEm) :
- 0;
-
- IsFixedPitch = Typeface.IsFixedPitch;
+ 0,
+ IsFixedPitch = Typeface.IsFixedPitch
+ };
- IsFakeBold = isFakeBold;
+ GlyphCount = Typeface.GlyphCount;
- IsFakeItalic = isFakeItalic;
+ FontSimulations = fontSimulations;
}
public Face Face { get; }
@@ -66,40 +61,39 @@ namespace Avalonia.Skia
public SKTypeface Typeface { get; }
- public int ReplacementCodepoint { get; }
-
- ///
- public short DesignEmHeight { get; }
-
- ///
- public int Ascent { get; }
+ public FontSimulations FontSimulations { get; }
- ///
- public int Descent { get; }
+ public int ReplacementCodepoint { get; }
- ///
- public int LineGap { get; }
+ public FontMetrics Metrics { get; }
- ///
- public int UnderlinePosition { get; }
+ public int GlyphCount { get; }
- ///
- public int UnderlineThickness { get; }
+ public bool IsFakeBold { get; }
- ///
- public int StrikethroughPosition { get; }
+ public bool IsFakeItalic { get; }
- ///
- public int StrikethroughThickness { get; }
+ public bool TryGetGlyphMetrics(ushort glyph, out GlyphMetrics metrics)
+ {
+ metrics = default;
- ///
- public bool IsFixedPitch { get; }
-
- public bool IsFakeBold { get; }
-
- public bool IsFakeItalic { get; }
+ if (!Font.TryGetGlyphExtents(glyph, out var extents))
+ {
+ return false;
+ }
+
+ metrics = new GlyphMetrics
+ {
+ XBearing = extents.XBearing,
+ YBearing = extents.YBearing,
+ Width = extents.Width,
+ Height = extents.Height
+ };
+
+ return true;
+ }
- ///
+ ///
public ushort GetGlyph(uint codepoint)
{
if (Font.TryGetGlyph(codepoint, out var glyph))
@@ -110,7 +104,14 @@ namespace Avalonia.Skia
return 0;
}
- ///
+ public bool TryGetGlyph(uint codepoint, out ushort glyph)
+ {
+ glyph = GetGlyph(codepoint);
+
+ return glyph != 0;
+ }
+
+ ///
public ushort[] GetGlyphs(ReadOnlySpan codepoints)
{
var glyphs = new ushort[codepoints.Length];
@@ -126,13 +127,13 @@ namespace Avalonia.Skia
return glyphs;
}
- ///
+ ///
public int GetGlyphAdvance(ushort glyph)
{
return Font.GetHorizontalGlyphAdvance(glyph);
}
- ///
+ ///
public int[] GetGlyphAdvances(ReadOnlySpan glyphs)
{
var glyphIndices = new uint[glyphs.Length];
@@ -180,5 +181,10 @@ namespace Avalonia.Skia
Dispose(true);
GC.SuppressFinalize(this);
}
+
+ public bool TryGetTable(uint tag, out byte[] table)
+ {
+ return Typeface.TryGetTableData(tag, out table);
+ }
}
}
diff --git a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs
index 91fe4fc085..a9696efbd4 100644
--- a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs
+++ b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs
@@ -1,7 +1,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
-using System.IO;
+using System.IO;
using System.Linq;
using System.Threading;
@@ -12,6 +12,8 @@ using Avalonia.OpenGL.Imaging;
using Avalonia.Platform;
using Avalonia.Media.Imaging;
using SkiaSharp;
+using System.Runtime.InteropServices;
+using System.Drawing;
namespace Avalonia.Skia
{
@@ -33,13 +35,17 @@ namespace Avalonia.Skia
}
var gl = AvaloniaLocator.Current.GetService();
- if (gl != null)
+ if (gl != null)
_skiaGpu = new GlSkiaGpu(gl, maxResourceBytes);
-
- //TODO: SKFont crashes when disposed in finalizer so we keep it alive
- GC.SuppressFinalize(s_font);
}
+
+ public bool SupportsIndividualRoundRects => true;
+
+ public AlphaFormat DefaultAlphaFormat => AlphaFormat.Premul;
+
+ public PixelFormat DefaultPixelFormat { get; }
+
public IGeometryImpl CreateEllipseGeometry(Rect rect) => new EllipseGeometryImpl(rect);
public IGeometryImpl CreateLineGeometry(Point p1, Point p2) => new LineGeometryImpl(p1, p2);
@@ -64,7 +70,7 @@ namespace Avalonia.Skia
public IGeometryImpl BuildGlyphRunGeometry(GlyphRun glyphRun)
{
- if (glyphRun.GlyphTypeface.PlatformImpl is not GlyphTypefaceImpl glyphTypeface)
+ if (glyphRun.GlyphTypeface is not GlyphTypefaceImpl glyphTypeface)
{
throw new InvalidOperationException("PlatformImpl can't be null.");
}
@@ -228,133 +234,95 @@ namespace Avalonia.Skia
return new WriteableBitmapImpl(size, dpi, format, alphaFormat);
}
- private static readonly SKFont s_font = new SKFont
- {
- Subpixel = true,
- Edging = SKFontEdging.SubpixelAntialias,
- Hinting = SKFontHinting.Full,
- LinearMetrics = true
- };
-
- private static readonly ThreadLocal s_textBlobBuilderThreadLocal = new ThreadLocal(() => new SKTextBlobBuilder());
-
- ///
- public IGlyphRunImpl CreateGlyphRun(GlyphRun glyphRun)
+ public IOpenGlBitmapImpl CreateOpenGlBitmap(PixelSize size, Vector dpi)
{
- var count = glyphRun.GlyphIndices.Count;
- var textBlobBuilder = s_textBlobBuilderThreadLocal.Value;
-
- var glyphTypeface = (GlyphTypefaceImpl)glyphRun.GlyphTypeface.PlatformImpl;
+ if (_skiaGpu is IOpenGlAwareSkiaGpu glAware)
+ return glAware.CreateOpenGlBitmap(size, dpi);
+ if (_skiaGpu == null)
+ throw new PlatformNotSupportedException("GPU acceleration is not available");
+ throw new PlatformNotSupportedException(
+ "Current GPU acceleration backend does not support OpenGL integration");
+ }
- var typeface = glyphTypeface.Typeface;
+ public IGlyphRunBuffer AllocateGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
+ => new SKGlyphRunBuffer(glyphTypeface, fontRenderingEmSize, length);
- s_font.Size = (float)glyphRun.FontRenderingEmSize;
- s_font.Typeface = typeface;
- s_font.Embolden = glyphTypeface.IsFakeBold;
- s_font.SkewX = glyphTypeface.IsFakeItalic ? -0.2f : 0;
+ public IHorizontalGlyphRunBuffer AllocateHorizontalGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
+ => new SKHorizontalGlyphRunBuffer(glyphTypeface, fontRenderingEmSize, length);
- SKTextBlob textBlob;
+ public IPositionedGlyphRunBuffer AllocatePositionedGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
+ => new SKPositionedGlyphRunBuffer(glyphTypeface, fontRenderingEmSize, length);
- var scale = (float)(glyphRun.FontRenderingEmSize / glyphTypeface.DesignEmHeight);
+ private abstract class SKGlyphRunBufferBase : IGlyphRunBuffer
+ {
+ protected readonly SKTextBlobBuilder _builder;
+ protected readonly SKFont _font;
- if (glyphRun.GlyphOffsets == null)
+ public SKGlyphRunBufferBase(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length)
{
- if (glyphTypeface.IsFixedPitch)
- {
- var buffer = textBlobBuilder.AllocateRun(s_font, glyphRun.GlyphIndices.Count, 0, 0);
-
- var glyphs = buffer.GetGlyphSpan();
+ _builder = new SKTextBlobBuilder();
- for (int i = 0; i < glyphs.Length; i++)
- {
- glyphs[i] = glyphRun.GlyphIndices[i];
- }
+ var glyphTypefaceImpl = (GlyphTypefaceImpl)glyphTypeface;
- textBlob = textBlobBuilder.Build();
- }
- else
+ _font = new SKFont
{
- var buffer = textBlobBuilder.AllocateHorizontalRun(s_font, count, 0);
-
- var positions = buffer.GetPositionSpan();
-
- var width = 0d;
-
- for (var i = 0; i < count; i++)
- {
- positions[i] = (float)width;
-
- if (glyphRun.GlyphAdvances == null)
- {
- width += glyphTypeface.GetGlyphAdvance(glyphRun.GlyphIndices[i]) * scale;
- }
- else
- {
- width += glyphRun.GlyphAdvances[i];
- }
- }
-
- var glyphs = buffer.GetGlyphSpan();
+ Subpixel = true,
+ Edging = SKFontEdging.SubpixelAntialias,
+ Hinting = SKFontHinting.Full,
+ LinearMetrics = true,
+ Size = fontRenderingEmSize,
+ Typeface = glyphTypefaceImpl.Typeface,
+ Embolden = glyphTypefaceImpl.IsFakeBold,
+ SkewX = glyphTypefaceImpl.IsFakeItalic ? -0.2f : 0
+ };
+ }
- for (int i = 0; i < glyphs.Length; i++)
- {
- glyphs[i] = glyphRun.GlyphIndices[i];
- }
+ public abstract Span GlyphIndices { get; }
- textBlob = textBlobBuilder.Build();
- }
- }
- else
+ public IGlyphRunImpl Build()
{
- var buffer = textBlobBuilder.AllocatePositionedRun(s_font, count);
-
- var glyphPositions = buffer.GetPositionSpan();
+ return new GlyphRunImpl(_builder.Build());
+ }
+ }
- var currentX = 0.0;
+ private sealed class SKGlyphRunBuffer : SKGlyphRunBufferBase
+ {
+ private readonly SKRunBuffer _buffer;
- for (var i = 0; i < count; i++)
- {
- var glyphOffset = glyphRun.GlyphOffsets[i];
-
- glyphPositions[i] = new SKPoint((float)(currentX + glyphOffset.X), (float)glyphOffset.Y);
-
- if (glyphRun.GlyphAdvances == null)
- {
- currentX += glyphTypeface.GetGlyphAdvance(glyphRun.GlyphIndices[i]) * scale;
- }
- else
- {
- currentX += glyphRun.GlyphAdvances[i];
- }
- }
+ public SKGlyphRunBuffer(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) : base(glyphTypeface, fontRenderingEmSize, length)
+ {
+ _buffer = _builder.AllocateRun(_font, length, 0, 0);
+ }
- var glyphs = buffer.GetGlyphSpan();
+ public override Span GlyphIndices => _buffer.GetGlyphSpan();
+ }
- for (int i = 0; i < glyphs.Length; i++)
- {
- glyphs[i] = glyphRun.GlyphIndices[i];
- }
+ private sealed class SKHorizontalGlyphRunBuffer : SKGlyphRunBufferBase, IHorizontalGlyphRunBuffer
+ {
+ private readonly SKHorizontalRunBuffer _buffer;
- textBlob = textBlobBuilder.Build();
+ public SKHorizontalGlyphRunBuffer(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) : base(glyphTypeface, fontRenderingEmSize, length)
+ {
+ _buffer = _builder.AllocateHorizontalRun(_font, length, 0);
}
- return new GlyphRunImpl(textBlob);
+ public override Span GlyphIndices => _buffer.GetGlyphSpan();
+
+ public Span GlyphPositions => _buffer.GetPositionSpan();
}
- public IOpenGlBitmapImpl CreateOpenGlBitmap(PixelSize size, Vector dpi)
+ private sealed class SKPositionedGlyphRunBuffer : SKGlyphRunBufferBase, IPositionedGlyphRunBuffer
{
- if (_skiaGpu is IOpenGlAwareSkiaGpu glAware)
- return glAware.CreateOpenGlBitmap(size, dpi);
- if (_skiaGpu == null)
- throw new PlatformNotSupportedException("GPU acceleration is not available");
- throw new PlatformNotSupportedException(
- "Current GPU acceleration backend does not support OpenGL integration");
- }
+ private readonly SKPositionedRunBuffer _buffer;
- public bool SupportsIndividualRoundRects => true;
+ public SKPositionedGlyphRunBuffer(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length) : base(glyphTypeface, fontRenderingEmSize, length)
+ {
+ _buffer = _builder.AllocatePositionedRun(_font, length);
+ }
- public AlphaFormat DefaultAlphaFormat => AlphaFormat.Premul;
+ public override Span GlyphIndices => _buffer.GetGlyphSpan();
- public PixelFormat DefaultPixelFormat { get; }
+ public Span GlyphPositions => MemoryMarshal.Cast(_buffer.GetPositionSpan());
+ }
}
}
diff --git a/src/Skia/Avalonia.Skia/TextShaperImpl.cs b/src/Skia/Avalonia.Skia/TextShaperImpl.cs
index 6da8a20f6a..b07deb1f4d 100644
--- a/src/Skia/Avalonia.Skia/TextShaperImpl.cs
+++ b/src/Skia/Avalonia.Skia/TextShaperImpl.cs
@@ -31,7 +31,7 @@ namespace Avalonia.Skia
buffer.Language = new Language(culture ?? CultureInfo.CurrentCulture);
- var font = ((GlyphTypefaceImpl)typeface.PlatformImpl).Font;
+ var font = ((GlyphTypefaceImpl)typeface).Font;
font.Shape(buffer);
diff --git a/src/Web/Avalonia.Web.Blazor/Avalonia.Web.Blazor.CompilationTuning.props b/src/Web/Avalonia.Web.Blazor/Avalonia.Web.Blazor.CompilationTuning.props
deleted file mode 100644
index eb5e5dd733..0000000000
--- a/src/Web/Avalonia.Web.Blazor/Avalonia.Web.Blazor.CompilationTuning.props
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
- 16777216
- false
- false
-
-
diff --git a/src/Web/Avalonia.Web.Blazor/Avalonia.Web.Blazor.csproj b/src/Web/Avalonia.Web.Blazor/Avalonia.Web.Blazor.csproj
index 693a6a1462..1c31e0eb5d 100644
--- a/src/Web/Avalonia.Web.Blazor/Avalonia.Web.Blazor.csproj
+++ b/src/Web/Avalonia.Web.Blazor/Avalonia.Web.Blazor.csproj
@@ -1,53 +1,40 @@
- net6.0
- enable
+ net7.0
Avalonia.Web.Blazor
- preview
- false
- true
+ _IncludeGeneratedAvaloniaStaticFiles;$(ResolveStaticWebAssetsInputsDependsOn)
-
-
-
-
- true
- build\
-
-
- true
- build\;buildTransitive\
-
+
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
+
+
+ <_AvaloniaWebAssets Include="$(MSBuildThisFileDirectory)../Avalonia.Web/wwwroot/**/*.*" />
+
+
+
+
diff --git a/src/Web/Avalonia.Web.Blazor/Avalonia.Web.Blazor.props b/src/Web/Avalonia.Web.Blazor/Avalonia.Web.Blazor.props
deleted file mode 100644
index dd96a60c6a..0000000000
--- a/src/Web/Avalonia.Web.Blazor/Avalonia.Web.Blazor.props
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
diff --git a/src/Web/Avalonia.Web.Blazor/Avalonia.Web.Blazor.targets b/src/Web/Avalonia.Web.Blazor/Avalonia.Web.Blazor.targets
deleted file mode 100644
index e9052fda88..0000000000
--- a/src/Web/Avalonia.Web.Blazor/Avalonia.Web.Blazor.targets
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
diff --git a/src/Web/Avalonia.Web.Blazor/AvaloniaBlazorAppBuilder.cs b/src/Web/Avalonia.Web.Blazor/AvaloniaBlazorAppBuilder.cs
deleted file mode 100644
index 11d9bcc98f..0000000000
--- a/src/Web/Avalonia.Web.Blazor/AvaloniaBlazorAppBuilder.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using Avalonia.Controls;
-using Avalonia.Platform;
-
-namespace Avalonia.Web.Blazor
-{
- public class AvaloniaBlazorAppBuilder : AppBuilderBase
- {
- public AvaloniaBlazorAppBuilder(IRuntimePlatform platform, Action platformServices)
- : base(platform, platformServices)
- {
- }
-
- public AvaloniaBlazorAppBuilder()
- : base(new StandardRuntimePlatform(),
- builder => StandardRuntimePlatformServices.Register(builder.ApplicationType!.Assembly))
- {
- UseWindowingSubsystem(BlazorWindowingPlatform.Register);
- }
- }
-}
diff --git a/src/Web/Avalonia.Web.Blazor/AvaloniaView.cs b/src/Web/Avalonia.Web.Blazor/AvaloniaView.cs
new file mode 100644
index 0000000000..909e2dd441
--- /dev/null
+++ b/src/Web/Avalonia.Web.Blazor/AvaloniaView.cs
@@ -0,0 +1,44 @@
+using System.Runtime.InteropServices.JavaScript;
+using System.Runtime.Versioning;
+using System.Threading.Tasks;
+using System;
+using Avalonia.Controls.ApplicationLifetimes;
+using Microsoft.AspNetCore.Components;
+using Microsoft.AspNetCore.Components.Rendering;
+using BrowserView = Avalonia.Web.AvaloniaView;
+
+namespace Avalonia.Web.Blazor;
+
+[SupportedOSPlatform("browser")]
+public class AvaloniaView : ComponentBase
+{
+ private BrowserView? _browserView;
+ private readonly string _containerId;
+
+ public AvaloniaView()
+ {
+ _containerId = "av_container_" + Guid.NewGuid();
+ }
+
+ protected override void BuildRenderTree(RenderTreeBuilder builder)
+ {
+ builder.OpenElement(0, "div");
+ builder.AddAttribute(1, "id", _containerId);
+ builder.AddAttribute(2, "style", "width:100vw;height:100vh");
+ builder.CloseElement();
+ }
+
+ protected override async Task OnInitializedAsync()
+ {
+ if (OperatingSystem.IsBrowser())
+ {
+ await Avalonia.Web.Interop.AvaloniaModule.ImportMain();
+
+ _browserView = new BrowserView(_containerId);
+ if (Application.Current?.ApplicationLifetime is ISingleViewApplicationLifetime lifetime)
+ {
+ _browserView.Content = lifetime.MainView;
+ }
+ }
+ }
+}
diff --git a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor b/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor
deleted file mode 100644
index 5a3b9d5f71..0000000000
--- a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor
+++ /dev/null
@@ -1,67 +0,0 @@
-
-
-
diff --git a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs b/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs
deleted file mode 100644
index 0e64e98f1e..0000000000
--- a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs
+++ /dev/null
@@ -1,500 +0,0 @@
-using System.Diagnostics;
-using Avalonia.Controls.ApplicationLifetimes;
-using Avalonia.Controls.Embedding;
-using Avalonia.Controls.Platform;
-using Avalonia.Input;
-using Avalonia.Input.Raw;
-using Avalonia.Input.TextInput;
-using Avalonia.Platform.Storage;
-using Avalonia.Rendering;
-using Avalonia.Rendering.Composition;
-using Avalonia.Web.Blazor.Interop;
-using Avalonia.Web.Blazor.Interop.Storage;
-
-using Microsoft.AspNetCore.Components;
-using Microsoft.AspNetCore.Components.Forms;
-using Microsoft.AspNetCore.Components.Web;
-using Microsoft.JSInterop;
-
-using SkiaSharp;
-using HTMLPointerEventArgs = Microsoft.AspNetCore.Components.Web.PointerEventArgs;
-
-namespace Avalonia.Web.Blazor
-{
- public partial class AvaloniaView : ITextInputMethodImpl
- {
- private readonly RazorViewTopLevelImpl _topLevelImpl;
- private EmbeddableControlRoot _topLevel;
-
- // Interop
- private SKHtmlCanvasInterop? _interop = null;
- private SizeWatcherInterop? _sizeWatcher = null;
- private DpiWatcherInterop? _dpiWatcher = null;
- private SKHtmlCanvasInterop.GLInfo? _jsGlInfo = null;
- private AvaloniaModule? _avaloniaModule = null;
- private InputHelperInterop? _inputHelper = null;
- private FocusHelperInterop? _canvasHelper = null;
- private FocusHelperInterop? _containerHelper = null;
- private NativeControlHostInterop? _nativeControlHost = null;
- private StorageProviderInterop? _storageProvider = null;
- private ElementReference _htmlCanvas;
- private ElementReference _inputElement;
- private ElementReference _containerElement;
- private ElementReference _nativeControlsContainer;
- private double _dpi = 1;
- private SKSize _canvasSize = new (100, 100);
-
- private GRContext? _context;
- private GRGlInterface? _glInterface;
- private const SKColorType ColorType = SKColorType.Rgba8888;
-
- private bool _useGL;
- private bool _inputElementFocused;
-
- private ITextInputMethodClient? _client;
-
-
- [Inject] private IJSRuntime Js { get; set; } = null!;
-
- public AvaloniaView()
- {
- _topLevelImpl = new RazorViewTopLevelImpl(this);
-
- _topLevel = new EmbeddableControlRoot(_topLevelImpl);
-
- if (Application.Current?.ApplicationLifetime is ISingleViewApplicationLifetime lifetime)
- {
- _topLevel.Content = lifetime.MainView;
- }
- }
-
- public bool KeyPreventDefault { get; set; }
-
- public ITextInputMethodClient? Client => _client;
-
- public bool IsActive => _client != null;
-
- public bool IsComposing { get; private set; }
-
- internal INativeControlHostImpl GetNativeControlHostImpl()
- {
- return _nativeControlHost ?? throw new InvalidOperationException("Blazor View wasn't initialized yet");
- }
-
- internal IStorageProvider GetStorageProvider()
- {
- return _storageProvider ?? throw new InvalidOperationException("Blazor View wasn't initialized yet");
- }
-
- private void OnPointerCancel(HTMLPointerEventArgs e)
- {
- if (e.PointerType == "touch")
- {
- _topLevelImpl.RawPointerEvent(RawPointerEventType.TouchCancel, e.PointerType, GetPointFromEventArgs(e),
- GetModifiers(e), e.PointerId);
- }
- }
-
- private void OnPointerMove(HTMLPointerEventArgs e)
- {
- var type = e.PointerType switch
- {
- "touch" => RawPointerEventType.TouchUpdate,
- _ => RawPointerEventType.Move
- };
-
- _topLevelImpl.RawPointerEvent(type, e.PointerType, GetPointFromEventArgs(e), GetModifiers(e), e.PointerId);
- }
-
- private void OnPointerUp(HTMLPointerEventArgs e)
- {
- var type = e.PointerType switch
- {
- "touch" => RawPointerEventType.TouchEnd,
- _ => e.Button switch
- {
- 0 => RawPointerEventType.LeftButtonUp,
- 1 => RawPointerEventType.MiddleButtonUp,
- 2 => RawPointerEventType.RightButtonUp,
- 3 => RawPointerEventType.XButton1Up,
- 4 => RawPointerEventType.XButton2Up,
- // 5 => Pen eraser button,
- _ => RawPointerEventType.Move
- }
- };
-
- _topLevelImpl.RawPointerEvent(type, e.PointerType, GetPointFromEventArgs(e), GetModifiers(e), e.PointerId);
- }
-
- private void OnPointerDown(HTMLPointerEventArgs e)
- {
- var type = e.PointerType switch
- {
- "touch" => RawPointerEventType.TouchBegin,
- _ => e.Button switch
- {
- 0 => RawPointerEventType.LeftButtonDown,
- 1 => RawPointerEventType.MiddleButtonDown,
- 2 => RawPointerEventType.RightButtonDown,
- 3 => RawPointerEventType.XButton1Down,
- 4 => RawPointerEventType.XButton2Down,
- // 5 => Pen eraser button,
- _ => RawPointerEventType.Move
- }
- };
-
- _topLevelImpl.RawPointerEvent(type, e.PointerType, GetPointFromEventArgs(e), GetModifiers(e), e.PointerId);
- }
-
- private static RawPointerPoint GetPointFromEventArgs(HTMLPointerEventArgs args)
- {
- return new RawPointerPoint
- {
- Position = new Point(args.ClientX, args.ClientY),
- Pressure = args.Pressure,
- XTilt = args.TiltX,
- YTilt = args.TiltY
- // Twist = args.Twist - read from JS code directly when
- };
- }
-
- private void OnWheel(WheelEventArgs e)
- {
- _topLevelImpl.RawMouseWheelEvent( new Point(e.ClientX, e.ClientY),
- new Vector(-(e.DeltaX / 50), -(e.DeltaY / 50)), GetModifiers(e));
- }
-
- private static RawInputModifiers GetModifiers(MouseEventArgs e)
- {
- var modifiers = RawInputModifiers.None;
-
- if (e.CtrlKey)
- modifiers |= RawInputModifiers.Control;
- if (e.AltKey)
- modifiers |= RawInputModifiers.Alt;
- if (e.ShiftKey)
- modifiers |= RawInputModifiers.Shift;
- if (e.MetaKey)
- modifiers |= RawInputModifiers.Meta;
-
- if ((e.Buttons & 1L) == 1)
- modifiers |= RawInputModifiers.LeftMouseButton;
-
- if ((e.Buttons & 2L) == 2)
- modifiers |= e.Type == "pen" ? RawInputModifiers.PenBarrelButton : RawInputModifiers.RightMouseButton;
-
- if ((e.Buttons & 4L) == 4)
- modifiers |= RawInputModifiers.MiddleMouseButton;
-
- if ((e.Buttons & 8L) == 8)
- modifiers |= RawInputModifiers.XButton1MouseButton;
-
- if ((e.Buttons & 16L) == 16)
- modifiers |= RawInputModifiers.XButton2MouseButton;
-
- if ((e.Buttons & 32L) == 32)
- modifiers |= RawInputModifiers.PenEraser;
-
- return modifiers;
- }
-
- private static RawInputModifiers GetModifiers(KeyboardEventArgs e)
- {
- var modifiers = RawInputModifiers.None;
-
- if (e.CtrlKey)
- modifiers |= RawInputModifiers.Control;
- if (e.AltKey)
- modifiers |= RawInputModifiers.Alt;
- if (e.ShiftKey)
- modifiers |= RawInputModifiers.Shift;
- if (e.MetaKey)
- modifiers |= RawInputModifiers.Meta;
-
- return modifiers;
- }
-
- private void OnKeyDown(KeyboardEventArgs e)
- {
- KeyPreventDefault = _topLevelImpl.RawKeyboardEvent(RawKeyEventType.KeyDown, e.Code, e.Key, GetModifiers(e));
- }
-
- private void OnKeyUp(KeyboardEventArgs e)
- {
- KeyPreventDefault = _topLevelImpl.RawKeyboardEvent(RawKeyEventType.KeyUp, e.Code, e.Key, GetModifiers(e));
- }
-
- private void OnFocus(FocusEventArgs e)
- {
- // if focus has unexpectedly moved from the input element to the container element,
- // shift it back to the input element
- if (_inputElementFocused && _inputHelper is not null)
- {
- _inputHelper.Focus();
- }
- }
-
- [Parameter(CaptureUnmatchedValues = true)]
- public IReadOnlyDictionary? AdditionalAttributes { get; set; }
-
- protected override async Task OnAfterRenderAsync(bool firstRender)
- {
- if (firstRender)
- {
- AvaloniaLocator.CurrentMutable.Bind().ToConstant((IJSInProcessRuntime)Js);
-
- _avaloniaModule = await AvaloniaModule.ImportAsync(Js);
-
- _canvasHelper = new FocusHelperInterop(_avaloniaModule, _htmlCanvas);
- _containerHelper = new FocusHelperInterop(_avaloniaModule, _containerElement);
- _inputHelper = new InputHelperInterop(_avaloniaModule, _inputElement);
-
- _inputHelper.CompositionEvent += InputHelperOnCompositionEvent;
- _inputHelper.InputEvent += InputHelperOnInputEvent;
-
- HideIme();
- _canvasHelper.SetCursor("default");
- _topLevelImpl.SetCssCursor = x =>
- {
- _inputHelper.SetCursor(x); //macOS
- _canvasHelper.SetCursor(x); //windows
- };
-
- _nativeControlHost = new NativeControlHostInterop(_avaloniaModule, _nativeControlsContainer);
- _storageProvider = await StorageProviderInterop.ImportAsync(Js);
-
- Console.WriteLine("starting html canvas setup");
- _interop = new SKHtmlCanvasInterop(_avaloniaModule, _htmlCanvas, OnRenderFrame);
-
- Console.WriteLine("Interop created");
-
- var skiaOptions = AvaloniaLocator.Current.GetService();
- _useGL = skiaOptions?.CustomGpuFactory != null;
-
- if (_useGL)
- {
- _jsGlInfo = _interop.InitGL();
- Console.WriteLine("jsglinfo created - init gl");
- }
- else
- {
- var rasterInitialized = _interop.InitRaster();
- Console.WriteLine("raster initialized: {0}", rasterInitialized);
- }
-
- if (_useGL)
- {
- // create the SkiaSharp context
- if (_context == null)
- {
- _glInterface = GRGlInterface.Create();
- _context = GRContext.CreateGl(_glInterface);
-
-
- // bump the default resource cache limit
- _context.SetResourceCacheLimit(skiaOptions?.MaxGpuResourceSizeBytes ?? 32 * 1024 * 1024);
- Console.WriteLine("glcontext created and resource limit set");
- }
-
- _topLevelImpl.SetSurface(_context, _jsGlInfo!, ColorType,
- new PixelSize((int)_canvasSize.Width, (int)_canvasSize.Height), _dpi);
- }
- else
- {
- _topLevelImpl.SetSurface(ColorType,
- new PixelSize((int)_canvasSize.Width, (int)_canvasSize.Height), _dpi, _interop.PutImageData);
- }
-
- _interop.SetCanvasSize((int)(_canvasSize.Width * _dpi), (int)(_canvasSize.Height * _dpi));
-
- Threading.Dispatcher.UIThread.Post(async () =>
- {
- _interop.RequestAnimationFrame(true);
-
- _sizeWatcher = new SizeWatcherInterop(_avaloniaModule, _htmlCanvas, OnSizeChanged);
- _dpiWatcher = new DpiWatcherInterop(_avaloniaModule, OnDpiChanged);
-
- _sizeWatcher.Start();
- _topLevel.Prepare();
-
- _topLevel.Renderer.Start();
- });
- }
- }
-
- private void InputHelperOnInputEvent(object? sender, WebInputEventArgs e)
- {
- if (IsComposing)
- {
- return;
- }
-
- _topLevelImpl.RawTextEvent(e.Data);
-
- e.Handled = true;
- }
-
- private void InputHelperOnCompositionEvent(object? sender, WebCompositionEventArgs e)
- {
- if(_client == null)
- {
- return;
- }
-
- switch (e.Type)
- {
- case WebCompositionEventArgs.WebCompositionEventType.Start:
- _client.SetPreeditText(null);
- IsComposing = true;
- break;
- case WebCompositionEventArgs.WebCompositionEventType.Update:
- _client.SetPreeditText(e.Data);
- break;
- case WebCompositionEventArgs.WebCompositionEventType.End:
- IsComposing = false;
- _client.SetPreeditText(null);
- _topLevelImpl.RawTextEvent(e.Data);
- break;
- }
- }
-
- private void OnRenderFrame()
- {
- if (_useGL && (_jsGlInfo == null))
- {
- Console.WriteLine("nothing to render");
- return;
- }
- if (_canvasSize.Width <= 0 || _canvasSize.Height <= 0 || _dpi <= 0)
- {
- Console.WriteLine("nothing to render");
- return;
- }
-
- ManualTriggerRenderTimer.Instance.RaiseTick();
- }
-
- public void Dispose()
- {
- _dpiWatcher?.Unsubscribe(OnDpiChanged);
- _sizeWatcher?.Dispose();
- _interop?.Dispose();
- }
-
- private void ForceBlit()
- {
- // Note: this is technically a hack, but it's a kinda unique use case when
- // we want to blit the previous frame
- // renderer doesn't have much control over the render target
- // we render on the UI thread
- // We also don't want to have it as a meaningful public API.
- // Therefore we have InternalsVisibleTo hack here.
-
- if (_topLevel.Renderer is CompositingRenderer dr)
- {
- dr.CompositionTarget.ImmediateUIThreadRender();
- }
- }
-
- private void OnDpiChanged(double newDpi)
- {
- if (Math.Abs(_dpi - newDpi) > 0.0001)
- {
- _dpi = newDpi;
-
- _interop!.SetCanvasSize((int)(_canvasSize.Width * _dpi), (int)(_canvasSize.Height * _dpi));
-
- _topLevelImpl.SetClientSize(_canvasSize, _dpi);
-
- ForceBlit();
- }
- }
-
- private void OnSizeChanged(SKSize newSize)
- {
- if (_canvasSize != newSize)
- {
- _canvasSize = newSize;
-
- _interop!.SetCanvasSize((int)(_canvasSize.Width * _dpi), (int)(_canvasSize.Height * _dpi));
-
- _topLevelImpl.SetClientSize(_canvasSize, _dpi);
-
- ForceBlit();
- }
- }
-
- private void HideIme()
- {
- _inputHelper?.Hide();
- _containerHelper?.Focus();
- }
-
- public void SetClient(ITextInputMethodClient? client)
- {
- if (_inputHelper is null)
- {
- return;
- }
-
- if(_client != null)
- {
- _client.SurroundingTextChanged -= SurroundingTextChanged;
- }
-
- if(client != null)
- {
- client.SurroundingTextChanged += SurroundingTextChanged;
- }
-
- _inputHelper.Clear();
-
- _client = client;
-
- if (IsActive && _client != null)
- {
- _inputHelper.Show();
- _inputElementFocused = true;
- _inputHelper.Focus();
-
- var surroundingText = _client.SurroundingText;
-
- _inputHelper?.SetSurroundingText(surroundingText.Text, surroundingText.AnchorOffset, surroundingText.CursorOffset);
- }
- else
- {
- _inputElementFocused = false;
- HideIme();
- }
- }
-
- private void SurroundingTextChanged(object? sender, EventArgs e)
- {
- if(_client != null)
- {
- var surroundingText = _client.SurroundingText;
-
- _inputHelper?.SetSurroundingText(surroundingText.Text, surroundingText.AnchorOffset, surroundingText.CursorOffset);
- }
- }
-
- public void SetCursorRect(Rect rect)
- {
- _inputHelper?.Focus();
- var bounds = new PixelRect((int)rect.X, (int) rect.Y, (int) rect.Width, (int) rect.Height);
-
- _inputHelper?.SetBounds(bounds, _client?.SurroundingText.CursorOffset ?? 0);
- _inputHelper?.Focus();
- }
-
- public void SetOptions(TextInputOptions options)
- {
- }
-
- public void Reset()
- {
- _inputHelper?.Clear();
- _inputHelper?.SetSurroundingText("", 0, 0);
- }
- }
-}
diff --git a/src/Web/Avalonia.Web.Blazor/BlazorSingleViewLifetime.cs b/src/Web/Avalonia.Web.Blazor/BlazorSingleViewLifetime.cs
index 7970f09a58..f38779f834 100644
--- a/src/Web/Avalonia.Web.Blazor/BlazorSingleViewLifetime.cs
+++ b/src/Web/Avalonia.Web.Blazor/BlazorSingleViewLifetime.cs
@@ -1,31 +1,39 @@
-using Avalonia.Controls;
+using System.Runtime.Versioning;
+
+using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
-using Avalonia.Media;
-namespace Avalonia.Web.Blazor
+namespace Avalonia.Web.Blazor;
+
+[SupportedOSPlatform("browser")]
+public static class WebAppBuilder
{
- public class BlazorSingleViewLifetime : ISingleViewApplicationLifetime
+ public static T SetupWithSingleViewLifetime(
+ this T builder)
+ where T : AppBuilderBase, new()
{
- public Control? MainView { get; set; }
+ return builder.SetupWithLifetime(new BlazorSingleViewLifetime());
}
- public static class WebAppBuilder
+ public static T UseBlazor(this T builder) where T : AppBuilderBase, new()
{
- public static T SetupWithSingleViewLifetime(
- this T builder)
- where T : AppBuilderBase, new()
- {
- return builder.SetupWithLifetime(new BlazorSingleViewLifetime());
- }
+ return builder
+ .UseBrowser()
+ .With(new BrowserPlatformOptions
+ {
+ FrameworkAssetPathResolver = new(filePath => $"/_content/Avalonia.Web.Blazor/{filePath}")
+ });
+ }
- public static AvaloniaBlazorAppBuilder Configure()
- where TApp : Application, new()
- {
- var builder = AvaloniaBlazorAppBuilder.Configure()
- .UseSkia()
- .With(new SkiaOptions { CustomGpuFactory = () => new BlazorSkiaGpu() });
+ public static AppBuilder Configure()
+ where TApp : Application, new()
+ {
+ return AppBuilder.Configure()
+ .UseBlazor();
+ }
- return builder;
- }
+ internal class BlazorSingleViewLifetime : ISingleViewApplicationLifetime
+ {
+ public Control? MainView { get; set; }
}
}
diff --git a/src/Web/Avalonia.Web.Blazor/BlazorSkiaGpu.cs b/src/Web/Avalonia.Web.Blazor/BlazorSkiaGpu.cs
deleted file mode 100644
index 6fa7bf0bde..0000000000
--- a/src/Web/Avalonia.Web.Blazor/BlazorSkiaGpu.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using Avalonia.Skia;
-
-namespace Avalonia.Web.Blazor
-{
- public class BlazorSkiaGpu : ISkiaGpu
- {
- public ISkiaGpuRenderTarget? TryCreateRenderTarget(IEnumerable surfaces)
- {
- foreach (var surface in surfaces)
- {
- if (surface is BlazorSkiaSurface blazorSkiaSurface)
- {
- return new BlazorSkiaGpuRenderTarget(blazorSkiaSurface);
- }
- }
-
- return null;
- }
-
- public ISkiaSurface? TryCreateSurface(PixelSize size, ISkiaGpuRenderSession session)
- {
- return null;
- }
- }
-}
diff --git a/src/Web/Avalonia.Web.Blazor/BlazorSkiaGpuRenderSession.cs b/src/Web/Avalonia.Web.Blazor/BlazorSkiaGpuRenderSession.cs
deleted file mode 100644
index 0c53825131..0000000000
--- a/src/Web/Avalonia.Web.Blazor/BlazorSkiaGpuRenderSession.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-using Avalonia.Skia;
-using SkiaSharp;
-
-namespace Avalonia.Web.Blazor
-{
- internal class BlazorSkiaGpuRenderSession : ISkiaGpuRenderSession
- {
- private readonly SKSurface _surface;
-
-
- public BlazorSkiaGpuRenderSession(BlazorSkiaSurface blazorSkiaSurface, GRBackendRenderTarget renderTarget)
- {
- _surface = SKSurface.Create(blazorSkiaSurface.Context, renderTarget, blazorSkiaSurface.Origin, blazorSkiaSurface.ColorType);
-
- GrContext = blazorSkiaSurface.Context;
-
- ScaleFactor = blazorSkiaSurface.Scaling;
-
- SurfaceOrigin = blazorSkiaSurface.Origin;
- }
-
- public void Dispose()
- {
- _surface.Flush();
-
- _surface.Dispose();
- }
-
- public GRContext GrContext { get; }
-
- public SKSurface SkSurface => _surface;
-
- public double ScaleFactor { get; }
-
- public GRSurfaceOrigin SurfaceOrigin { get; }
- }
-}
diff --git a/src/Web/Avalonia.Web.Blazor/BlazorSkiaGpuRenderTarget.cs b/src/Web/Avalonia.Web.Blazor/BlazorSkiaGpuRenderTarget.cs
deleted file mode 100644
index fa6a39f210..0000000000
--- a/src/Web/Avalonia.Web.Blazor/BlazorSkiaGpuRenderTarget.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-using Avalonia.Skia;
-using SkiaSharp;
-
-namespace Avalonia.Web.Blazor
-{
- internal class BlazorSkiaGpuRenderTarget : ISkiaGpuRenderTarget
- {
- private readonly GRBackendRenderTarget _renderTarget;
- private readonly BlazorSkiaSurface _blazorSkiaSurface;
- private readonly PixelSize _size;
-
- public BlazorSkiaGpuRenderTarget(BlazorSkiaSurface blazorSkiaSurface)
- {
- _size = blazorSkiaSurface.Size;
-
- var glFbInfo = new GRGlFramebufferInfo(blazorSkiaSurface.GlInfo.FboId, blazorSkiaSurface.ColorType.ToGlSizedFormat());
- {
- _blazorSkiaSurface = blazorSkiaSurface;
- _renderTarget = new GRBackendRenderTarget(
- (int)(blazorSkiaSurface.Size.Width * blazorSkiaSurface.Scaling),
- (int)(blazorSkiaSurface.Size.Height * blazorSkiaSurface.Scaling),
- blazorSkiaSurface.GlInfo.Samples,
- blazorSkiaSurface.GlInfo.Stencils, glFbInfo);
- }
- }
-
- public void Dispose()
- {
- _renderTarget.Dispose();
- }
-
- public ISkiaGpuRenderSession BeginRenderingSession()
- {
- return new BlazorSkiaGpuRenderSession(_blazorSkiaSurface, _renderTarget);
- }
-
- public bool IsCorrupted => _blazorSkiaSurface.Size != _size;
- }
-}
diff --git a/src/Web/Avalonia.Web.Blazor/BlazorSkiaRasterSurface.cs b/src/Web/Avalonia.Web.Blazor/BlazorSkiaRasterSurface.cs
deleted file mode 100644
index 603a792de3..0000000000
--- a/src/Web/Avalonia.Web.Blazor/BlazorSkiaRasterSurface.cs
+++ /dev/null
@@ -1,87 +0,0 @@
-using System.Runtime.InteropServices;
-using Avalonia.Controls.Platform.Surfaces;
-using Avalonia.Platform;
-using Avalonia.Skia;
-using SkiaSharp;
-
-namespace Avalonia.Web.Blazor
-{
- internal class BlazorSkiaRasterSurface : IBlazorSkiaSurface, IFramebufferPlatformSurface, IDisposable
- {
- public SKColorType ColorType { get; set; }
-
- public PixelSize Size { get; set; }
-
- public double Scaling { get; set; }
-
- private FramebufferData? _fbData;
- private readonly Action _blitCallback;
- private readonly Action _onDisposeAction;
-
- public BlazorSkiaRasterSurface(
- SKColorType colorType, PixelSize size, double scaling, Action blitCallback)
- {
- ColorType = colorType;
- Size = size;
- Scaling = scaling;
- _blitCallback = blitCallback;
- _onDisposeAction = Blit;
- }
-
- public void Dispose()
- {
- _fbData?.Dispose();
- _fbData = null;
- }
-
- public ILockedFramebuffer Lock()
- {
- var bytesPerPixel = 4; // TODO: derive from ColorType
- var dpi = Scaling * 96.0;
- var width = (int)(Size.Width * Scaling);
- var height = (int)(Size.Height * Scaling);
-
- if (_fbData is null || _fbData?.Size.Width != width || _fbData?.Size.Height != height)
- {
- _fbData?.Dispose();
- _fbData = new FramebufferData(width, height, bytesPerPixel);
- }
-
- var pixelFormat = ColorType.ToPixelFormat();
- var data = _fbData.Value;
- return new LockedFramebuffer(
- data.Address, data.Size, data.RowBytes,
- new Vector(dpi, dpi), pixelFormat, _onDisposeAction);
- }
-
- private void Blit()
- {
- if (_fbData != null)
- {
- var data = _fbData.Value;
- _blitCallback(data.Address, new SKSizeI(data.Size.Width, data.Size.Height));
- }
- }
-
- private readonly struct FramebufferData
- {
- public PixelSize Size { get; }
-
- public int RowBytes { get; }
-
- public IntPtr Address { get; }
-
- public FramebufferData(int width, int height, int bytesPerPixel)
- {
- Size = new PixelSize(width, height);
- RowBytes = width * bytesPerPixel;
- Address = Marshal.AllocHGlobal(width * height * bytesPerPixel);
- }
-
- public void Dispose()
- {
- Marshal.FreeHGlobal(Address);
- }
- }
- }
-}
diff --git a/src/Web/Avalonia.Web.Blazor/BlazorSkiaSurface.cs b/src/Web/Avalonia.Web.Blazor/BlazorSkiaSurface.cs
deleted file mode 100644
index fb49df338b..0000000000
--- a/src/Web/Avalonia.Web.Blazor/BlazorSkiaSurface.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-using Avalonia.Web.Blazor.Interop;
-using SkiaSharp;
-
-namespace Avalonia.Web.Blazor
-{
- internal class BlazorSkiaSurface : IBlazorSkiaSurface
- {
- public BlazorSkiaSurface(GRContext context, SKHtmlCanvasInterop.GLInfo glInfo, SKColorType colorType, PixelSize size, double scaling, GRSurfaceOrigin origin)
- {
- Context = context;
- GlInfo = glInfo;
- ColorType = colorType;
- Size = size;
- Scaling = scaling;
- Origin = origin;
- }
-
- public SKColorType ColorType { get; set; }
-
- public PixelSize Size { get; set; }
-
- public GRContext Context { get; set; }
-
- public GRSurfaceOrigin Origin { get; set; }
-
- public double Scaling { get; set; }
-
- public SKHtmlCanvasInterop.GLInfo GlInfo { get; set; }
- }
-}
diff --git a/src/Web/Avalonia.Web.Blazor/ClipboardImpl.cs b/src/Web/Avalonia.Web.Blazor/ClipboardImpl.cs
deleted file mode 100644
index bafc07ca15..0000000000
--- a/src/Web/Avalonia.Web.Blazor/ClipboardImpl.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using Avalonia.Input;
-using Avalonia.Input.Platform;
-using Microsoft.JSInterop;
-
-namespace Avalonia.Web.Blazor
-{
- internal class ClipboardImpl : IClipboard
- {
- public async Task GetTextAsync()
- {
- return await AvaloniaLocator.Current.GetRequiredService().
- InvokeAsync("navigator.clipboard.readText");
- }
-
- public async Task SetTextAsync(string text)
- {
- await AvaloniaLocator.Current.GetRequiredService().
- InvokeAsync("navigator.clipboard.writeText",text);
- }
-
- public async Task ClearAsync() => await SetTextAsync("");
-
- public Task SetDataObjectAsync(IDataObject data) => Task.CompletedTask;
-
- public Task GetFormatsAsync() => Task.FromResult(Array.Empty());
-
- public Task GetDataAsync(string format) => Task.FromResult(new());
- }
-}
diff --git a/src/Web/Avalonia.Web.Blazor/Cursor.cs b/src/Web/Avalonia.Web.Blazor/Cursor.cs
deleted file mode 100644
index d921b2fa6c..0000000000
--- a/src/Web/Avalonia.Web.Blazor/Cursor.cs
+++ /dev/null
@@ -1,93 +0,0 @@
-using Avalonia.Input;
-using Avalonia.Platform;
-
-namespace Avalonia.Web.Blazor
-{
- public class CssCursor : ICursorImpl
- {
- public const string Default = "default";
- public string? Value { get; set; }
-
- public CssCursor(StandardCursorType type)
- {
- Value = ToKeyword(type);
- }
-
- ///
- /// Create a cursor from base64 image
- ///
- public CssCursor(string base64, string format, PixelPoint hotspot, StandardCursorType fallback)
- {
- Value = $"url(\"data:image/{format};base64,{base64}\") {hotspot.X} {hotspot.Y}, {ToKeyword(fallback)}";
- }
-
- ///
- /// Create a cursor from url to *.cur file.
- ///
- public CssCursor(string url, StandardCursorType fallback)
- {
- Value = $"url('{url}'), {ToKeyword(fallback)}";
- }
-
- ///
- /// Create a cursor from png/svg and hotspot position
- ///
- public CssCursor(string url, PixelPoint hotSpot, StandardCursorType fallback)
- {
- Value = $"url('{url}') {hotSpot.X} {hotSpot.Y}, {ToKeyword(fallback)}";
- }
-
- private static string ToKeyword(StandardCursorType type) => type switch
- {
- StandardCursorType.Hand => "pointer",
- StandardCursorType.Cross => "crosshair",
- StandardCursorType.Help => "help",
- StandardCursorType.Ibeam => "text",
- StandardCursorType.No => "not-allowed",
- StandardCursorType.None => "none",
- StandardCursorType.Wait => "progress",
- StandardCursorType.AppStarting => "wait",
-
- StandardCursorType.DragMove => "move",
- StandardCursorType.DragCopy => "copy",
- StandardCursorType.DragLink => "alias",
-
- StandardCursorType.UpArrow => "default",/*not found matching one*/
- StandardCursorType.SizeWestEast => "ew-resize",
- StandardCursorType.SizeNorthSouth => "ns-resize",
- StandardCursorType.SizeAll => "move",
-
- StandardCursorType.TopSide => "n-resize",
- StandardCursorType.BottomSide => "s-resize",
- StandardCursorType.LeftSide => "w-resize",
- StandardCursorType.RightSide => "e-resize",
- StandardCursorType.TopLeftCorner => "nw-resize",
- StandardCursorType.TopRightCorner => "ne-resize",
- StandardCursorType.BottomLeftCorner => "sw-resize",
- StandardCursorType.BottomRightCorner => "se-resize",
-
- _ => Default,
- };
-
- public void Dispose() {}
- }
-
- internal class CssCursorFactory : ICursorFactory
- {
- public ICursorImpl CreateCursor(IBitmapImpl cursor, PixelPoint hotSpot)
- {
- using var imageStream = new MemoryStream();
- cursor.Save(imageStream);
-
- //not memory optimized because CryptoStream with ToBase64Transform is not supported in the browser.
- var base64String = Convert.ToBase64String(imageStream.ToArray());
- return new CssCursor(base64String, "png", hotSpot, StandardCursorType.Arrow);
- }
-
- ICursorImpl ICursorFactory.GetCursor(StandardCursorType cursorType)
- {
- return new CssCursor(cursorType);
- }
- }
-}
-
diff --git a/src/Web/Avalonia.Web.Blazor/IBlazorSkiaSurface.cs b/src/Web/Avalonia.Web.Blazor/IBlazorSkiaSurface.cs
deleted file mode 100644
index 5463893e27..0000000000
--- a/src/Web/Avalonia.Web.Blazor/IBlazorSkiaSurface.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace Avalonia.Web.Blazor
-{
- internal interface IBlazorSkiaSurface
- {
- public PixelSize Size { get; set; }
-
- public double Scaling { get; set; }
- }
-}
diff --git a/src/Web/Avalonia.Web.Blazor/Interop/ActionHelper.cs b/src/Web/Avalonia.Web.Blazor/Interop/ActionHelper.cs
deleted file mode 100644
index 8bb266a942..0000000000
--- a/src/Web/Avalonia.Web.Blazor/Interop/ActionHelper.cs
+++ /dev/null
@@ -1,81 +0,0 @@
-using System;
-using System.ComponentModel;
-using Microsoft.JSInterop;
-
-namespace Avalonia.Web.Blazor.Interop
-{
- [EditorBrowsable(EditorBrowsableState.Never)]
- public class ActionHelper
- {
- private readonly Action action;
-
- public ActionHelper(Action action)
- {
- this.action = action;
- }
-
- [JSInvokable]
- public void Invoke() => action?.Invoke();
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public class ActionHelper
- {
- private readonly Action action;
-
- public ActionHelper(Action action)
- {
- this.action = action;
- }
-
- [JSInvokable]
- public void Invoke(T param1) => action?.Invoke(param1);
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public class ActionHelper
- {
- private readonly Action action;
-
- public ActionHelper(Action action)
- {
- this.action = action;
- }
-
- [JSInvokable]
- public void Invoke(T1 p1, T2 p2) => action?.Invoke(p1, p2);
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public class ActionHelper
- {
- private readonly Action action;
-
- public ActionHelper(Action action)
- {
- this.action = action;
- }
-
- [JSInvokable]
- public void Invoke(T1 p1, T2 p2, T3 p3) => action?.Invoke(p1, p2, p3);
- }
-
- [EditorBrowsable(EditorBrowsableState.Never)]
- public class ActionHelper
- {
- private readonly Action action;
-
- public ActionHelper(Action action)
- {
- this.action = action;
- }
-
- [JSInvokable]
- public void Invoke(T1 p1, T2 p2, T3 p3, T4 p4) => action?.Invoke(p1, p2, p3, p4);
- }
-
-
-
-
-
-}
diff --git a/src/Web/Avalonia.Web.Blazor/Interop/AvaloniaModule.cs b/src/Web/Avalonia.Web.Blazor/Interop/AvaloniaModule.cs
deleted file mode 100644
index ff13e95aa7..0000000000
--- a/src/Web/Avalonia.Web.Blazor/Interop/AvaloniaModule.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using Microsoft.JSInterop;
-
-namespace Avalonia.Web.Blazor.Interop
-{
- internal class AvaloniaModule : JSModuleInterop
- {
- private AvaloniaModule(IJSRuntime js) : base(js, "./_content/Avalonia.Web.Blazor/Avalonia.js")
- {
- }
-
- public static async Task ImportAsync(IJSRuntime js)
- {
- var interop = new AvaloniaModule(js);
- await interop.ImportAsync();
- return interop;
- }
- }
-}
diff --git a/src/Web/Avalonia.Web.Blazor/Interop/DpiWatcherInterop.cs b/src/Web/Avalonia.Web.Blazor/Interop/DpiWatcherInterop.cs
deleted file mode 100644
index c86c72f29c..0000000000
--- a/src/Web/Avalonia.Web.Blazor/Interop/DpiWatcherInterop.cs
+++ /dev/null
@@ -1,72 +0,0 @@
-using Microsoft.JSInterop;
-
-namespace Avalonia.Web.Blazor.Interop
-{
- internal class DpiWatcherInterop : IDisposable
- {
- private const string StartSymbol = "DpiWatcher.start";
- private const string StopSymbol = "DpiWatcher.stop";
- private const string GetDpiSymbol = "DpiWatcher.getDpi";
-
- private event Action? callbacksEvent;
- private readonly ActionHelper _callbackHelper;
- private readonly AvaloniaModule _module;
-
- private DotNetObjectReference>? callbackReference;
-
- public DpiWatcherInterop(AvaloniaModule module, Action? callback = null)
- {
- _module = module;
- _callbackHelper = new ActionHelper((o, n) => callbacksEvent?.Invoke(n));
-
- if (callback != null)
- Subscribe(callback);
- }
-
- public void Dispose() => Stop();
-
- public void Subscribe(Action callback)
- {
- var shouldStart = callbacksEvent == null;
-
- callbacksEvent += callback;
-
- var dpi = shouldStart
- ? Start()
- : GetDpi();
-
- callback(dpi);
- }
-
- public void Unsubscribe(Action callback)
- {
- callbacksEvent -= callback;
-
- if (callbacksEvent == null)
- Stop();
- }
-
- private double Start()
- {
- if (callbackReference != null)
- return GetDpi();
-
- callbackReference = DotNetObjectReference.Create(_callbackHelper);
-
- return _module.Invoke(StartSymbol, callbackReference);
- }
-
- private void Stop()
- {
- if (callbackReference == null)
- return;
-
- _module.Invoke(StopSymbol);
-
- callbackReference?.Dispose();
- callbackReference = null;
- }
-
- public double GetDpi() => _module.Invoke(GetDpiSymbol);
- }
-}
diff --git a/src/Web/Avalonia.Web.Blazor/Interop/FocusHelperInterop.cs b/src/Web/Avalonia.Web.Blazor/Interop/FocusHelperInterop.cs
deleted file mode 100644
index 090909f98f..0000000000
--- a/src/Web/Avalonia.Web.Blazor/Interop/FocusHelperInterop.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using Microsoft.AspNetCore.Components;
-
-namespace Avalonia.Web.Blazor.Interop;
-
-internal class FocusHelperInterop
-{
- private const string FocusSymbol = "FocusHelper.focus";
- private const string SetCursorSymbol = "FocusHelper.setCursor";
-
- private readonly AvaloniaModule _module;
- private readonly ElementReference _inputElement;
-
- public FocusHelperInterop(AvaloniaModule module, ElementReference inputElement)
- {
- _module = module;
- _inputElement = inputElement;
- }
-
- public void Focus() => _module.Invoke(FocusSymbol, _inputElement);
-
- public void SetCursor(string kind) => _module.Invoke(SetCursorSymbol, _inputElement, kind);
-}
diff --git a/src/Web/Avalonia.Web.Blazor/Interop/InputHelperInterop.cs b/src/Web/Avalonia.Web.Blazor/Interop/InputHelperInterop.cs
deleted file mode 100644
index 8872339f91..0000000000
--- a/src/Web/Avalonia.Web.Blazor/Interop/InputHelperInterop.cs
+++ /dev/null
@@ -1,130 +0,0 @@
-using Microsoft.AspNetCore.Components;
-using Microsoft.JSInterop;
-
-namespace Avalonia.Web.Blazor.Interop
-{
- internal class WebCompositionEventArgs : EventArgs
- {
- public enum WebCompositionEventType
- {
- Start,
- Update,
- End
- }
-
- public WebCompositionEventArgs(string type, string data)
- {
- Type = type switch
- {
- "compositionstart" => WebCompositionEventType.Start,
- "compositionupdate" => WebCompositionEventType.Update,
- "compositionend" => WebCompositionEventType.End,
- _ => Type
- };
-
- Data = data;
- }
-
- public WebCompositionEventType Type { get; }
-
- public string Data { get; }
- }
-
- internal class WebInputEventArgs
- {
- public WebInputEventArgs(string type, string data)
- {
- Type = type;
- Data = data;
- }
-
- public string Type { get; }
-
- public string Data { get; }
-
- public bool Handled { get; set; }
- }
-
- internal class InputHelperInterop
- {
- private const string ClearSymbol = "InputHelper.clear";
- private const string FocusSymbol = "InputHelper.focus";
- private const string SetCursorSymbol = "InputHelper.setCursor";
- private const string HideSymbol = "InputHelper.hide";
- private const string ShowSymbol = "InputHelper.show";
- private const string StartSymbol = "InputHelper.start";
- private const string SetSurroundingTextSymbol = "InputHelper.setSurroundingText";
- private const string SetBoundsSymbol = "InputHelper.setBounds";
-
- private readonly AvaloniaModule _module;
- private readonly ElementReference _inputElement;
- private readonly ActionHelper _compositionAction;
- private readonly ActionHelper _inputAction;
-
- private DotNetObjectReference>? compositionActionReference;
- private DotNetObjectReference>? inputActionReference;
-
- public InputHelperInterop(AvaloniaModule module, ElementReference inputElement)
- {
- _module = module;
- _inputElement = inputElement;
-
- _compositionAction = new ActionHelper(OnCompositionEvent);
- _inputAction = new ActionHelper(OnInputEvent);
-
- Start();
- }
-
- public event EventHandler? CompositionEvent;
- public event EventHandler? InputEvent;
-
- private void OnCompositionEvent(string type, string data)
- {
- Console.WriteLine($"CompositionEvent Handler Helper {CompositionEvent == null} ");
- CompositionEvent?.Invoke(this, new WebCompositionEventArgs(type, data));
- }
-
- private void OnInputEvent(string type, string data)
- {
- Console.WriteLine($"InputEvent Handler Helper {InputEvent == null} ");
-
- var args = new WebInputEventArgs(type, data);
-
- InputEvent?.Invoke(this, args);
- }
-
- public void Clear() => _module.Invoke(ClearSymbol, _inputElement);
-
- public void Focus() => _module.Invoke(FocusSymbol, _inputElement);
-
- public void SetCursor(string kind) => _module.Invoke(SetCursorSymbol, _inputElement, kind);
-
- public void Hide() => _module.Invoke(HideSymbol, _inputElement);
-
- public void Show() => _module.Invoke(ShowSymbol, _inputElement);
-
- private void Start()
- {
- if(compositionActionReference != null)
- {
- return;
- }
-
- compositionActionReference = DotNetObjectReference.Create(_compositionAction);
-
- inputActionReference = DotNetObjectReference.Create(_inputAction);
-
- _module.Invoke(StartSymbol, _inputElement, compositionActionReference, inputActionReference);
- }
-
- public void SetSurroundingText(string text, int start, int end)
- {
- _module.Invoke(SetSurroundingTextSymbol, _inputElement, text, start, end);
- }
-
- public void SetBounds(PixelRect bounds, int caret)
- {
- _module.Invoke(SetBoundsSymbol, _inputElement, bounds.X, bounds.Y, bounds.Width, bounds.Height, caret);
- }
- }
-}
diff --git a/src/Web/Avalonia.Web.Blazor/Interop/JSModuleInterop.cs b/src/Web/Avalonia.Web.Blazor/Interop/JSModuleInterop.cs
deleted file mode 100644
index dca1b53650..0000000000
--- a/src/Web/Avalonia.Web.Blazor/Interop/JSModuleInterop.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-using Microsoft.JSInterop;
-
-namespace Avalonia.Web.Blazor.Interop
-{
- internal class JSModuleInterop : IDisposable
- {
- private readonly Task moduleTask;
- private IJSUnmarshalledObjectReference? module;
-
- public JSModuleInterop(IJSRuntime js, string filename)
- {
- if (js is not IJSInProcessRuntime)
- throw new NotSupportedException("SkiaSharp currently only works on Web Assembly.");
-
- moduleTask = js.InvokeAsync("import", filename).AsTask();
- }
-
- public async Task ImportAsync()
- {
- module = await moduleTask;
- }
-
- public void Dispose()
- {
- OnDisposingModule();
- Module.Dispose();
- }
-
- protected IJSUnmarshalledObjectReference Module =>
- module ?? throw new InvalidOperationException("Make sure to run ImportAsync() first.");
-
- internal void Invoke(string identifier, params object?[]? args) =>
- Module.InvokeVoid(identifier, args);
-
- internal TValue Invoke(string identifier, params object?[]? args) =>
- Module.Invoke(identifier, args);
-
- internal ValueTask InvokeAsync(string identifier, params object?[]? args) =>
- Module.InvokeVoidAsync(identifier, args);
-
- internal ValueTask InvokeAsync(string identifier, params object?[]? args) =>
- Module.InvokeAsync(identifier, args);
-
- protected virtual void OnDisposingModule() { }
- }
-}
diff --git a/src/Web/Avalonia.Web.Blazor/Interop/NativeControlHostImpl.cs b/src/Web/Avalonia.Web.Blazor/Interop/NativeControlHostImpl.cs
deleted file mode 100644
index b824fcae46..0000000000
--- a/src/Web/Avalonia.Web.Blazor/Interop/NativeControlHostImpl.cs
+++ /dev/null
@@ -1,144 +0,0 @@
-using System.Diagnostics.CodeAnalysis;
-
-using Avalonia.Controls.Platform;
-using Avalonia.Platform;
-
-using Microsoft.AspNetCore.Components;
-using Microsoft.JSInterop;
-
-namespace Avalonia.Web.Blazor.Interop
-{
-
- internal class NativeControlHostInterop : INativeControlHostImpl
- {
- private const string CreateDefaultChildSymbol = "NativeControlHost.CreateDefaultChild";
- private const string CreateAttachmentSymbol = "NativeControlHost.CreateAttachment";
- private const string GetReferenceSymbol = "NativeControlHost.GetReference";
-
- private readonly AvaloniaModule _module;
- private readonly ElementReference _hostElement;
-
- public NativeControlHostInterop(AvaloniaModule module, ElementReference element)
- {
- _module = module;
- _hostElement = element;
- }
-
- public INativeControlHostDestroyableControlHandle CreateDefaultChild(IPlatformHandle parent)
- {
- var element = _module.Invoke(CreateDefaultChildSymbol);
- return new JSObjectControlHandle(element);
- }
-
- public INativeControlHostControlTopLevelAttachment CreateNewAttachment(Func create)
- {
- Attachment? a = null;
- try
- {
- using var hostElementJsReference = _module.Invoke(GetReferenceSymbol, _hostElement);
- var child = create(new JSObjectControlHandle(hostElementJsReference));
- var attachmenetReference = _module.Invoke(CreateAttachmentSymbol);
- // It has to be assigned to the variable before property setter is called so we dispose it on exception
-#pragma warning disable IDE0017 // Simplify object initialization
- a = new Attachment(attachmenetReference, child);
-#pragma warning restore IDE0017 // Simplify object initialization
- a.AttachedTo = this;
- return a;
- }
- catch
- {
- a?.Dispose();
- throw;
- }
- }
-
- public INativeControlHostControlTopLevelAttachment CreateNewAttachment(IPlatformHandle handle)
- {
- var attachmenetReference = _module.Invoke(CreateAttachmentSymbol);
- var a = new Attachment(attachmenetReference, handle);
- a.AttachedTo = this;
- return a;
- }
-
- public bool IsCompatibleWith(IPlatformHandle handle) => handle is JSObjectControlHandle;
-
- class Attachment : INativeControlHostControlTopLevelAttachment
- {
- private const string InitializeWithChildHandleSymbol = "InitializeWithChildHandle";
- private const string AttachToSymbol = "AttachTo";
- private const string ShowInBoundsSymbol = "ShowInBounds";
- private const string HideWithSizeSymbol = "HideWithSize";
- private const string ReleaseChildSymbol = "ReleaseChild";
-
- private IJSInProcessObjectReference? _native;
- private NativeControlHostInterop? _attachedTo;
-
- public Attachment(IJSInProcessObjectReference native, IPlatformHandle handle)
- {
- _native = native;
- _native.InvokeVoid(InitializeWithChildHandleSymbol, ((JSObjectControlHandle)handle).Object);
- }
-
- public void Dispose()
- {
- if (_native != null)
- {
- _native.InvokeVoid(ReleaseChildSymbol);
- _native.Dispose();
- _native = null;
- }
- }
-
- public INativeControlHostImpl? AttachedTo
- {
- get => _attachedTo!;
- set
- {
- CheckDisposed();
-
- var host = (NativeControlHostInterop?)value;
- if (host == null)
- {
- _native.InvokeVoid(AttachToSymbol);
- }
- else
- {
- _native.InvokeVoid(AttachToSymbol, host._hostElement);
- }
- _attachedTo = host;
- }
- }
-
- public bool IsCompatibleWith(INativeControlHostImpl host) => host is NativeControlHostInterop;
-
- public void HideWithSize(Size size)
- {
- CheckDisposed();
- if (_attachedTo == null)
- return;
-
- _native.InvokeVoid(HideWithSizeSymbol, Math.Max(1, (float)size.Width), Math.Max(1, (float)size.Height));
- }
-
- public void ShowInBounds(Rect bounds)
- {
- CheckDisposed();
-
- if (_attachedTo == null)
- throw new InvalidOperationException("Native control isn't attached to a toplevel");
-
- bounds = new Rect(bounds.X, bounds.Y, Math.Max(1, bounds.Width),
- Math.Max(1, bounds.Height));
-
- _native.InvokeVoid(ShowInBoundsSymbol, (float)bounds.X, (float)bounds.Y, (float)bounds.Width, (float)bounds.Height);
- }
-
- [MemberNotNull(nameof(_native))]
- private void CheckDisposed()
- {
- if (_native == null)
- throw new ObjectDisposedException(nameof(Attachment));
- }
- }
- }
-}
diff --git a/src/Web/Avalonia.Web.Blazor/Interop/SKHtmlCanvasInterop.cs b/src/Web/Avalonia.Web.Blazor/Interop/SKHtmlCanvasInterop.cs
deleted file mode 100644
index cf9350fb62..0000000000
--- a/src/Web/Avalonia.Web.Blazor/Interop/SKHtmlCanvasInterop.cs
+++ /dev/null
@@ -1,76 +0,0 @@
-using Microsoft.AspNetCore.Components;
-using Microsoft.JSInterop;
-using SkiaSharp;
-
-namespace Avalonia.Web.Blazor.Interop
-{
- internal class SKHtmlCanvasInterop : IDisposable
- {
- private const string JsFilename = "./_content/Avalonia.Web.Blazor/SKHtmlCanvas.js";
- private const string InitGLSymbol = "SKHtmlCanvas.initGL";
- private const string InitRasterSymbol = "SKHtmlCanvas.initRaster";
- private const string DeinitSymbol = "SKHtmlCanvas.deinit";
- private const string RequestAnimationFrameSymbol = "SKHtmlCanvas.requestAnimationFrame";
- private const string SetCanvasSizeSymbol = "SKHtmlCanvas.setCanvasSize";
- private const string PutImageDataSymbol = "SKHtmlCanvas.putImageData";
-
- private readonly AvaloniaModule _module;
- private readonly ElementReference _htmlCanvas;
- private readonly string _htmlElementId;
- private readonly ActionHelper _callbackHelper;
-
- private DotNetObjectReference? callbackReference;
-
- public SKHtmlCanvasInterop(AvaloniaModule module, ElementReference element, Action renderFrameCallback)
- {
- _module = module;
- _htmlCanvas = element;
- _htmlElementId = element.Id;
-
- _callbackHelper = new ActionHelper(renderFrameCallback);
- }
-
- public void Dispose() => Deinit();
-
- public GLInfo InitGL()
- {
- if (callbackReference != null)
- throw new InvalidOperationException("Unable to initialize the same canvas more than once.");
-
- callbackReference = DotNetObjectReference.Create(_callbackHelper);
-
- return _module.Invoke(InitGLSymbol, _htmlCanvas, _htmlElementId, callbackReference);
- }
-
- public bool InitRaster()
- {
- if (callbackReference != null)
- throw new InvalidOperationException("Unable to initialize the same canvas more than once.");
-
- callbackReference = DotNetObjectReference.Create(_callbackHelper);
-
- return _module.Invoke(InitRasterSymbol, _htmlCanvas, _htmlElementId, callbackReference);
- }
-
- public void Deinit()
- {
- if (callbackReference == null)
- return;
-
- _module.Invoke(DeinitSymbol, _htmlElementId);
-
- callbackReference?.Dispose();
- }
-
- public void RequestAnimationFrame(bool enableRenderLoop) =>
- _module.Invoke(RequestAnimationFrameSymbol, _htmlCanvas, enableRenderLoop);
-
- public void SetCanvasSize(int rawWidth, int rawHeight) =>
- _module.Invoke(SetCanvasSizeSymbol, _htmlCanvas, rawWidth, rawHeight);
-
- public void PutImageData(IntPtr intPtr, SKSizeI rawSize) =>
- _module.Invoke(PutImageDataSymbol, _htmlCanvas, intPtr.ToInt64(), rawSize.Width, rawSize.Height);
-
- public record GLInfo(int ContextId, uint FboId, int Stencils, int Samples, int Depth);
- }
-}
diff --git a/src/Web/Avalonia.Web.Blazor/Interop/SizeWatcherInterop.cs b/src/Web/Avalonia.Web.Blazor/Interop/SizeWatcherInterop.cs
deleted file mode 100644
index e21ae837d0..0000000000
--- a/src/Web/Avalonia.Web.Blazor/Interop/SizeWatcherInterop.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-using Microsoft.AspNetCore.Components;
-using Microsoft.JSInterop;
-using SkiaSharp;
-
-namespace Avalonia.Web.Blazor.Interop
-{
- internal class SizeWatcherInterop : IDisposable
- {
- private const string ObserveSymbol = "SizeWatcher.observe";
- private const string UnobserveSymbol = "SizeWatcher.unobserve";
-
- private readonly AvaloniaModule _module;
- private readonly ElementReference _htmlElement;
- private readonly string _htmlElementId;
- private readonly ActionHelper _callbackHelper;
-
- private DotNetObjectReference>? callbackReference;
-
- public SizeWatcherInterop(AvaloniaModule module, ElementReference element, Action callback)
- {
- _module = module;
- _htmlElement = element;
- _htmlElementId = element.Id;
- _callbackHelper = new ActionHelper((x, y) => callback(new SKSize(x, y)));
- }
-
- public void Dispose() => Stop();
-
- public void Start()
- {
- if (callbackReference != null)
- return;
-
- callbackReference = DotNetObjectReference.Create(_callbackHelper);
-
- _module.Invoke(ObserveSymbol, _htmlElement, _htmlElementId, callbackReference);
- }
-
- public void Stop()
- {
- if (callbackReference == null)
- return;
-
- _module.Invoke(UnobserveSymbol, _htmlElementId);
-
- callbackReference?.Dispose();
- callbackReference = null;
- }
- }
-}
diff --git a/src/Web/Avalonia.Web.Blazor/Interop/Storage/StorageProviderInterop.cs b/src/Web/Avalonia.Web.Blazor/Interop/Storage/StorageProviderInterop.cs
deleted file mode 100644
index 690d9683df..0000000000
--- a/src/Web/Avalonia.Web.Blazor/Interop/Storage/StorageProviderInterop.cs
+++ /dev/null
@@ -1,225 +0,0 @@
-using System.Diagnostics.CodeAnalysis;
-
-using Avalonia.Platform.Storage;
-
-using Microsoft.JSInterop;
-
-namespace Avalonia.Web.Blazor.Interop.Storage
-{
- internal record FilePickerAcceptType(string Description, IReadOnlyDictionary> Accept);
-
- internal record FileProperties(ulong Size, long LastModified, string? Type);
-
- internal class StorageProviderInterop : JSModuleInterop, IStorageProvider
- {
- private const string JsFilename = "./_content/Avalonia.Web.Blazor/Storage.js";
- private const string PickerCancelMessage = "The user aborted a request";
-
- public static async Task ImportAsync(IJSRuntime js)
- {
- var interop = new StorageProviderInterop(js);
- await interop.ImportAsync();
- return interop;
- }
-
- public StorageProviderInterop(IJSRuntime js)
- : base(js, JsFilename)
- {
- }
-
- public bool CanOpen => Invoke("StorageProvider.canOpen");
- public bool CanSave => Invoke("StorageProvider.canSave");
- public bool CanPickFolder => Invoke("StorageProvider.canPickFolder");
-
- public async Task> OpenFilePickerAsync(FilePickerOpenOptions options)
- {
- try
- {
- var startIn = (options.SuggestedStartLocation as JSStorageItem)?.FileHandle;
-
- var (types, exludeAll) = ConvertFileTypes(options.FileTypeFilter);
- var items = await InvokeAsync("StorageProvider.openFileDialog", startIn, options.AllowMultiple, types, exludeAll);
- var count = items.Invoke("count");
-
- return Enumerable.Range(0, count)
- .Select(index => new JSStorageFile(items.Invoke("at", index)))
- .ToArray();
- }
- catch (JSException ex) when (ex.Message.Contains(PickerCancelMessage, StringComparison.Ordinal))
- {
- return Array.Empty();
- }
- }
-
- public async Task SaveFilePickerAsync(FilePickerSaveOptions options)
- {
- try
- {
- var startIn = (options.SuggestedStartLocation as JSStorageItem)?.FileHandle;
-
- var (types, exludeAll) = ConvertFileTypes(options.FileTypeChoices);
- var item = await InvokeAsync("StorageProvider.saveFileDialog", startIn, options.SuggestedFileName, types, exludeAll);
-
- return item is not null ? new JSStorageFile(item) : null;
- }
- catch (JSException ex) when (ex.Message.Contains(PickerCancelMessage, StringComparison.Ordinal))
- {
- return null;
- }
- }
-
- public async Task> OpenFolderPickerAsync(FolderPickerOpenOptions options)
- {
- try
- {
- var startIn = (options.SuggestedStartLocation as JSStorageItem)?.FileHandle;
-
- var item = await InvokeAsync("StorageProvider.selectFolderDialog", startIn);
-
- return item is not null ? new[] { new JSStorageFolder(item) } : Array.Empty();
- }
- catch (JSException ex) when (ex.Message.Contains(PickerCancelMessage, StringComparison.Ordinal))
- {
- return Array.Empty();
- }
- }
-
- public async Task OpenFileBookmarkAsync(string bookmark)
- {
- var item = await InvokeAsync("StorageProvider.openBookmark", bookmark);
- return item is not null ? new JSStorageFile(item) : null;
- }
-
- public async Task OpenFolderBookmarkAsync(string bookmark)
- {
- var item = await InvokeAsync("StorageProvider.openBookmark", bookmark);
- return item is not null ? new JSStorageFolder(item) : null;
- }
-
- private static (FilePickerAcceptType[]? types, bool excludeAllOption) ConvertFileTypes(IEnumerable? input)
- {
- var types = input?
- .Where(t => t.MimeTypes?.Any() == true && t != FilePickerFileTypes.All)
- .Select(t => new FilePickerAcceptType(t.Name, t.MimeTypes!
- .ToDictionary(m => m, _ => (IReadOnlyList)Array.Empty())))
- .ToArray();
- if (types?.Length == 0)
- {
- types = null;
- }
-
- var inlcudeAll = input?.Contains(FilePickerFileTypes.All) == true || types is null;
-
- return (types, !inlcudeAll);
- }
- }
-
- internal abstract class JSStorageItem : IStorageBookmarkItem
- {
- internal IJSInProcessObjectReference? _fileHandle;
-
- protected JSStorageItem(IJSInProcessObjectReference fileHandle)
- {
- _fileHandle = fileHandle ?? throw new ArgumentNullException(nameof(fileHandle));
- }
-
- internal IJSInProcessObjectReference FileHandle => _fileHandle ?? throw new ObjectDisposedException(nameof(JSStorageItem));
-
- public string Name => FileHandle.Invoke("getName");
-
- public bool TryGetUri([NotNullWhen(true)] out Uri? uri)
- {
- uri = new Uri(Name, UriKind.Relative);
- return false;
- }
-
- public async Task GetBasicPropertiesAsync()
- {
- var properties = await FileHandle.InvokeAsync("getProperties");
-
- return new StorageItemProperties(
- properties?.Size,
- dateCreated: null,
- dateModified: properties?.LastModified > 0 ? DateTimeOffset.FromUnixTimeMilliseconds(properties.LastModified) : null);
- }
-
- public bool CanBookmark => true;
-
- public Task SaveBookmarkAsync()
- {
- return FileHandle.InvokeAsync("saveBookmark").AsTask();
- }
-
- public Task GetParentAsync()
- {
- return Task.FromResult(null);
- }
-
- public Task ReleaseBookmarkAsync()
- {
- return FileHandle.InvokeAsync("deleteBookmark").AsTask();
- }
-
- public void Dispose()
- {
- _fileHandle?.Dispose();
- _fileHandle = null;
- }
- }
-
- internal class JSStorageFile : JSStorageItem, IStorageBookmarkFile
- {
- public JSStorageFile(IJSInProcessObjectReference fileHandle) : base(fileHandle)
- {
- }
-
- public bool CanOpenRead => true;
- public async Task OpenReadAsync()
- {
- var stream = await FileHandle.InvokeAsync("openRead");
- // Remove maxAllowedSize limit, as developer can decide if they read only small part or everything.
- return await stream.OpenReadStreamAsync(long.MaxValue, CancellationToken.None);
- }
-
- public bool CanOpenWrite => true;
- public async Task OpenWriteAsync()
- {
- var properties = await FileHandle.InvokeAsync("getProperties");
- var streamWriter = await FileHandle.InvokeAsync("openWrite");
-
- return new JSWriteableStream(streamWriter, (long)(properties?.Size ?? 0));
- }
- }
-
- internal class JSStorageFolder : JSStorageItem, IStorageBookmarkFolder
- {
- public JSStorageFolder(IJSInProcessObjectReference fileHandle) : base(fileHandle)
- {
- }
-
- public async Task> GetItemsAsync()
- {
- var items = await FileHandle.InvokeAsync("getItems");
- if (items is null)
- {
- return Array.Empty();
- }
-
- var count = items.Invoke("count");
-
- return Enumerable.Range(0, count)
- .Select(index =>
- {
- var reference = items.Invoke("at", index);
- return reference.Invoke("getKind") switch
- {
- "directory" => (IStorageItem)new JSStorageFolder(reference),
- "file" => new JSStorageFile(reference),
- _ => null
- };
- })
- .Where(i => i is not null)
- .ToArray()!;
- }
- }
-}
diff --git a/src/Web/Avalonia.Web.Blazor/Interop/Storage/WriteableStream.cs b/src/Web/Avalonia.Web.Blazor/Interop/Storage/WriteableStream.cs
deleted file mode 100644
index 55a7831b1a..0000000000
--- a/src/Web/Avalonia.Web.Blazor/Interop/Storage/WriteableStream.cs
+++ /dev/null
@@ -1,124 +0,0 @@
-using System.Buffers;
-using System.Text.Json.Serialization;
-
-using Microsoft.JSInterop;
-
-namespace Avalonia.Web.Blazor.Interop.Storage
-{
- // Loose wrapper implementaion of a stream on top of FileAPI FileSystemWritableFileStream
- internal sealed class JSWriteableStream : Stream
- {
- private IJSInProcessObjectReference? _jSReference;
-
- // Unfortunatelly we can't read current length/position, so we need to keep it C#-side only.
- private long _length, _position;
-
- internal JSWriteableStream(IJSInProcessObjectReference jSReference, long initialLength)
- {
- _jSReference = jSReference;
- _length = initialLength;
- }
-
- private IJSInProcessObjectReference JSReference => _jSReference ?? throw new ObjectDisposedException(nameof(JSWriteableStream));
-
- public override bool CanRead => false;
-
- public override bool CanSeek => true;
-
- public override bool CanWrite => true;
-
- public override long Length => _length;
-
- public override long Position
- {
- get => _position;
- set => Seek(_position, SeekOrigin.Begin);
- }
-
- public override void Flush()
- {
- // no-op
- }
-
- public override int Read(byte[] buffer, int offset, int count)
- {
- throw new NotSupportedException();
- }
-
- public override long Seek(long offset, SeekOrigin origin)
- {
- var position = origin switch
- {
- SeekOrigin.Current => _position + offset,
- SeekOrigin.End => _length + offset,
- _ => offset
- };
- JSReference.InvokeVoid("seek", position);
- return position;
- }
-
- public override void SetLength(long value)
- {
- _length = value;
-
- // See https://docs.w3cub.com/dom/filesystemwritablefilestream/truncate
- // If the offset is smaller than the size, it remains unchanged. If the offset is larger than size, the offset is set to that size
- if (_position > _length)
- {
- _position = _length;
- }
-
- JSReference.InvokeVoid("truncate", value);
- }
-
- public override void Write(byte[] buffer, int offset, int count)
- {
- throw new NotSupportedException("Synchronous writes are not supported.");
- }
-
- public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
- {
- if (offset != 0 || count != buffer.Length)
- {
- // TODO, we need to pass prepared buffer to the JS
- // Can't use ArrayPool as it can return bigger array than requested
- // Can't use Span/Memory, as it's not supported by JS interop yet.
- // Alternatively we can pass original buffer and offset+count, so it can be trimmed on the JS side (but is it more efficient tho?)
- buffer = buffer.AsMemory(offset, count).ToArray();
- }
- return WriteAsyncInternal(buffer, cancellationToken).AsTask();
- }
-
- public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default)
- {
- return WriteAsyncInternal(buffer.ToArray(), cancellationToken);
- }
-
- private ValueTask WriteAsyncInternal(byte[] buffer, CancellationToken _)
- {
- _position += buffer.Length;
-
- return JSReference.InvokeVoidAsync("write", buffer);
- }
-
- protected override void Dispose(bool disposing)
- {
- if (_jSReference is { } jsReference)
- {
- _jSReference = null;
- jsReference.InvokeVoid("close");
- jsReference.Dispose();
- }
- }
-
- public override async ValueTask DisposeAsync()
- {
- if (_jSReference is { } jsReference)
- {
- _jSReference = null;
- await jsReference.InvokeVoidAsync("close");
- await jsReference.DisposeAsync();
- }
- }
- }
-}
diff --git a/src/Web/Avalonia.Web.Blazor/JSObjectControlHandle.cs b/src/Web/Avalonia.Web.Blazor/JSObjectControlHandle.cs
deleted file mode 100644
index 4426c3fbd7..0000000000
--- a/src/Web/Avalonia.Web.Blazor/JSObjectControlHandle.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-#nullable enable
-using Avalonia.Controls.Platform;
-
-using Microsoft.JSInterop;
-
-namespace Avalonia.Web.Blazor
-{
- public class JSObjectControlHandle : INativeControlHostDestroyableControlHandle
- {
- internal const string ElementReferenceDescriptor = "JSObjectReference";
-
- public JSObjectControlHandle(IJSObjectReference reference)
- {
- Object = reference;
- }
-
- public IJSObjectReference Object { get; }
-
- public IntPtr Handle => throw new NotSupportedException();
-
- public string? HandleDescriptor => ElementReferenceDescriptor;
-
- public void Destroy()
- {
- if (Object is IJSInProcessObjectReference inProcess)
- {
- inProcess.Dispose();
- }
- else
- {
- _ = Object.DisposeAsync();
- }
- }
- }
-}
diff --git a/src/Web/Avalonia.Web.Blazor/Keycodes.cs b/src/Web/Avalonia.Web.Blazor/Keycodes.cs
deleted file mode 100644
index ea30f0a9f0..0000000000
--- a/src/Web/Avalonia.Web.Blazor/Keycodes.cs
+++ /dev/null
@@ -1,127 +0,0 @@
-using Avalonia.Input;
-
-namespace Avalonia.Web.Blazor
-{
- internal static class Keycodes
- {
- public static Dictionary KeyCodes = new()
- {
- { "Escape", Key.Escape },
- { "Digit1", Key.D1 },
- { "Digit2", Key.D2 },
- { "Digit3", Key.D3 },
- { "Digit4", Key.D4 },
- { "Digit5", Key.D5 },
- { "Digit6", Key.D6 },
- { "Digit7", Key.D7 },
- { "Digit8", Key.D8 },
- { "Digit9", Key.D9 },
- { "Digit0", Key.D0 },
- { "Minus", Key.OemMinus },
- //{ "Equal" , Key. },
- { "Backspace", Key.Back },
- { "Tab", Key.Tab },
- { "KeyQ", Key.Q },
- { "KeyW", Key.W },
- { "KeyE", Key.E },
- { "KeyR", Key.R },
- { "KeyT", Key.T },
- { "KeyY", Key.Y },
- { "KeyU", Key.U },
- { "KeyI", Key.I },
- { "KeyO", Key.O },
- { "KeyP", Key.P },
- { "BracketLeft", Key.OemOpenBrackets },
- { "BracketRight", Key.OemCloseBrackets },
- { "Enter", Key.Enter },
- { "ControlLeft", Key.LeftCtrl },
- { "KeyA", Key.A },
- { "KeyS", Key.S },
- { "KeyD", Key.D },
- { "KeyF", Key.F },
- { "KeyG", Key.G },
- { "KeyH", Key.H },
- { "KeyJ", Key.J },
- { "KeyK", Key.K },
- { "KeyL", Key.L },
- { "Semicolon", Key.OemSemicolon },
- { "Quote", Key.OemQuotes },
- //{ "Backquote" , Key. },
- { "ShiftLeft", Key.LeftShift },
- { "Backslash", Key.OemBackslash },
- { "KeyZ", Key.Z },
- { "KeyX", Key.X },
- { "KeyC", Key.C },
- { "KeyV", Key.V },
- { "KeyB", Key.B },
- { "KeyN", Key.N },
- { "KeyM", Key.M },
- { "Comma", Key.OemComma },
- { "Period", Key.OemPeriod },
- //{ "Slash" , Key. },
- { "ShiftRight", Key.RightShift },
- { "NumpadMultiply", Key.Multiply },
- { "AltLeft", Key.LeftAlt },
- { "Space", Key.Space },
- { "CapsLock", Key.CapsLock },
- { "F1", Key.F1 },
- { "F2", Key.F2 },
- { "F3", Key.F3 },
- { "F4", Key.F4 },
- { "F5", Key.F5 },
- { "F6", Key.F6 },
- { "F7", Key.F7 },
- { "F8", Key.F8 },
- { "F9", Key.F9 },
- { "F10", Key.F10 },
- { "NumLock", Key.NumLock },
- { "ScrollLock", Key.Scroll },
- { "Numpad7", Key.NumPad7 },
- { "Numpad8", Key.NumPad8 },
- { "Numpad9", Key.NumPad9 },
- { "NumpadSubtract", Key.Subtract },
- { "Numpad4", Key.NumPad4 },
- { "Numpad5", Key.NumPad5 },
- { "Numpad6", Key.NumPad6 },
- { "NumpadAdd", Key.Add },
- { "Numpad1", Key.NumPad1 },
- { "Numpad2", Key.NumPad2 },
- { "Numpad3", Key.NumPad3 },
- { "Numpad0", Key.NumPad0 },
- { "NumpadDecimal", Key.Decimal },
- { "Unidentified", Key.NoName },
- //{ "IntlBackslash" , Key.bac },
- { "F11", Key.F11 },
- { "F12", Key.F12 },
- //{ "IntlRo" , Key.Ro },
- //{ "Unidentified" , Key. },
- { "Convert", Key.ImeConvert },
- { "KanaMode", Key.KanaMode },
- { "NonConvert", Key.ImeNonConvert },
- //{ "Unidentified" , Key. },
- { "NumpadEnter", Key.Enter },
- { "ControlRight", Key.RightCtrl },
- { "NumpadDivide", Key.Divide },
- { "PrintScreen", Key.PrintScreen },
- { "AltRight", Key.RightAlt },
- //{ "Unidentified" , Key. },
- { "Home", Key.Home },
- { "ArrowUp", Key.Up },
- { "PageUp", Key.PageUp },
- { "ArrowLeft", Key.Left },
- { "ArrowRight", Key.Right },
- { "End", Key.End },
- { "ArrowDown", Key.Down },
- { "PageDown", Key.PageDown },
- { "Insert", Key.Insert },
- { "Delete", Key.Delete },
- //{ "Unidentified" , Key. },
- { "AudioVolumeMute", Key.VolumeMute },
- { "AudioVolumeDown", Key.VolumeDown },
- { "AudioVolumeUp", Key.VolumeUp },
- //{ "NumpadEqual" , Key. },
- { "Pause", Key.Pause },
- { "NumpadComma", Key.OemComma }
- };
- }
-}
diff --git a/src/Web/Avalonia.Web.Blazor/ManualTriggerRenderTimer.cs b/src/Web/Avalonia.Web.Blazor/ManualTriggerRenderTimer.cs
deleted file mode 100644
index 7b9feab2e3..0000000000
--- a/src/Web/Avalonia.Web.Blazor/ManualTriggerRenderTimer.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using System.Diagnostics;
-using Avalonia.Rendering;
-
-namespace Avalonia.Web.Blazor
-{
- public class ManualTriggerRenderTimer : IRenderTimer
- {
- private static readonly Stopwatch s_sw = Stopwatch.StartNew();
-
- public static ManualTriggerRenderTimer Instance { get; } = new();
-
- public void RaiseTick() => Tick?.Invoke(s_sw.Elapsed);
-
- public event Action? Tick;
- public bool RunsInBackground => false;
- }
-}
diff --git a/src/Web/Avalonia.Web.Blazor/RazorViewTopLevelImpl.cs b/src/Web/Avalonia.Web.Blazor/RazorViewTopLevelImpl.cs
deleted file mode 100644
index 3a09c16932..0000000000
--- a/src/Web/Avalonia.Web.Blazor/RazorViewTopLevelImpl.cs
+++ /dev/null
@@ -1,222 +0,0 @@
-using System.Diagnostics;
-using Avalonia.Controls;
-using Avalonia.Controls.Platform;
-using Avalonia.Input;
-using Avalonia.Input.Raw;
-using Avalonia.Input.TextInput;
-using Avalonia.Platform;
-using Avalonia.Platform.Storage;
-using Avalonia.Rendering;
-using Avalonia.Rendering.Composition;
-using Avalonia.Web.Blazor.Interop;
-using SkiaSharp;
-
-#nullable enable
-
-namespace Avalonia.Web.Blazor
-{
- internal class RazorViewTopLevelImpl : ITopLevelImplWithTextInputMethod, ITopLevelImplWithNativeControlHost, ITopLevelImplWithStorageProvider
- {
- private Size _clientSize;
- private IBlazorSkiaSurface? _currentSurface;
- private IInputRoot? _inputRoot;
- private readonly Stopwatch _sw = Stopwatch.StartNew();
- private readonly AvaloniaView _avaloniaView;
- private readonly TouchDevice _touchDevice;
- private readonly PenDevice _penDevice;
- private string _currentCursor = CssCursor.Default;
-
- public RazorViewTopLevelImpl(AvaloniaView avaloniaView)
- {
- _avaloniaView = avaloniaView;
- TransparencyLevel = WindowTransparencyLevel.None;
- AcrylicCompensationLevels = new AcrylicPlatformCompensationLevels(1, 1, 1);
- _touchDevice = new TouchDevice();
- _penDevice = new PenDevice();
- }
-
- public ulong Timestamp => (ulong)_sw.ElapsedMilliseconds;
-
-
- internal void SetSurface(GRContext context, SKHtmlCanvasInterop.GLInfo glInfo, SKColorType colorType, PixelSize size, double scaling)
- {
- _currentSurface =
- new BlazorSkiaSurface(context, glInfo, colorType, size, scaling, GRSurfaceOrigin.BottomLeft);
- }
-
- internal void SetSurface(SKColorType colorType, PixelSize size, double scaling, Action blitCallback)
- {
- _currentSurface = new BlazorSkiaRasterSurface(colorType, size, scaling, blitCallback);
- }
-
- public void SetClientSize(SKSize size, double dpi)
- {
- var newSize = new Size(size.Width, size.Height);
-
- if (Math.Abs(RenderScaling - dpi) > 0.0001)
- {
- if (_currentSurface is { })
- {
- _currentSurface.Scaling = dpi;
- }
-
- ScalingChanged?.Invoke(dpi);
- }
-
- if (newSize != _clientSize)
- {
- _clientSize = newSize;
-
- if (_currentSurface is { })
- {
- _currentSurface.Size = new PixelSize((int)size.Width, (int)size.Height);
- }
-
- Resized?.Invoke(newSize, PlatformResizeReason.User);
- }
- }
-
- public void RawPointerEvent(
- RawPointerEventType eventType, string pointerType,
- RawPointerPoint p, RawInputModifiers modifiers, long touchPointId)
- {
- if (_inputRoot is { }
- && Input is { } input)
- {
- var device = GetPointerDevice(pointerType);
- var args = device is TouchDevice ?
- new RawTouchEventArgs(device, Timestamp, _inputRoot, eventType, p, modifiers, touchPointId) :
- new RawPointerEventArgs(device, Timestamp, _inputRoot, eventType, p, modifiers)
- {
- RawPointerId = touchPointId
- };
-
- input.Invoke(args);
- }
- }
-
- private IPointerDevice GetPointerDevice(string pointerType)
- {
- return pointerType switch
- {
- "touch" => _touchDevice,
- "pen" => _penDevice,
- _ => MouseDevice
- };
- }
-
- public void RawMouseWheelEvent(Point p, Vector v, RawInputModifiers modifiers)
- {
- if (_inputRoot is { })
- {
- Input?.Invoke(new RawMouseWheelEventArgs(MouseDevice, Timestamp, _inputRoot, p, v, modifiers));
- }
- }
-
- public bool RawKeyboardEvent(RawKeyEventType type, string code, string key, RawInputModifiers modifiers)
- {
- if (Keycodes.KeyCodes.TryGetValue(code, out var avkey))
- {
- if (_inputRoot is { })
- {
- var args = new RawKeyEventArgs(KeyboardDevice, Timestamp, _inputRoot, type, avkey, modifiers);
-
- Input?.Invoke(args);
-
- return args.Handled;
- }
- }
- else if (Keycodes.KeyCodes.TryGetValue(key, out avkey))
- {
- if (_inputRoot is { })
- {
- var args = new RawKeyEventArgs(KeyboardDevice, Timestamp, _inputRoot, type, avkey, modifiers);
-
- Input?.Invoke(args);
-
- return args.Handled;
- }
- }
-
- return false;
- }
-
- public void RawTextEvent(string text)
- {
- if (_inputRoot is { })
- {
- Input?.Invoke(new RawTextInputEventArgs(KeyboardDevice, Timestamp, _inputRoot, text));
- }
- }
-
- public void Dispose()
- {
-
- }
-
- public IRenderer CreateRenderer(IRenderRoot root)
- {
- var loop = AvaloniaLocator.Current.GetRequiredService();
- return new CompositingRenderer(root, new Compositor(loop, null));
- }
-
- public void Invalidate(Rect rect)
- {
- //Console.WriteLine("invalidate rect called");
- }
-
- public void SetInputRoot(IInputRoot inputRoot)
- {
- _inputRoot = inputRoot;
- }
-
- public Point PointToClient(PixelPoint point) => new Point(point.X, point.Y);
-
- public PixelPoint PointToScreen(Point point) => new PixelPoint((int)point.X, (int)point.Y);
-
- public void SetCursor(ICursorImpl? cursor)
- {
- var val = (cursor as CssCursor)?.Value ?? CssCursor.Default;
- if (_currentCursor != val)
- {
- SetCssCursor?.Invoke(val);
- _currentCursor = val;
- }
- }
-
- public IPopupImpl? CreatePopup()
- {
- return null;
- }
-
- public void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel)
- {
-
- }
-
- public Size ClientSize => _clientSize;
- public Size? FrameSize => null;
- public double RenderScaling => _currentSurface?.Scaling ?? 1;
-
- public IEnumerable Surfaces => new object[] { _currentSurface! };
-
- public Action? SetCssCursor { get; set; }
- public Action? Input { get; set; }
- public Action? Paint { get; set; }
- public Action? Resized { get; set; }
- public Action? ScalingChanged { get; set; }
- public Action? TransparencyLevelChanged { get; set; }
- public Action? Closed { get; set; }
- public Action? LostFocus { get; set; }
- public IMouseDevice MouseDevice { get; } = new MouseDevice();
-
- public IKeyboardDevice KeyboardDevice { get; } = BlazorWindowingPlatform.Keyboard;
- public WindowTransparencyLevel TransparencyLevel { get; }
- public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; }
-
- public ITextInputMethodImpl TextInputMethod => _avaloniaView;
-
- public INativeControlHostImpl? NativeControlHost => _avaloniaView.GetNativeControlHostImpl();
- public IStorageProvider StorageProvider => _avaloniaView.GetStorageProvider();
- }
-}
diff --git a/src/Web/Avalonia.Web.Blazor/WinStubs.cs b/src/Web/Avalonia.Web.Blazor/WinStubs.cs
deleted file mode 100644
index 808d1526d3..0000000000
--- a/src/Web/Avalonia.Web.Blazor/WinStubs.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-using Avalonia.Controls;
-using Avalonia.Controls.Platform;
-using Avalonia.Input;
-using Avalonia.Input.Platform;
-using Avalonia.Platform;
-
-#nullable enable
-
-namespace Avalonia.Web.Blazor
-{
- internal class IconLoaderStub : IPlatformIconLoader
- {
- private class IconStub : IWindowIconImpl
- {
- public void Save(Stream outputStream)
- {
-
- }
- }
-
- public IWindowIconImpl LoadIcon(string fileName) => new IconStub();
-
- public IWindowIconImpl LoadIcon(Stream stream) => new IconStub();
-
- public IWindowIconImpl LoadIcon(IBitmapImpl bitmap) => new IconStub();
- }
-
- internal class ScreenStub : IScreenImpl
- {
- public int ScreenCount => 1;
-
- public IReadOnlyList AllScreens { get; } =
- new[] { new Screen(96, new PixelRect(0, 0, 4000, 4000), new PixelRect(0, 0, 4000, 4000), 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);
- }
- }
-}
diff --git a/src/Web/Avalonia.Web.Blazor/WindowingPlatform.cs b/src/Web/Avalonia.Web.Blazor/WindowingPlatform.cs
deleted file mode 100644
index 46c05d8e8c..0000000000
--- a/src/Web/Avalonia.Web.Blazor/WindowingPlatform.cs
+++ /dev/null
@@ -1,106 +0,0 @@
-using Avalonia.Controls.Platform;
-using Avalonia.Input;
-using Avalonia.Input.Platform;
-using Avalonia.Platform;
-using Avalonia.Rendering;
-using Avalonia.Threading;
-
-#nullable enable
-
-namespace Avalonia.Web.Blazor
-{
- public class BlazorWindowingPlatform : IWindowingPlatform, IPlatformSettings, IPlatformThreadingInterface
- {
- private bool _signaled;
- private static KeyboardDevice? s_keyboard;
-
- public IWindowImpl CreateWindow() => throw new NotSupportedException();
-
- IWindowImpl IWindowingPlatform.CreateEmbeddableWindow()
- {
- throw new NotImplementedException();
- }
-
- public ITrayIconImpl? CreateTrayIcon()
- {
- return null;
- }
-
- public static KeyboardDevice Keyboard => s_keyboard ??
- throw new InvalidOperationException("BlazorWindowingPlatform not registered.");
-
- public static void Register()
- {
- var instance = new BlazorWindowingPlatform();
- s_keyboard = new KeyboardDevice();
- AvaloniaLocator.CurrentMutable
- .Bind().ToSingleton()
- .Bind().ToSingleton()
- .Bind().ToConstant(s_keyboard)
- .Bind().ToConstant(instance)
- .Bind().ToConstant(instance)
- .Bind().ToConstant(new RenderLoop())
- .Bind().ToConstant(ManualTriggerRenderTimer.Instance)
- .Bind().ToConstant(instance)
- .Bind().ToSingleton()
- .Bind().ToSingleton();
- }
-
- public Size DoubleClickSize { get; } = new Size(2, 2);
-
- public TimeSpan DoubleClickTime { get; } = TimeSpan.FromMilliseconds(500);
-
- public Size TouchDoubleClickSize => new Size(16, 16);
-
- public TimeSpan TouchDoubleClickTime => DoubleClickTime;
- public void RunLoop(CancellationToken cancellationToken)
- {
- throw new NotSupportedException();
- }
-
- public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action tick)
- {
- return GetRuntimePlatform()
- .StartSystemTimer(interval, () =>
- {
- Dispatcher.UIThread.RunJobs(priority);
- tick();
- });
- }
-
- public void Signal(DispatcherPriority priority)
- {
- if (_signaled)
- return;
-
- _signaled = true;
-
- IDisposable? disp = null;
-
- disp = GetRuntimePlatform()
- .StartSystemTimer(TimeSpan.FromMilliseconds(1),
- () =>
- {
- _signaled = false;
- disp?.Dispose();
-
- Signaled?.Invoke(null);
- });
- }
-
- public bool CurrentThreadIsLoopThread
- {
- get
- {
- return true; // Blazor is single threaded.
- }
- }
-
- public event Action? Signaled;
-
- private static IRuntimePlatform GetRuntimePlatform()
- {
- return AvaloniaLocator.Current.GetRequiredService();
- }
- }
-}
diff --git a/src/Web/Avalonia.Web.Blazor/_Imports.razor b/src/Web/Avalonia.Web.Blazor/_Imports.razor
deleted file mode 100644
index 77285129da..0000000000
--- a/src/Web/Avalonia.Web.Blazor/_Imports.razor
+++ /dev/null
@@ -1 +0,0 @@
-@using Microsoft.AspNetCore.Components.Web
diff --git a/src/Web/Avalonia.Web.Blazor/webapp/build.js b/src/Web/Avalonia.Web.Blazor/webapp/build.js
deleted file mode 100644
index 9d585dcade..0000000000
--- a/src/Web/Avalonia.Web.Blazor/webapp/build.js
+++ /dev/null
@@ -1,16 +0,0 @@
-require("esbuild").build({
- entryPoints: [
- "./modules/Avalonia.ts",
- "./modules/Storage.ts"
- ],
- outdir: "../wwwroot",
- bundle: true,
- minify: true,
- format: "esm",
- target: "es2016",
- platform: "browser",
- sourcemap: "linked",
- loader: {".ts": "ts"}
- })
- .then(() => console.log("⚡ Done"))
- .catch(() => process.exit(1));
\ No newline at end of file
diff --git a/src/Web/Avalonia.Web.Blazor/webapp/modules/Avalonia.ts b/src/Web/Avalonia.Web.Blazor/webapp/modules/Avalonia.ts
deleted file mode 100644
index 369f628a44..0000000000
--- a/src/Web/Avalonia.Web.Blazor/webapp/modules/Avalonia.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-export { DpiWatcher } from "./Avalonia/DpiWatcher"
-export { InputHelper } from "./Avalonia/InputHelper"
-export { FocusHelper } from "./Avalonia/FocusHelper"
-export { NativeControlHost } from "./Avalonia/NativeControlHost"
-export { SizeWatcher } from "./Avalonia/SizeWatcher"
-export { SKHtmlCanvas } from "./Avalonia/SKHtmlCanvas"
-export { CaretHelper } from "./Avalonia/CaretHelper"
diff --git a/src/Web/Avalonia.Web.Blazor/webapp/modules/Avalonia/CaretHelper.ts b/src/Web/Avalonia.Web.Blazor/webapp/modules/Avalonia/CaretHelper.ts
deleted file mode 100644
index 5709854087..0000000000
--- a/src/Web/Avalonia.Web.Blazor/webapp/modules/Avalonia/CaretHelper.ts
+++ /dev/null
@@ -1,149 +0,0 @@
-// Based on https://github.com/component/textarea-caret-position/blob/master/index.js
-export class CaretHelper {
- public static getCaretCoordinates(
- element: HTMLInputElement | HTMLTextAreaElement,
- position: number,
- options?: { debug: boolean }
- ) {
- if (!isBrowser) {
- throw new Error(
- "textarea-caret-position#getCaretCoordinates should only be called in a browser"
- );
- }
-
- const debug = (options && options.debug) || false;
- if (debug) {
- const el = document.querySelector(
- "#input-textarea-caret-position-mirror-div"
- );
- if (el) el.parentNode?.removeChild(el);
- }
-
- // The mirror div will replicate the textarea's style
- const div = document.createElement("div");
- div.id = "input-textarea-caret-position-mirror-div";
- document.body.appendChild(div);
-
- const style = div.style;
- const computed = window.getComputedStyle
- ? window.getComputedStyle(element)
- : ((element as any)["currentStyle"] as CSSStyleDeclaration); // currentStyle for IE < 9
- const isInput = element.nodeName === "INPUT";
-
- // Default textarea styles
- style.whiteSpace = "pre-wrap";
- if (!isInput) style.wordWrap = "break-word"; // only for textarea-s
-
- // Position off-screen
- style.position = "absolute"; // required to return coordinates properly
- if (!debug) style.visibility = "hidden"; // not 'display: none' because we want rendering
-
- // Transfer the element's properties to the div
- properties.forEach((prop: string) => {
- if (isInput && prop === "lineHeight") {
- // Special case for s because text is rendered centered and line height may be != height
- if (computed.boxSizing === "border-box") {
- const height = parseInt(computed.height);
- const outerHeight =
- parseInt(computed.paddingTop) +
- parseInt(computed.paddingBottom) +
- parseInt(computed.borderTopWidth) +
- parseInt(computed.borderBottomWidth);
- const targetHeight = outerHeight + parseInt(computed.lineHeight);
- if (height > targetHeight) {
- style.lineHeight = height - outerHeight + "px";
- } else if (height === targetHeight) {
- style.lineHeight = computed.lineHeight;
- } else {
- style.lineHeight = "0";
- }
- } else {
- style.lineHeight = computed.height;
- }
- } else {
- (style as any)[prop] = (computed as any)[prop];
- }
- });
-
- if (isFirefox) {
- // Firefox lies about the overflow property for textareas: https://bugzilla.mozilla.org/show_bug.cgi?id=984275
- if (element.scrollHeight > parseInt(computed.height))
- style.overflowY = "scroll";
- } else {
- style.overflow = "hidden"; // for Chrome to not render a scrollbar; IE keeps overflowY = 'scroll'
- }
-
- div.textContent = element.value.substring(0, position);
- // The second special handling for input type="text" vs textarea:
- // spaces need to be replaced with non-breaking spaces - http://stackoverflow.com/a/13402035/1269037
- if (isInput) div.textContent = div.textContent.replace(/\s/g, "\u00a0");
-
- const span = document.createElement("span");
- // Wrapping must be replicated *exactly*, including when a long word gets
- // onto the next line, with whitespace at the end of the line before (#7).
- // The *only* reliable way to do that is to copy the *entire* rest of the
- // textarea's content into the created at the caret position.
- // For inputs, just '.' would be enough, but no need to bother.
- span.textContent = element.value.substring(position) || "."; // || because a completely empty faux span doesn't render at all
- div.appendChild(span);
-
- const coordinates = {
- top: span.offsetTop + parseInt(computed["borderTopWidth"]),
- left: span.offsetLeft + parseInt(computed["borderLeftWidth"]),
- height: parseInt(computed["lineHeight"]),
- };
-
- if (debug) {
- span.style.backgroundColor = "#aaa";
- } else {
- document.body.removeChild(div);
- }
-
- return coordinates;
- }
-}
-
-
-var properties = [
- "direction", // RTL support
- "boxSizing",
- "width", // on Chrome and IE, exclude the scrollbar, so the mirror div wraps exactly as the textarea does
- "height",
- "overflowX",
- "overflowY", // copy the scrollbar for IE
-
- "borderTopWidth",
- "borderRightWidth",
- "borderBottomWidth",
- "borderLeftWidth",
- "borderStyle",
-
- "paddingTop",
- "paddingRight",
- "paddingBottom",
- "paddingLeft",
-
- // https://developer.mozilla.org/en-US/docs/Web/CSS/font
- "fontStyle",
- "fontVariant",
- "fontWeight",
- "fontStretch",
- "fontSize",
- "fontSizeAdjust",
- "lineHeight",
- "fontFamily",
-
- "textAlign",
- "textTransform",
- "textIndent",
- "textDecoration", // might not make a difference, but better be safe
-
- "letterSpacing",
- "wordSpacing",
-
- "tabSize",
- "MozTabSize",
-];
-
-const isBrowser = typeof window !== "undefined";
-const isFirefox = isBrowser && (window as any).mozInnerScreenX != null;
diff --git a/src/Web/Avalonia.Web.Blazor/webapp/modules/Avalonia/DpiWatcher.ts b/src/Web/Avalonia.Web.Blazor/webapp/modules/Avalonia/DpiWatcher.ts
deleted file mode 100644
index 06235782f8..0000000000
--- a/src/Web/Avalonia.Web.Blazor/webapp/modules/Avalonia/DpiWatcher.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-export class DpiWatcher {
- static lastDpi: number;
- static timerId: number;
- static callback?: DotNet.DotNetObject;
-
- public static getDpi() {
- return window.devicePixelRatio;
- }
-
- public static start(callback: DotNet.DotNetObject): number {
- //console.info(`Starting DPI watcher with callback ${callback._id}...`);
-
- DpiWatcher.lastDpi = window.devicePixelRatio;
- DpiWatcher.timerId = window.setInterval(DpiWatcher.update, 1000);
- DpiWatcher.callback = callback;
-
- return DpiWatcher.lastDpi;
- }
-
- public static stop() {
- //console.info(`Stopping DPI watcher with callback ${DpiWatcher.callback._id}...`);
-
- window.clearInterval(DpiWatcher.timerId);
-
- DpiWatcher.callback = undefined;
- }
-
- static update() {
- if (!DpiWatcher.callback)
- return;
-
- const currentDpi = window.devicePixelRatio;
- const lastDpi = DpiWatcher.lastDpi;
- DpiWatcher.lastDpi = currentDpi;
-
- if (Math.abs(lastDpi - currentDpi) > 0.001) {
- DpiWatcher.callback.invokeMethod('Invoke', lastDpi, currentDpi);
- }
- }
-}
diff --git a/src/Web/Avalonia.Web.Blazor/webapp/modules/Avalonia/FocusHelper.ts b/src/Web/Avalonia.Web.Blazor/webapp/modules/Avalonia/FocusHelper.ts
deleted file mode 100644
index 96ffee3d53..0000000000
--- a/src/Web/Avalonia.Web.Blazor/webapp/modules/Avalonia/FocusHelper.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-export class FocusHelper {
- public static focus(inputElement: HTMLElement) {
- inputElement.focus();
- }
-
- public static setCursor(inputElement: HTMLInputElement, kind: string) {
- inputElement.style.cursor = kind;
- }
-}
diff --git a/src/Web/Avalonia.Web.Blazor/webapp/modules/Avalonia/InputHelper.ts b/src/Web/Avalonia.Web.Blazor/webapp/modules/Avalonia/InputHelper.ts
deleted file mode 100644
index 1ea85d84e3..0000000000
--- a/src/Web/Avalonia.Web.Blazor/webapp/modules/Avalonia/InputHelper.ts
+++ /dev/null
@@ -1,86 +0,0 @@
-import {CaretHelper} from "./CaretHelper";
-
-export class InputHelper {
- static inputCallback?: DotNet.DotNetObject;
- static compositionCallback?: DotNet.DotNetObject
-
- public static start(inputElement: HTMLInputElement, compositionCallback: DotNet.DotNetObject, inputCallback: DotNet.DotNetObject)
- {
- InputHelper.compositionCallback = compositionCallback;
-
- inputElement.addEventListener('compositionstart', InputHelper.onCompositionEvent);
- inputElement.addEventListener('compositionupdate', InputHelper.onCompositionEvent);
- inputElement.addEventListener('compositionend', InputHelper.onCompositionEvent);
-
- InputHelper.inputCallback = inputCallback;
-
- inputElement.addEventListener('input', InputHelper.onInputEvent);
- }
-
- public static clear(inputElement: HTMLInputElement) {
- inputElement.value = "";
- }
- public static focus(inputElement: HTMLInputElement) {
- inputElement.focus();
- }
-
- public static setCursor(inputElement: HTMLInputElement, kind: string) {
- inputElement.style.cursor = kind;
- }
-
- public static setBounds(inputElement: HTMLInputElement, x: number, y: number, caretWidth: number, caretHeight: number, caret: number) {
- inputElement.style.left = (x).toFixed(0) + "px";
- inputElement.style.top = (y).toFixed(0) + "px";
-
- let {height, left, top} = CaretHelper.getCaretCoordinates(inputElement, caret);
-
- inputElement.style.left = (x - left).toFixed(0) + "px";
- inputElement.style.top = (y - top).toFixed(0) + "px";
- }
-
- public static hide(inputElement: HTMLInputElement) {
- inputElement.style.display = 'none';
- }
-
- public static show(inputElement: HTMLInputElement) {
- inputElement.style.display = 'block';
- }
-
- public static setSurroundingText(inputElement: HTMLInputElement, text: string, start: number, end: number) {
- if (!inputElement) {
- return;
- }
-
- inputElement.value = text;
- inputElement.setSelectionRange(start, end);
- inputElement.style.width = "20px";
- inputElement.style.width = inputElement.scrollWidth + "px";
- }
-
- private static onCompositionEvent(ev: CompositionEvent)
- {
- if(!InputHelper.compositionCallback)
- return;
-
- switch (ev.type)
- {
- case "compositionstart":
- case "compositionupdate":
- case "compositionend":
- InputHelper.compositionCallback.invokeMethod('Invoke', ev.type, ev.data);
- break;
- }
- }
-
- private static onInputEvent(ev: Event) {
- if (!InputHelper.inputCallback)
- return;
-
- var inputEvent = ev as InputEvent;
-
- InputHelper.inputCallback.invokeMethod('Invoke', ev.type, inputEvent.data);
- }
-}
-
-
-
diff --git a/src/Web/Avalonia.Web.Blazor/webapp/modules/Avalonia/NativeControlHost.ts b/src/Web/Avalonia.Web.Blazor/webapp/modules/Avalonia/NativeControlHost.ts
deleted file mode 100644
index 9e5c3843c8..0000000000
--- a/src/Web/Avalonia.Web.Blazor/webapp/modules/Avalonia/NativeControlHost.ts
+++ /dev/null
@@ -1,61 +0,0 @@
-export class NativeControlHost {
- public static CreateDefaultChild(parent: HTMLElement): HTMLElement {
- return document.createElement("div");
- }
-
- // Used to convert ElementReference to JSObjectReference.
- // Is there a better way?
- public static GetReference(element: Element): Element {
- return element;
- }
-
- public static CreateAttachment(): NativeControlHostTopLevelAttachment {
- return new NativeControlHostTopLevelAttachment();
- }
-}
-
-class NativeControlHostTopLevelAttachment {
- _child?: HTMLElement;
- _host?: HTMLElement;
-
- InitializeWithChildHandle(child: HTMLElement) {
- this._child = child;
- this._child.style.position = "absolute";
- }
-
- AttachTo(host: HTMLElement): void {
- if (this._host && this._child) {
- this._host.removeChild(this._child);
- }
-
- this._host = host;
-
- if (this._host && this._child) {
- this._host.appendChild(this._child);
- }
- }
-
- ShowInBounds(x: number, y: number, width: number, height: number): void {
- if (this._child) {
- this._child.style.top = y + "px";
- this._child.style.left = x + "px";
- this._child.style.width = width + "px";
- this._child.style.height = height + "px";
- this._child.style.display = "block";
- }
- }
-
- HideWithSize(width: number, height: number): void {
- if (this._child) {
- this._child.style.width = width + "px";
- this._child.style.height = height + "px";
- this._child.style.display = "none";
- }
- }
-
- ReleaseChild(): void {
- if (this._child) {
- this._child = undefined;
- }
- }
-}
diff --git a/src/Web/Avalonia.Web.Blazor/webapp/modules/Avalonia/SKHtmlCanvas.ts b/src/Web/Avalonia.Web.Blazor/webapp/modules/Avalonia/SKHtmlCanvas.ts
deleted file mode 100644
index e934f74807..0000000000
--- a/src/Web/Avalonia.Web.Blazor/webapp/modules/Avalonia/SKHtmlCanvas.ts
+++ /dev/null
@@ -1,255 +0,0 @@
-// aliases for emscripten
-declare let GL: any;
-declare let GLctx: WebGLRenderingContext;
-declare let Module: EmscriptenModule;
-
-// container for gl info
-type SKGLViewInfo = {
- context: WebGLRenderingContext | WebGL2RenderingContext | undefined;
- fboId: number;
- stencil: number;
- sample: number;
- depth: number;
-}
-
-// alias for a potential skia html canvas
-type SKHtmlCanvasElement = {
- SKHtmlCanvas: SKHtmlCanvas | undefined
-} & HTMLCanvasElement
-
-export class SKHtmlCanvas {
- static elements: Map;
-
- htmlCanvas: HTMLCanvasElement;
- glInfo?: SKGLViewInfo;
- renderFrameCallback: DotNet.DotNetObject;
- renderLoopEnabled: boolean = false;
- renderLoopRequest: number = 0;
- newWidth?: number;
- newHeight?: number;
-
- public static initGL(element: HTMLCanvasElement, elementId: string, callback: DotNet.DotNetObject): SKGLViewInfo | null {
- var view = SKHtmlCanvas.init(true, element, elementId, callback);
- if (!view || !view.glInfo)
- return null;
-
- return view.glInfo;
- }
-
- public static initRaster(element: HTMLCanvasElement, elementId: string, callback: DotNet.DotNetObject): boolean {
- var view = SKHtmlCanvas.init(false, element, elementId, callback);
- if (!view)
- return false;
-
- return true;
- }
-
- static init(useGL: boolean, element: HTMLCanvasElement, elementId: string, callback: DotNet.DotNetObject): SKHtmlCanvas | null {
- var htmlCanvas = element as SKHtmlCanvasElement;
- if (!htmlCanvas) {
- console.error(`No canvas element was provided.`);
- return null;
- }
-
- if (!SKHtmlCanvas.elements)
- SKHtmlCanvas.elements = new Map();
- SKHtmlCanvas.elements.set(elementId, element);
-
- const view = new SKHtmlCanvas(useGL, element, callback);
-
- htmlCanvas.SKHtmlCanvas = view;
-
- return view;
- }
-
- public static deinit(elementId: string) {
- if (!elementId)
- return;
-
- const element = SKHtmlCanvas.elements.get(elementId);
- SKHtmlCanvas.elements.delete(elementId);
-
- const htmlCanvas = element as SKHtmlCanvasElement;
- if (!htmlCanvas || !htmlCanvas.SKHtmlCanvas)
- return;
-
- htmlCanvas.SKHtmlCanvas.deinit();
- htmlCanvas.SKHtmlCanvas = undefined;
- }
-
- public static requestAnimationFrame(element: HTMLCanvasElement, renderLoop?: boolean) {
- const htmlCanvas = element as SKHtmlCanvasElement;
- if (!htmlCanvas || !htmlCanvas.SKHtmlCanvas)
- return;
-
- htmlCanvas.SKHtmlCanvas.requestAnimationFrame(renderLoop);
- }
-
- public static setCanvasSize(element: HTMLCanvasElement, width: number, height: number) {
- const htmlCanvas = element as SKHtmlCanvasElement;
- if (!htmlCanvas || !htmlCanvas.SKHtmlCanvas)
- return;
-
- htmlCanvas.SKHtmlCanvas.setCanvasSize(width, height);
- }
-
- public static setEnableRenderLoop(element: HTMLCanvasElement, enable: boolean) {
- const htmlCanvas = element as SKHtmlCanvasElement;
- if (!htmlCanvas || !htmlCanvas.SKHtmlCanvas)
- return;
-
- htmlCanvas.SKHtmlCanvas.setEnableRenderLoop(enable);
- }
-
- public static putImageData(element: HTMLCanvasElement, pData: number, width: number, height: number) {
- const htmlCanvas = element as SKHtmlCanvasElement;
- if (!htmlCanvas || !htmlCanvas.SKHtmlCanvas)
- return;
-
- htmlCanvas.SKHtmlCanvas.putImageData(pData, width, height);
- }
-
- public constructor(useGL: boolean, element: HTMLCanvasElement, callback: DotNet.DotNetObject) {
- this.htmlCanvas = element;
- this.renderFrameCallback = callback;
-
- if (useGL) {
- const ctx = SKHtmlCanvas.createWebGLContext(this.htmlCanvas);
- if (!ctx) {
- console.error(`Failed to create WebGL context: err ${ctx}`);
- return;
- }
-
- // make current
- GL.makeContextCurrent(ctx);
-
- // read values
- const fbo = GLctx.getParameter(GLctx.FRAMEBUFFER_BINDING);
- this.glInfo = {
- context: ctx,
- fboId: fbo ? fbo.id : 0,
- stencil: GLctx.getParameter(GLctx.STENCIL_BITS),
- sample: 0, // TODO: GLctx.getParameter(GLctx.SAMPLES)
- depth: GLctx.getParameter(GLctx.DEPTH_BITS),
- };
- }
- }
-
- public deinit() {
- this.setEnableRenderLoop(false);
- }
-
- public setCanvasSize(width: number, height: number) {
- this.newWidth = width;
- this.newHeight = height;
-
- if (this.htmlCanvas.width != this.newWidth) {
- this.htmlCanvas.width = this.newWidth;
- }
-
- if (this.htmlCanvas.height != this.newHeight) {
- this.htmlCanvas.height = this.newHeight;
- }
-
- if (this.glInfo) {
- // make current
- GL.makeContextCurrent(this.glInfo.context);
- }
- }
-
- public requestAnimationFrame(renderLoop?: boolean) {
- // optionally update the render loop
- if (renderLoop !== undefined && this.renderLoopEnabled !== renderLoop)
- this.setEnableRenderLoop(renderLoop);
-
- // skip because we have a render loop
- if (this.renderLoopRequest !== 0)
- return;
-
- // add the draw to the next frame
- this.renderLoopRequest = window.requestAnimationFrame(() => {
- if (this.glInfo) {
- // make current
- GL.makeContextCurrent(this.glInfo.context);
- }
-
- if (this.htmlCanvas.width != this.newWidth) {
- this.htmlCanvas.width = this.newWidth || 0;
- }
-
- if (this.htmlCanvas.height != this.newHeight) {
- this.htmlCanvas.height = this.newHeight || 0;
- }
-
- this.renderFrameCallback.invokeMethod('Invoke');
- this.renderLoopRequest = 0;
-
- // we may want to draw the next frame
- if (this.renderLoopEnabled)
- this.requestAnimationFrame();
- });
- }
-
- public setEnableRenderLoop(enable: boolean) {
- this.renderLoopEnabled = enable;
-
- // either start the new frame or cancel the existing one
- if (enable) {
- //console.info(`Enabling render loop with callback ${this.renderFrameCallback._id}...`);
- this.requestAnimationFrame();
- } else if (this.renderLoopRequest !== 0) {
- window.cancelAnimationFrame(this.renderLoopRequest);
- this.renderLoopRequest = 0;
- }
- }
-
- public putImageData(pData: number, width: number, height: number): boolean {
- if (this.glInfo || !pData || width <= 0 || width <= 0)
- return false;
-
- var ctx = this.htmlCanvas.getContext('2d');
- if (!ctx) {
- console.error(`Failed to obtain 2D canvas context.`);
- return false;
- }
-
- // make sure the canvas is scaled correctly for the drawing
- this.htmlCanvas.width = width;
- this.htmlCanvas.height = height;
-
- // set the canvas to be the bytes
- var buffer = new Uint8ClampedArray(Module.HEAPU8.buffer, pData, width * height * 4);
- var imageData = new ImageData(buffer, width, height);
- ctx.putImageData(imageData, 0, 0);
-
- return true;
- }
-
- static createWebGLContext(htmlCanvas: HTMLCanvasElement): WebGLRenderingContext | WebGL2RenderingContext {
- const contextAttributes = {
- alpha: 1,
- depth: 1,
- stencil: 8,
- antialias: 0,
- premultipliedAlpha: 1,
- preserveDrawingBuffer: 0,
- preferLowPowerToHighPerformance: 0,
- failIfMajorPerformanceCaveat: 0,
- majorVersion: 2,
- minorVersion: 0,
- enableExtensionsByDefault: 1,
- explicitSwapControl: 0,
- renderViaOffscreenBackBuffer: 1,
- };
-
- let ctx: WebGLRenderingContext = GL.createContext(htmlCanvas, contextAttributes);
- if (!ctx && contextAttributes.majorVersion > 1) {
- console.warn('Falling back to WebGL 1.0');
- contextAttributes.majorVersion = 1;
- contextAttributes.minorVersion = 0;
- ctx = GL.createContext(htmlCanvas, contextAttributes);
- }
-
- return ctx;
- }
-}
diff --git a/src/Web/Avalonia.Web.Blazor/webapp/modules/Avalonia/SizeWatcher.ts b/src/Web/Avalonia.Web.Blazor/webapp/modules/Avalonia/SizeWatcher.ts
deleted file mode 100644
index 715b252988..0000000000
--- a/src/Web/Avalonia.Web.Blazor/webapp/modules/Avalonia/SizeWatcher.ts
+++ /dev/null
@@ -1,67 +0,0 @@
-type SizeWatcherElement = {
- SizeWatcher: SizeWatcherInstance;
-} & HTMLElement
-
-type SizeWatcherInstance = {
- callback: DotNet.DotNetObject;
-}
-
-export class SizeWatcher {
- static observer: ResizeObserver;
- static elements: Map;
-
- public static observe(element: HTMLElement, elementId: string, callback: DotNet.DotNetObject) {
- if (!element || !callback)
- return;
-
- //console.info(`Adding size watcher observation with callback ${callback._id}...`);
-
- SizeWatcher.init();
-
- const watcherElement = element as SizeWatcherElement;
- watcherElement.SizeWatcher = {
- callback: callback
- };
-
- SizeWatcher.elements.set(elementId, element);
- SizeWatcher.observer.observe(element);
-
- SizeWatcher.invoke(element);
- }
-
- public static unobserve(elementId: string) {
- if (!elementId || !SizeWatcher.observer)
- return;
-
- //console.info('Removing size watcher observation...');
-
- const element = SizeWatcher.elements.get(elementId)!;
-
- SizeWatcher.elements.delete(elementId);
- SizeWatcher.observer.unobserve(element);
- }
-
- static init() {
- if (SizeWatcher.observer)
- return;
-
- //console.info('Starting size watcher...');
-
- SizeWatcher.elements = new Map();
- SizeWatcher.observer = new ResizeObserver((entries) => {
- for (let entry of entries) {
- SizeWatcher.invoke(entry.target);
- }
- });
- }
-
- static invoke(element: Element) {
- const watcherElement = element as SizeWatcherElement;
- const instance = watcherElement.SizeWatcher;
-
- if (!instance || !instance.callback)
- return;
-
- return instance.callback.invokeMethod('Invoke', element.clientWidth, element.clientHeight);
- }
-}
diff --git a/src/Web/Avalonia.Web.Blazor/webapp/modules/Storage.ts b/src/Web/Avalonia.Web.Blazor/webapp/modules/Storage.ts
deleted file mode 100644
index 2c4654e81b..0000000000
--- a/src/Web/Avalonia.Web.Blazor/webapp/modules/Storage.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { StorageProvider } from "./Storage/StorageProvider"
diff --git a/src/Web/Avalonia.Web.Blazor/webapp/modules/Storage/IndexedDbWrapper.ts b/src/Web/Avalonia.Web.Blazor/webapp/modules/Storage/IndexedDbWrapper.ts
deleted file mode 100644
index 2eaa8de2fe..0000000000
--- a/src/Web/Avalonia.Web.Blazor/webapp/modules/Storage/IndexedDbWrapper.ts
+++ /dev/null
@@ -1,79 +0,0 @@
-class InnerDbConnection {
- constructor(private database: IDBDatabase) { }
-
- private openStore(store: string, mode: IDBTransactionMode): IDBObjectStore {
- const tx = this.database.transaction(store, mode);
- return tx.objectStore(store);
- }
-
- public put(store: string, obj: any, key?: IDBValidKey): Promise {
- const os = this.openStore(store, "readwrite");
-
- return new Promise((resolve, reject) => {
- const response = os.put(obj, key);
- response.onsuccess = () => {
- resolve(response.result);
- };
- response.onerror = () => {
- reject(response.error);
- };
- });
- }
-
- public get(store: string, key: IDBValidKey): any {
- const os = this.openStore(store, "readonly");
-
- return new Promise((resolve, reject) => {
- const response = os.get(key);
- response.onsuccess = () => {
- resolve(response.result);
- };
- response.onerror = () => {
- reject(response.error);
- };
- });
- }
-
- public delete(store: string, key: IDBValidKey): Promise {
- const os = this.openStore(store, "readwrite");
-
- return new Promise((resolve, reject) => {
- const response = os.delete(key);
- response.onsuccess = () => {
- resolve();
- };
- response.onerror = () => {
- reject(response.error);
- };
- });
- }
-
- public close() {
- this.database.close();
- }
-}
-
-export class IndexedDbWrapper {
- constructor(private databaseName: string, private objectStores: [string]) {
- }
-
- public connect(): Promise {
- const conn = window.indexedDB.open(this.databaseName, 1);
-
- conn.onupgradeneeded = event => {
- const db = (>event.target).result;
- this.objectStores.forEach(store => {
- db.createObjectStore(store);
- });
- };
-
- return new Promise((resolve, reject) => {
- conn.onsuccess = event => {
- resolve(new InnerDbConnection((>event.target).result));
- };
- conn.onerror = event => {
- reject((>event.target).error);
- };
- });
- }
-}
diff --git a/src/Web/Avalonia.Web.Blazor/webapp/modules/Storage/StorageProvider.ts b/src/Web/Avalonia.Web.Blazor/webapp/modules/Storage/StorageProvider.ts
deleted file mode 100644
index 896e174e43..0000000000
--- a/src/Web/Avalonia.Web.Blazor/webapp/modules/Storage/StorageProvider.ts
+++ /dev/null
@@ -1,204 +0,0 @@
-import { IndexedDbWrapper } from "./IndexedDbWrapper";
-
-declare global {
- type WellKnownDirectory = "desktop" | "documents" | "downloads" | "music" | "pictures" | "videos";
- type StartInDirectory = WellKnownDirectory | FileSystemHandle;
- interface OpenFilePickerOptions {
- startIn?: StartInDirectory
- }
- interface SaveFilePickerOptions {
- startIn?: StartInDirectory
- }
-}
-
-const fileBookmarksStore: string = "fileBookmarks";
-const avaloniaDb = new IndexedDbWrapper("AvaloniaDb", [
- fileBookmarksStore
-]);
-
-class StorageItem {
- constructor(public handle: FileSystemHandle, private bookmarkId?: string) { }
-
- public getName(): string {
- return this.handle.name
- }
-
- public getKind(): string {
- return this.handle.kind;
- }
-
- public async openRead(): Promise {
- if (!(this.handle instanceof FileSystemFileHandle)) {
- throw new Error("StorageItem is not a file");
- }
-
- await this.verityPermissions('read');
-
- const file = await this.handle.getFile();
- return file;
- }
-
- public async openWrite(): Promise {
- if (!(this.handle instanceof FileSystemFileHandle)) {
- throw new Error("StorageItem is not a file");
- }
-
- await this.verityPermissions('readwrite');
-
- return await this.handle.createWritable({ keepExistingData: true });
- }
-
- public async getProperties(): Promise<{ Size: number, LastModified: number, Type: string } | null> {
- const file = this.handle instanceof FileSystemFileHandle
- && await this.handle.getFile();
-
- if (!file) {
- return null;
- }
-
- return {
- Size: file.size,
- LastModified: file.lastModified,
- Type: file.type
- }
- }
-
- public async getItems(): Promise {
- if (this.handle.kind !== "directory"){
- return new StorageItems([]);
- }
-
- const items: StorageItem[] = [];
- for await (const [key, value] of (this.handle as any).entries()) {
- items.push(new StorageItem(value));
- }
- return new StorageItems(items);
- }
-
- private async verityPermissions(mode: FileSystemPermissionMode): Promise {
- if (await this.handle.queryPermission({ mode }) === 'granted') {
- return;
- }
-
- if (await this.handle.requestPermission({ mode }) === "denied") {
- throw new Error("Read permissions denied");
- }
- }
-
- public async saveBookmark(): Promise {
- // If file was previously bookmarked, just return old one.
- if (this.bookmarkId) {
- return this.bookmarkId;
- }
-
- const connection = await avaloniaDb.connect();
- try {
- const key = await connection.put(fileBookmarksStore, this.handle, this.generateBookmarkId());
- return key;
- }
- finally {
- connection.close();
- }
- }
-
- public async deleteBookmark(): Promise {
- if (!this.bookmarkId) {
- return;
- }
-
- const connection = await avaloniaDb.connect();
- try {
- const key = await connection.delete(fileBookmarksStore, this.bookmarkId);
- }
- finally {
- connection.close();
- }
- }
-
- private generateBookmarkId(): string {
- return Date.now().toString(36) + Math.random().toString(36).substring(2);
- }
-}
-
-class StorageItems {
- constructor(private items: StorageItem[]) { }
-
- public count(): number {
- return this.items.length;
- }
-
- public at(index: number): StorageItem {
- return this.items[index];
- }
-}
-
-export class StorageProvider {
-
- public static canOpen(): boolean {
- return typeof window.showOpenFilePicker !== 'undefined';
- }
-
- public static canSave(): boolean {
- return typeof window.showSaveFilePicker !== 'undefined';
- }
-
- public static canPickFolder(): boolean {
- return typeof window.showDirectoryPicker !== 'undefined';
- }
-
- public static async selectFolderDialog(
- startIn: StorageItem | null)
- : Promise {
-
- // 'Picker' API doesn't accept "null" as a parameter, so it should be set to undefined.
- const options: DirectoryPickerOptions = {
- startIn: (startIn?.handle || undefined)
- };
-
- const handle = await window.showDirectoryPicker(options);
- return new StorageItem(handle);
- }
-
- public static async openFileDialog(
- startIn: StorageItem | null, multiple: boolean,
- types: FilePickerAcceptType[] | null, excludeAcceptAllOption: boolean)
- : Promise {
-
- const options: OpenFilePickerOptions = {
- startIn: (startIn?.handle || undefined),
- multiple,
- excludeAcceptAllOption,
- types: (types || undefined)
- };
-
- const handles = await window.showOpenFilePicker(options);
- return new StorageItems(handles.map((handle: FileSystemHandle) => new StorageItem(handle)));
- }
-
- public static async saveFileDialog(
- startIn: StorageItem | null, suggestedName: string | null,
- types: FilePickerAcceptType[] | null, excludeAcceptAllOption: boolean)
- : Promise {
-
- const options: SaveFilePickerOptions = {
- startIn: (startIn?.handle || undefined),
- suggestedName: (suggestedName || undefined),
- excludeAcceptAllOption,
- types: (types || undefined)
- };
-
- const handle = await window.showSaveFilePicker(options);
- return new StorageItem(handle);
- }
-
- public static async openBookmark(key: string): Promise {
- const connection = await avaloniaDb.connect();
- try {
- const handle = await connection.get(fileBookmarksStore, key);
- return handle && new StorageItem(handle, key);
- }
- finally {
- connection.close();
- }
- }
-}
diff --git a/src/Web/Avalonia.Web.Blazor/webapp/package.json b/src/Web/Avalonia.Web.Blazor/webapp/package.json
deleted file mode 100644
index 27e15b0abd..0000000000
--- a/src/Web/Avalonia.Web.Blazor/webapp/package.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "name": "avalonia.web",
- "scripts": {
- "prebuild": "tsc -noEmit",
- "build": "node build.js"
- },
- "devDependencies": {
- "@types/emscripten": "^1.39.6",
- "@types/wicg-file-system-access": "^2020.9.5",
- "typescript": "^4.7.4",
- "esbuild": "^0.15.7"
- }
-}
diff --git a/src/Web/Avalonia.Web.Blazor/webapp/tsconfig.json b/src/Web/Avalonia.Web.Blazor/webapp/tsconfig.json
deleted file mode 100644
index 4e90bc230d..0000000000
--- a/src/Web/Avalonia.Web.Blazor/webapp/tsconfig.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "compilerOptions": {
- "target": "es2016",
- "strict": true,
- "sourceMap": true,
- "outDir": "../wwwroot",
- "noEmitOnError": true,
- "isolatedModules": true, // we need it for esbuild
- "lib": [
- "dom",
- "es2016",
- "esnext.asynciterable"
- ]
- },
- "exclude": [
- "node_modules"
- ]
-}
diff --git a/src/Web/Avalonia.Web.Blazor/webapp/types/dotnet/index.d.ts b/src/Web/Avalonia.Web.Blazor/webapp/types/dotnet/index.d.ts
deleted file mode 100644
index 932dfa1e1f..0000000000
--- a/src/Web/Avalonia.Web.Blazor/webapp/types/dotnet/index.d.ts
+++ /dev/null
@@ -1,56 +0,0 @@
-// Type definitions for non-npm package @blazor/javascript-interop 3.1
-// Project: https://docs.microsoft.com/en-us/aspnet/core/blazor/javascript-interop?view=aspnetcore-3.1
-// Definitions by: Piotr Błażejewicz (Peter Blazejewicz)
-// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
-// Minimum TypeScript Version: 3.0
-
-// Here be dragons!
-// This is community-maintained definition file intended to ease the process of developing
-// high quality JavaScript interop code to be used in Blazor application from your C# .NET code.
-// Could be removed without a notice in case official definition types ships with Blazor itself.
-
-// tslint:disable:no-unnecessary-generics
-
-declare namespace DotNet {
- /**
- * Invokes the specified .NET public method synchronously. Not all hosting scenarios support
- * synchronous invocation, so if possible use invokeMethodAsync instead.
- *
- * @param assemblyName The short name (without key/version or .dll extension) of the .NET assembly containing the method.
- * @param methodIdentifier The identifier of the method to invoke. The method must have a [JSInvokable] attribute specifying this identifier.
- * @param args Arguments to pass to the method, each of which must be JSON-serializable.
- * @returns The result of the operation.
- */
- function invokeMethod(assemblyName: string, methodIdentifier: string, ...args: any[]): T;
- /**
- * Invokes the specified .NET public method asynchronously.
- *
- * @param assemblyName The short name (without key/version or .dll extension) of the .NET assembly containing the method.
- * @param methodIdentifier The identifier of the method to invoke. The method must have a [JSInvokable] attribute specifying this identifier.
- * @param args Arguments to pass to the method, each of which must be JSON-serializable.
- * @returns A promise representing the result of the operation.
- */
- function invokeMethodAsync(assemblyName: string, methodIdentifier: string, ...args: any[]): Promise;
- /**
- * Represents the .NET instance passed by reference to JavaScript.
- */
- interface DotNetObject {
- /**
- * Invokes the specified .NET instance public method synchronously. Not all hosting scenarios support
- * synchronous invocation, so if possible use invokeMethodAsync instead.
- *
- * @param methodIdentifier The identifier of the method to invoke. The method must have a [JSInvokable] attribute specifying this identifier.
- * @param args Arguments to pass to the method, each of which must be JSON-serializable.
- * @returns The result of the operation.
- */
- invokeMethod(methodIdentifier: string, ...args: any[]): T;
- /**
- * Invokes the specified .NET instance public method asynchronously.
- *
- * @param methodIdentifier The identifier of the method to invoke. The method must have a [JSInvokable] attribute specifying this identifier.
- * @param args Arguments to pass to the method, each of which must be JSON-serializable.
- * @returns A promise representing the result of the operation.
- */
- invokeMethodAsync(methodIdentifier: string, ...args: any[]): Promise;
- }
-}
diff --git a/src/Web/Avalonia.Web.Sample/Avalonia.Web.Sample.csproj b/src/Web/Avalonia.Web.Sample/Avalonia.Web.Sample.csproj
deleted file mode 100644
index 13aad8c13e..0000000000
--- a/src/Web/Avalonia.Web.Sample/Avalonia.Web.Sample.csproj
+++ /dev/null
@@ -1,41 +0,0 @@
-
-