Browse Source

Merge branch 'master' into screen-updates

pull/9133/head
robloo 3 years ago
parent
commit
9382f364c6
  1. 12
      .editorconfig
  2. 39
      Avalonia.sln
  3. 4
      NOTICE.md
  4. 15
      azure-pipelines-integrationtests.yml
  5. 12
      azure-pipelines.yml
  6. 5
      build/NetAnalyzers.props
  7. 13
      dirs.proj
  8. 4
      global.json
  9. 3
      packages/Avalonia/AvaloniaBuildTasks.targets
  10. 12
      samples/ControlCatalog.Android/MainActivity.cs
  11. 13
      samples/ControlCatalog.Android/SplashActivity.cs
  12. 0
      samples/ControlCatalog.Blazor.Web/App.razor
  13. 17
      samples/ControlCatalog.Blazor.Web/App.razor.cs
  14. 29
      samples/ControlCatalog.Blazor.Web/ControlCatalog.Blazor.Web.csproj
  15. 0
      samples/ControlCatalog.Blazor.Web/Pages/Index.razor
  16. 29
      samples/ControlCatalog.Blazor.Web/Program.cs
  17. 0
      samples/ControlCatalog.Blazor.Web/Properties/launchSettings.json
  18. 0
      samples/ControlCatalog.Blazor.Web/Shared/MainLayout.razor
  19. 3
      samples/ControlCatalog.Blazor.Web/_Imports.razor
  20. 0
      samples/ControlCatalog.Blazor.Web/wwwroot/css/app.css
  21. 0
      samples/ControlCatalog.Blazor.Web/wwwroot/favicon.ico
  22. 0
      samples/ControlCatalog.Blazor.Web/wwwroot/index.html
  23. 1
      samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
  24. 20
      samples/ControlCatalog.Web/App.razor.cs
  25. 68
      samples/ControlCatalog.Web/ControlCatalog.Web.csproj
  26. 32
      samples/ControlCatalog.Web/EmbedSample.Browser.cs
  27. 0
      samples/ControlCatalog.Web/Logo.svg
  28. 37
      samples/ControlCatalog.Web/Program.cs
  29. 6
      samples/ControlCatalog.Web/Roots.xml
  30. 23
      samples/ControlCatalog.Web/app.css
  31. 0
      samples/ControlCatalog.Web/embed.js
  32. 0
      samples/ControlCatalog.Web/favicon.ico
  33. 2
      samples/ControlCatalog.Web/index.html
  34. 4
      samples/ControlCatalog.Web/main.js
  35. 0
      samples/ControlCatalog.Web/runtimeconfig.template.json
  36. 8
      samples/ControlCatalog/Pages/ColorPickerPage.xaml
  37. 16
      samples/ControlCatalog/Pages/ColorPickerPage.xaml.cs
  38. 24
      samples/ControlCatalog/Pages/DateTimePickerPage.xaml
  39. 4
      samples/MobileSandbox.Android/MainActivity.cs
  40. 4
      samples/MobileSandbox.Android/SplashActivity.cs
  41. 4
      samples/RenderDemo/Pages/GlyphRunPage.xaml.cs
  42. 105
      src/Android/Avalonia.Android/AvaloniaActivity.cs
  43. 70
      src/Android/Avalonia.Android/AvaloniaMainActivity.cs
  44. 34
      src/Android/Avalonia.Android/AvaloniaSplashActivity.cs
  45. 3
      src/Android/Avalonia.Android/AvaloniaView.cs
  46. 11
      src/Android/Avalonia.Android/AvaloniaViewModel.cs
  47. 11
      src/Android/Avalonia.Android/IActivityResultHandler.cs
  48. 6
      src/Android/Avalonia.Android/OpenGL/GlPlatformSurface.cs
  49. 13
      src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
  50. 6
      src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidMotionEventsHelper.cs
  51. 15
      src/Android/Avalonia.Android/Platform/Storage/AndroidStorageProvider.cs
  52. 26
      src/Android/Avalonia.Android/SingleViewLifetime.cs
  53. 5
      src/Avalonia.Base/Avalonia.Base.csproj
  54. 2
      src/Avalonia.Base/AvaloniaProperty.cs
  55. 2
      src/Avalonia.Base/AvaloniaPropertyRegistry.cs
  56. 12
      src/Avalonia.Base/Compatibility/StringCompatibilityExtensions.cs
  57. 2
      src/Avalonia.Base/Input/Cursor.cs
  58. 12
      src/Avalonia.Base/Media/FontManager.cs
  59. 58
      src/Avalonia.Base/Media/FontMetrics.cs
  60. 95
      src/Avalonia.Base/Media/GlyphRun.cs
  61. 125
      src/Avalonia.Base/Media/GlyphTypeface.cs
  62. 68
      src/Avalonia.Base/Media/IGlyphTypeface.cs
  63. 2
      src/Avalonia.Base/Media/PreciseEllipticArcHelper.cs
  64. 16
      src/Avalonia.Base/Media/TextDecoration.cs
  65. 6
      src/Avalonia.Base/Media/TextFormatting/ShapedBuffer.cs
  66. 9
      src/Avalonia.Base/Media/TextFormatting/ShapedTextCharacters.cs
  67. 26
      src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs
  68. 24
      src/Avalonia.Base/Media/TextFormatting/TextMetrics.cs
  69. 4
      src/Avalonia.Base/Media/TextFormatting/TextShaperOptions.cs
  70. 2
      src/Avalonia.Base/Media/TextFormatting/Unicode/BinaryReaderExtensions.cs
  71. 7
      src/Avalonia.Base/Media/TextFormatting/Unicode/Codepoint.cs
  72. 2
      src/Avalonia.Base/Media/TextFormatting/Unicode/GraphemeBreak.cs
  73. 2
      src/Avalonia.Base/Media/TextFormatting/Unicode/LineBreak.cs
  74. 2
      src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeTrie.cs
  75. 2
      src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeTrieBuilder.Constants.cs
  76. 2
      src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeTrieBuilder.cs
  77. 2
      src/Avalonia.Base/Media/Typeface.cs
  78. 2
      src/Avalonia.Base/Media/UnicodeRange.cs
  79. 2
      src/Avalonia.Base/Platform/IFontManagerImpl.cs
  80. 22
      src/Avalonia.Base/Platform/IGlyphRunBuffer.cs
  81. 37
      src/Avalonia.Base/Platform/IPlatformRenderInterface.cs
  82. 2
      src/Avalonia.Base/Rendering/Composition/Server/FpsCounter.cs
  83. 2
      src/Avalonia.Base/Utilities/BinarySearchExtension.cs
  84. 2
      src/Avalonia.Base/Utilities/IdentifierParser.cs
  85. 2
      src/Avalonia.Base/Utilities/MathUtilities.cs
  86. 2
      src/Avalonia.Base/Utilities/ReadOnlySlice.cs
  87. 5
      src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
  88. 5
      src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs
  89. 6
      src/Avalonia.Build.Tasks/GenerateAvaloniaResourcesTask.cs
  90. 3
      src/Avalonia.Build.Tasks/Program.cs
  91. 32
      src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.Helpers.cs
  92. 17
      src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
  93. 781
      src/Avalonia.Controls.ColorPicker/ColorPalettes/FlatColorPalette.cs
  94. 150
      src/Avalonia.Controls.ColorPicker/ColorPalettes/FlatHalfColorPalette.cs
  95. 577
      src/Avalonia.Controls.ColorPicker/ColorPalettes/MaterialColorPalette.cs
  96. 150
      src/Avalonia.Controls.ColorPicker/ColorPalettes/MaterialHalfColorPalette.cs
  97. 229
      src/Avalonia.Controls.ColorPicker/ColorPalettes/SixteenColorPalette.cs
  98. 33
      src/Avalonia.Controls.ColorPicker/ColorPicker/ColorPicker.cs
  99. 53
      src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs
  100. 24
      src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs

12
.editorconfig

@ -134,6 +134,18 @@ 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
# CA1825: Avoid zero-length array allocations
dotnet_diagnostic.CA1825.severity = warning
# CA1821: Remove empty finalizers
dotnet_diagnostic.CA1821.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

39
Avalonia.sln

@ -90,6 +90,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 +103,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 +201,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 +217,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 +483,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 +515,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 +531,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 +582,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 +590,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 +597,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}

4
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.
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

15
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'

12
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'

5
build/NetAnalyzers.props

@ -0,0 +1,5 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
</PropertyGroup>
</Project>

13
dirs.proj

@ -9,21 +9,18 @@
<ProjectReference Remove="**/*.shproj" />
<ProjectReference Remove="src/Markup/Avalonia.Markup.Xaml/PortableXaml/**/*.*proj" />
<ProjectReference Remove="src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github/**/*.*proj" />
<ProjectReference Remove="tests/Avalonia.ReactiveUI.Events.UnitTests/Avalonia.ReactiveUI.Events.UnitTests.csproj" />
<ProjectReference Remove="samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj" />
<ProjectReference Remove="samples/MobileSandbox.iOS/MobileSandbox.iOS.csproj" />
<ProjectReference Remove="samples/ControlCatalog.iOS.Legacy/ControlCatalog.iOS.Legacy.csproj" />
<ProjectReference Remove="samples/ControlCatalog.Android/ControlCatalog.Android.csproj" />
<ProjectReference Remove="samples/MobileSandbox.Android/MobileSandbox.Android.csproj" />
<ProjectReference Remove="src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj" />
<!-- Exclude iOS, Android and Web samples from build -->
<ProjectReference Remove="samples/*.iOS/*.csproj" />
<ProjectReference Remove="samples/*.Android/*.csproj" />
<ProjectReference Remove="samples/*.Web/*.csproj" />
</ItemGroup>
<ItemGroup Condition="!$([MSBuild]::IsOsPlatform('Windows')) OR '$(MSBuildRuntimeType)' != 'Full'">
<ProjectReference Remove="src/Windows/Avalonia.Win32.Interop/Avalonia.Win32.Interop.csproj" />
<ProjectReference Remove="samples/interop/**/*.*proj" />
<ProjectReference Remove="samples/ControlCatalog.Desktop/*.*proj" />
</ItemGroup>
<!-- Build android and iOS projects only on Windows, where we have installed android workload -->
<!-- Build android and iOS projects only on Windows, where we have installed android workload -->
<ItemGroup Condition="!$([MSBuild]::IsOsPlatform('Windows'))">
<ProjectReference Remove="src/Android/**/*.*proj" />
<ProjectReference Remove="src/iOS/**/*.*proj" />

4
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",

3
packages/Avalonia/AvaloniaBuildTasks.targets

@ -3,7 +3,6 @@
<_AvaloniaUseExternalMSBuild>$(AvaloniaUseExternalMSBuild)</_AvaloniaUseExternalMSBuild>
<_AvaloniaUseExternalMSBuild Condition="'$(_AvaloniaForceInternalMSBuild)' == 'true'">false</_AvaloniaUseExternalMSBuild>
<AvaloniaXamlReportImportance Condition="'$(AvaloniaXamlReportImportance)' == ''">low</AvaloniaXamlReportImportance>
<_AvaloniaPatchComInterop Condition="'$(_AvaloniaPatchComInterop)' == ''">false</_AvaloniaPatchComInterop>
<_AvaloniaSkipXamlCompilation Condition="'$(_AvaloniaSkipXamlCompilation)' == ''">false</_AvaloniaSkipXamlCompilation>
</PropertyGroup>
@ -71,7 +70,6 @@
Output="$(AvaloniaResourcesTemporaryFilePath)"
Root="$(MSBuildProjectDirectory)"
Resources="@(AvaloniaResource)"
EmbeddedResources="@(EmbeddedResources)"
ReportImportance="$(AvaloniaXamlReportImportance)"/>
<Exec
Condition="'$(_AvaloniaUseExternalMSBuild)' == 'true'"
@ -106,7 +104,6 @@
AssemblyOriginatorKeyFile="$(AssemblyOriginatorKeyFile)"
SignAssembly="$(SignAssembly)"
DelaySign="$(DelaySign)"
EnableComInteropPatching="$(_AvaloniaPatchComInterop)"
SkipXamlCompilation="$(_AvaloniaSkipXamlCompilation)"
DebuggerLaunch="$(AvaloniaXamlIlDebuggerLaunch)"
/>

12
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<App>
[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();
});
}
}
}

13
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<App>
{
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);

0
samples/ControlCatalog.Web/App.razor → samples/ControlCatalog.Blazor.Web/App.razor

17
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<ControlCatalog.App>()
.UseBlazor()
// .With(new SkiaOptions { CustomGpuFactory = null }) // uncomment to disable GPU/GL rendering
.SetupWithSingleViewLifetime();
base.OnParametersSet();
}
}

29
samples/ControlCatalog.Blazor.Web/ControlCatalog.Blazor.Web.csproj

@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<RuntimeIdentifier>browser-wasm</RuntimeIdentifier>
<Nullable>enable</Nullable>
<EmccTotalMemory>16777216</EmccTotalMemory>
<BlazorEnableTimeZoneSupport>false</BlazorEnableTimeZoneSupport>
<BlazorWebAssemblyPreserveCollationData>false</BlazorWebAssemblyPreserveCollationData>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="7.0.0-rc.1.22427.2" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="7.0.0-rc.1.22427.2" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Skia\Avalonia.Skia\Avalonia.Skia.csproj" />
<ProjectReference Include="..\..\src\Web\Avalonia.Web.Blazor\Avalonia.Web.Blazor.csproj" />
<ProjectReference Include="..\ControlCatalog\ControlCatalog.csproj" />
</ItemGroup>
<Import Project="..\..\build\ReferenceCoreLibraries.props" />
<Import Project="..\..\build\BuildTargets.targets" />
<Import Project="..\..\src\Web\Avalonia.Web\Avalonia.Web.props" />
<Import Project="..\..\src\Web\Avalonia.Web\Avalonia.Web.targets" />
</Project>

0
samples/ControlCatalog.Web/Pages/Index.razor → samples/ControlCatalog.Blazor.Web/Pages/Index.razor

29
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>("#app");
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
return builder;
}
}

0
samples/ControlCatalog.Web/Properties/launchSettings.json → samples/ControlCatalog.Blazor.Web/Properties/launchSettings.json

0
samples/ControlCatalog.Web/Shared/MainLayout.razor → samples/ControlCatalog.Blazor.Web/Shared/MainLayout.razor

3
samples/ControlCatalog.Web/_Imports.razor → 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

0
samples/ControlCatalog.Web/wwwroot/css/app.css → samples/ControlCatalog.Blazor.Web/wwwroot/css/app.css

0
samples/ControlCatalog.Web/wwwroot/favicon.ico → samples/ControlCatalog.Blazor.Web/wwwroot/favicon.ico

Before

Width:  |  Height:  |  Size: 172 KiB

After

Width:  |  Height:  |  Size: 172 KiB

0
samples/ControlCatalog.Web/wwwroot/index.html → samples/ControlCatalog.Blazor.Web/wwwroot/index.html

1
samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj

@ -5,6 +5,7 @@
<TargetFramework>net6.0</TargetFramework>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<RuntimeFrameworkVersion>6.0.9</RuntimeFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(RunNativeAotCompilation)' == 'true'">

20
samples/ControlCatalog.Web/App.razor.cs

@ -1,20 +0,0 @@
using Avalonia;
using Avalonia.Web.Blazor;
namespace ControlCatalog.Web;
public partial class App
{
protected override void OnParametersSet()
{
WebAppBuilder.Configure<ControlCatalog.App>()
.AfterSetup(_ =>
{
ControlCatalog.Pages.EmbedSample.Implementation = new EmbedSampleWeb();
})
.With(new SkiaOptions { CustomGpuFactory = null }) // uncomment to disable GPU/GL rendering
.SetupWithSingleViewLifetime();
base.OnParametersSet();
}
}

68
samples/ControlCatalog.Web/ControlCatalog.Web.csproj

@ -1,57 +1,45 @@
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<!--Temporal hack that fixes compilation in VS-->
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
<EmccTotalMemory>16777216</EmccTotalMemory>
<BlazorEnableTimeZoneSupport>false</BlazorEnableTimeZoneSupport>
<BlazorWebAssemblyPreserveCollationData>false</BlazorWebAssemblyPreserveCollationData>
<TargetFramework>net7.0</TargetFramework>
<RuntimeIdentifier>browser-wasm</RuntimeIdentifier>
<WasmMainJSPath>main.js</WasmMainJSPath>
<OutputType>Exe</OutputType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<MSBuildEnableWorkloadResolver>true</MSBuildEnableWorkloadResolver>
<WasmBuildNative>true</WasmBuildNative>
<EmccFlags>-sVERBOSE -sERROR_ON_UNDEFINED_SYMBOLS=0</EmccFlags>
</PropertyGroup>
<!-- In debug, make builds faster by reducing optimizations -->
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<WasmNativeStrip>false</WasmNativeStrip>
<EmccCompileOptimizationFlag>-O1</EmccCompileOptimizationFlag>
<RunAOTCompilation>false</RunAOTCompilation>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<Optimize>true</Optimize>
<WasmNativeStrip>true</WasmNativeStrip>
<PropertyGroup Condition="'$(Configuration)'=='Release'">
<RunAOTCompilation>true</RunAOTCompilation>
<PublishTrimmed>true</PublishTrimmed>
<TrimMode>full</TrimMode>
<WasmBuildNative>true</WasmBuildNative>
<InvariantGlobalization>true</InvariantGlobalization>
<WasmEnableSIMD>true</WasmEnableSIMD>
<EmccCompileOptimizationFlag>-O3</EmccCompileOptimizationFlag>
<EmccLinkOptimizationFlag>-O3</EmccLinkOptimizationFlag>
<RunAOTCompilation>false</RunAOTCompilation>
<DebuggerSupport>false</DebuggerSupport>
<EnableUnsafeBinaryFormatterSerialization>false</EnableUnsafeBinaryFormatterSerialization>
<EnableUnsafeUTF7Encoding>false</EnableUnsafeUTF7Encoding>
<EventSourceSupport>false</EventSourceSupport>
<HttpActivityPropagationSupport>false</HttpActivityPropagationSupport>
<InvariantGlobalization>true</InvariantGlobalization>
<MetadataUpdaterSupport>false</MetadataUpdaterSupport>
<UseNativeHttpHandler>true</UseNativeHttpHandler>
<UseSystemResourceKeys>true</UseSystemResourceKeys>
<PublishTrimmed>true</PublishTrimmed>
<TrimMode>link</TrimMode>
<TrimmerRemoveSymbols>true</TrimmerRemoveSymbols>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="6.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="6.0.0" PrivateAssets="all" />
<TrimmerRootDescriptor Include="Roots.xml" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Skia\Avalonia.Skia\Avalonia.Skia.csproj" />
<ProjectReference Include="..\..\src\Web\Avalonia.Web.Blazor\Avalonia.Web.Blazor.csproj" />
<ProjectReference Include="..\..\src\Web\Avalonia.Web\Avalonia.Web.csproj" />
<ProjectReference Include="..\ControlCatalog\ControlCatalog.csproj" />
</ItemGroup>
<Import Project="..\..\build\ReferenceCoreLibraries.props" />
<Import Project="..\..\build\BuildTargets.targets" />
<Import Project="..\..\src\Web\Avalonia.Web.Blazor\Avalonia.Web.Blazor.targets" />
<Import Project="..\..\src\Web\Avalonia.Web.Blazor\Avalonia.Web.Blazor.CompilationTuning.props" />
<ItemGroup>
<WasmExtraFilesToDeploy Include="index.html" />
<WasmExtraFilesToDeploy Include="main.js" />
<WasmExtraFilesToDeploy Include="embed.js" />
<WasmExtraFilesToDeploy Include="favicon.ico" />
<WasmExtraFilesToDeploy Include="Logo.svg" />
<WasmExtraFilesToDeploy Include="app.css" />
</ItemGroup>
<Import Project="..\..\src\Web\Avalonia.Web\Avalonia.Web.props" />
<Import Project="..\..\src\Web\Avalonia.Web\Avalonia.Web.targets" />
</Project>

32
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<IPlatformHandle> createDefault)
{
var runtime = AvaloniaLocator.Current.GetRequiredService<IJSInProcessRuntime>();
if (isSecond)
{
var iframe = runtime.Invoke<IJSInProcessObjectReference>("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<IJSInProcessObjectReference>("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);
}

0
src/Web/Avalonia.Web.Sample/Logo.svg → samples/ControlCatalog.Web/Logo.svg

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

37
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>("#app");
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
return builder;
}
public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure<App>();
}

6
samples/ControlCatalog.Web/Roots.xml

@ -0,0 +1,6 @@
<linker>
<assembly fullname="ControlCatalog" preserve="All" />
<assembly fullname="ControlCatalog.Web" preserve="All" />
<assembly fullname="Avalonia.Themes.Fluent" preserve="All" />
<assembly fullname="Avalonia.Themes.Simple" preserve="All" />
</linker>

23
src/Web/Avalonia.Web.Sample/app.css → 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;
}
}

0
src/Web/Avalonia.Web.Sample/embed.js → samples/ControlCatalog.Web/embed.js

0
src/Web/Avalonia.Web.Sample/favicon.ico → samples/ControlCatalog.Web/favicon.ico

Before

Width:  |  Height:  |  Size: 172 KiB

After

Width:  |  Height:  |  Size: 172 KiB

2
src/Web/Avalonia.Web.Sample/index.html → samples/ControlCatalog.Web/index.html

@ -4,7 +4,7 @@
<html>
<head>
<title>Avalonia.Web.Sample</title>
<title>AvaloniaUI - ControlCatalog</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="modulepreload" href="./main.js" />

4
src/Web/Avalonia.Web.Sample/main.js → 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();

0
src/Web/Avalonia.Web.Sample/runtimeconfig.template.json → samples/ControlCatalog.Web/runtimeconfig.template.json

8
samples/ControlCatalog/Pages/ColorPickerPage.xaml

@ -11,16 +11,17 @@
x:Class="ControlCatalog.Pages.ColorPickerPage">
<UserControl.Resources>
<pc:ThirdComponentConverter x:Key="ThirdComponent" />
</UserControl.Resources>
<Grid ColumnDefinitions="Auto,10,Auto,10,Auto"
<Grid x:Name="LayoutRoot"
ColumnDefinitions="Auto,10,Auto"
RowDefinitions="Auto,Auto">
<ColorView Grid.Column="0"
Grid.Row="0"
ColorSpectrumShape="Ring" />
<ColorPicker Grid.Column="0"
Grid.Row="1"
HsvColor="hsv(120, 1, 1)"
Margin="0,50,0,0">
<ColorPicker.Palette>
<controls:FlatColorPalette />
@ -56,8 +57,5 @@
IsAccentColorsVisible="False"
HsvColor="{Binding HsvColor, ElementName=ColorSpectrum1}" />
</Grid>
<Grid Grid.Column="4"
Grid.Row="0">
</Grid>
</Grid>
</UserControl>

16
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<Grid>("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()

24
samples/ControlCatalog/Pages/DateTimePickerPage.xaml

@ -13,17 +13,17 @@
Margin="16"
HorizontalAlignment="Stretch"
Spacing="16">
<TextBlock FontSize="18">A simple DatePicker with a header</TextBlock>
<TextBlock FontSize="18">A simple DatePicker</TextBlock>
<StackPanel Orientation="Vertical">
<Border BorderBrush="{DynamicResource SystemControlHighlightBaseLowBrush}"
BorderThickness="1" Padding="15">
<DatePicker Header="Pick a date" />
<DatePicker />
</Border>
<Panel Background="{DynamicResource SystemControlBackgroundBaseLowBrush}">
<TextBlock Padding="15">
<TextBlock.Text>
<x:String>
&lt;DatePicker Header="Pick a date" /&gt;
&lt;DatePicker/&gt;
</x:String>
</TextBlock.Text>
</TextBlock>
@ -33,7 +33,7 @@
<Border BorderBrush="{DynamicResource SystemControlHighlightBaseLowBrush}"
BorderThickness="1" Padding="15">
<DatePicker Header="Pick a date">
<DatePicker >
<DataValidationErrors.Error>
<sys:Exception />
</DataValidationErrors.Error>
@ -79,24 +79,24 @@
<Border BorderBrush="{DynamicResource SystemControlHighlightBaseLowBrush}"
BorderThickness="1" Padding="15">
<TimePicker Header="Pick a time">
<TimePicker>
<DataValidationErrors.Error>
<sys:Exception />
</DataValidationErrors.Error>
</TimePicker>
</Border>
<TextBlock FontSize="18">A TimePicker with a header and minute increments specified.</TextBlock>
<TextBlock FontSize="18">A TimePicker with minute increments specified.</TextBlock>
<StackPanel Orientation="Vertical">
<Border BorderBrush="{DynamicResource SystemControlHighlightBaseLowBrush}"
BorderThickness="1" Padding="15">
<TimePicker Header="Arrival time" MinuteIncrement="15" />
<TimePicker MinuteIncrement="15" />
</Border>
<Panel Background="{DynamicResource SystemControlBackgroundBaseLowBrush}">
<TextBlock Padding="15">
<TextBlock.Text>
<x:String>
&lt;TimePicker Header="Arrival time" MinuteIncrement="15" /&gt;
&lt;TimePicker MinuteIncrement="15" /&gt;
</x:String>
</TextBlock.Text>
</TextBlock>
@ -107,13 +107,13 @@
<StackPanel Orientation="Vertical">
<Border BorderBrush="{DynamicResource SystemControlHighlightBaseLowBrush}"
BorderThickness="1" Padding="15">
<TimePicker ClockIdentifier="12HourClock" Header="12 hour clock" />
<TimePicker ClockIdentifier="12HourClock"/>
</Border>
<Panel Background="{DynamicResource SystemControlBackgroundBaseLowBrush}">
<TextBlock Padding="15">
<TextBlock.Text>
<x:String>
&lt;TimePicker ClockIdentifier="12HourClock" Header="12 hour clock" /&gt;
&lt;TimePicker ClockIdentifier="12HourClock" /&gt;
</x:String>
</TextBlock.Text>
</TextBlock>
@ -124,13 +124,13 @@
<StackPanel Orientation="Vertical">
<Border BorderBrush="{DynamicResource SystemControlHighlightBaseLowBrush}"
BorderThickness="1" Padding="15">
<TimePicker ClockIdentifier="24HourClock" Header="24 hour clock" />
<TimePicker ClockIdentifier="24HourClock" />
</Border>
<Panel Background="{DynamicResource SystemControlBackgroundBaseLowBrush}">
<TextBlock Padding="15">
<TextBlock.Text>
<x:String>
&lt;TimePicker ClockIdentifier="24HourClock" Header="24 hour clock" /&gt;
&lt;TimePicker ClockIdentifier="24HourClock" /&gt;
</x:String>
</TextBlock.Text>
</TextBlock>

4
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<App>
[Activity(Label = "MobileSandbox.Android", Theme = "@style/MyTheme.NoActionBar", Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleTop, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)]
public class MainActivity : AvaloniaMainActivity
{
}
}

4
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<App>
{
protected override void OnResume()
{

4
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];

105
src/Android/Avalonia.Android/AvaloniaActivity.cs

@ -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<int, Result, Intent> 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<TApp> : AvaloniaActivity where TApp : Application, new()
{
protected virtual AppBuilder CustomizeAppBuilder(AppBuilder builder) => builder.UseAndroid();
protected override AppBuilder CreateAppBuilder()
{
var builder = AppBuilder.Configure<TApp>();
return CustomizeAppBuilder(builder);
}
}
}

70
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<int, Result, Intent> 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);
}
}
}

34
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<TApp> : AvaloniaSplashActivity where TApp : Application, new()
{
protected virtual AppBuilder CustomizeAppBuilder(AppBuilder builder) => builder.UseAndroid();
protected override AppBuilder CreateAppBuilder()
{
var builder = AppBuilder.Configure<TApp>();
return CustomizeAppBuilder(builder);
}
}
}

3
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();
}

11
src/Android/Avalonia.Android/AvaloniaViewModel.cs

@ -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; }
}
}

11
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<int, Result, Intent> ActivityResult { get; set; }
}
}

6
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<IPlatformOpenGlInterface>();
if (feature is EglPlatformOpenGlInterface egl)
{
return new GlPlatformSurface(egl, info);
}

13
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);
}

6
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<RawPointerPoint> 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
};
}

15
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<Intent?>();
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);
}

26
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; }
}
}

5
src/Avalonia.Base/Avalonia.Base.csproj

@ -21,6 +21,9 @@
<Import Project="..\..\build\NullableEnable.props" />
<Import Project="..\..\build\DevAnalyzers.props" />
<Import Project="..\..\build\SourceGenerators.props" />
<ItemGroup>
<Compile Include="..\Shared\IsExternalInit.cs" Link="IsExternalInit.cs" />
</ItemGroup>
<ItemGroup Label="InternalsVisibleTo">
<InternalsVisibleTo Include="Avalonia.Base.UnitTests, PublicKey=$(AvaloniaPublicKey)" />
@ -38,7 +41,7 @@
<InternalsVisibleTo Include="Avalonia.Win32, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Web.Blazor, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Dialogs, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Diagnostics, PublicKey=$(AvaloniaPublicKey)"/>
<InternalsVisibleTo Include="Avalonia.Diagnostics, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7" />
</ItemGroup>

2
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.");
}

2
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.");
}

12
src/Avalonia.Base/Compatibility/StringCompatibilityExtensions.cs

@ -0,0 +1,12 @@
using System.Runtime.CompilerServices;
namespace System;
#if !NET6_0_OR_GREATER
public static class StringCompatibilityExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Contains(this string str, char search) =>
str.Contains(search.ToString());
}
#endif

2
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,

12
src/Avalonia.Base/Media/FontManager.cs

@ -13,8 +13,8 @@ namespace Avalonia.Media
/// </summary>
public sealed class FontManager
{
private readonly ConcurrentDictionary<Typeface, GlyphTypeface> _glyphTypefaceCache =
new ConcurrentDictionary<Typeface, GlyphTypeface>();
private readonly ConcurrentDictionary<Typeface, IGlyphTypeface> _glyphTypefaceCache =
new ConcurrentDictionary<Typeface, IGlyphTypeface>();
private readonly FontFamily _defaultFontFamily;
private readonly IReadOnlyList<FontFallback>? _fontFallbacks;
@ -81,13 +81,13 @@ namespace Avalonia.Media
PlatformImpl.GetInstalledFontFamilyNames(checkForUpdates);
/// <summary>
/// Returns a new <see cref="GlyphTypeface"/>, or an existing one if a matching <see cref="GlyphTypeface"/> exists.
/// Returns a new <see cref="IGlyphTypeface"/>, or an existing one if a matching <see cref="IGlyphTypeface"/> exists.
/// </summary>
/// <param name="typeface">The typeface.</param>
/// <returns>
/// The <see cref="GlyphTypeface"/>.
/// The <see cref="IGlyphTypeface"/>.
/// </returns>
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))
{

58
src/Avalonia.Base/Media/FontMetrics.cs

@ -0,0 +1,58 @@
namespace Avalonia.Media
{
/// <summary>
/// The font metrics is holding information about a font's ascent, descent, etc. in design em units.
/// </summary>
public readonly struct FontMetrics
{
/// <summary>
/// Gets the font design units per em.
/// </summary>
public short DesignEmHeight { get; init; }
/// <summary>
/// A <see cref="bool"/> value indicating whether all glyphs in the font have the same advancement.
/// </summary>
public bool IsFixedPitch { get; init; }
/// <summary>
/// Gets the recommended distance above the baseline in design em size.
/// </summary>
public int Ascent { get; init; }
/// <summary>
/// Gets the recommended distance under the baseline in design em size.
/// </summary>
public int Descent { get; init; }
/// <summary>
/// Gets the recommended additional space between two lines of text in design em size.
/// </summary>
public int LineGap { get; init; }
/// <summary>
/// Gets the recommended line spacing of a formed text line.
/// </summary>
public int LineSpacing => Descent - Ascent + LineGap;
/// <summary>
/// Gets a value that indicates the distance of the underline from the baseline in design em size.
/// </summary>
public int UnderlinePosition { get; init; }
/// <summary>
/// Gets a value that indicates the thickness of the underline in design em size.
/// </summary>
public int UnderlineThickness { get; init; }
/// <summary>
/// Gets a value that indicates the distance of the strikethrough from the baseline in design em size.
/// </summary>
public int StrikethroughPosition { get; init; }
/// <summary>
/// Gets a value that indicates the thickness of the underline in design em size.
/// </summary>
public int StrikethroughThickness { get; init; }
}
}

95
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<int> s_descendingComparer = new ReverseComparer<int>();
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
/// <param name="glyphClusters">The glyph clusters.</param>
/// <param name="biDiLevel">The bidi level.</param>
public GlyphRun(
GlyphTypeface glyphTypeface,
IGlyphTypeface glyphTypeface,
double fontRenderingEmSize,
ReadOnlySlice<char> characters,
IReadOnlyList<ushort> glyphIndices,
@ -69,9 +70,9 @@ namespace Avalonia.Media
}
/// <summary>
/// Gets the <see cref="Media.GlyphTypeface"/> for the <see cref="GlyphRun"/>.
/// Gets the <see cref="IGlyphTypeface"/> for the <see cref="GlyphRun"/>.
/// </summary>
public GlyphTypeface GlyphTypeface => _glyphTypeface;
public IGlyphTypeface GlyphTypeface => _glyphTypeface;
/// <summary>
/// Gets or sets the em size used for rendering the <see cref="GlyphRun"/>.
@ -171,7 +172,7 @@ namespace Avalonia.Media
/// <summary>
/// Gets the scale of the current <see cref="Media.GlyphTypeface"/>
/// </summary>
internal double Scale => FontRenderingEmSize / GlyphTypeface.DesignEmHeight;
internal double Scale => FontRenderingEmSize / GlyphTypeface.Metrics.DesignEmHeight;
/// <summary>
/// Returns <c>true</c> if the text direction is left-to-right. Otherwise, returns <c>false</c>.
@ -612,7 +613,7 @@ namespace Avalonia.Media
/// <returns>The baseline origin.</returns>
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<IPlatformRenderInterface>();
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()

125
src/Avalonia.Base/Media/GlyphTypeface.cs

@ -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; }
/// <summary>
/// Gets the font design units per em.
/// </summary>
public short DesignEmHeight => PlatformImpl.DesignEmHeight;
/// <summary>
/// Gets the recommended distance above the baseline in design em size.
/// </summary>
public int Ascent => PlatformImpl.Ascent;
/// <summary>
/// Gets the recommended distance under the baseline in design em size.
/// </summary>
public int Descent => PlatformImpl.Descent;
/// <summary>
/// Gets the recommended additional space between two lines of text in design em size.
/// </summary>
public int LineGap => PlatformImpl.LineGap;
/// <summary>
/// Gets the recommended line height.
/// </summary>
public int LineHeight => Descent - Ascent + LineGap;
/// <summary>
/// Gets a value that indicates the distance of the underline from the baseline in design em size.
/// </summary>
public int UnderlinePosition => PlatformImpl.UnderlinePosition;
/// <summary>
/// Gets a value that indicates the thickness of the underline in design em size.
/// </summary>
public int UnderlineThickness => PlatformImpl.UnderlineThickness;
/// <summary>
/// Gets a value that indicates the distance of the strikethrough from the baseline in design em size.
/// </summary>
public int StrikethroughPosition => PlatformImpl.StrikethroughPosition;
/// <summary>
/// Gets a value that indicates the thickness of the underline in design em size.
/// </summary>
public int StrikethroughThickness => PlatformImpl.StrikethroughThickness;
/// <summary>
/// A <see cref="bool"/> value indicating whether all glyphs in the font have the same advancement.
/// </summary>
public bool IsFixedPitch => PlatformImpl.IsFixedPitch;
/// <summary>
/// Returns an glyph index for the specified codepoint.
/// </summary>
/// <remarks>
/// Returns a replacement glyph if a glyph isn't found.
/// </remarks>
/// <param name="codepoint">The codepoint.</param>
/// <returns>
/// A glyph index.
/// </returns>
public ushort GetGlyph(uint codepoint) => PlatformImpl.GetGlyph(codepoint);
/// <summary>
/// Tries to get an glyph index for specified codepoint.
/// </summary>
/// <param name="codepoint">The codepoint.</param>
/// <param name="glyph">A glyph index.</param>
/// <returns>
/// <c>true</c> if an glyph index was found, <c>false</c> otherwise.
/// </returns>
public bool TryGetGlyph(uint codepoint, out ushort glyph)
{
glyph = PlatformImpl.GetGlyph(codepoint);
return glyph != 0;
}
/// <summary>
/// Returns an array of glyph indices. Codepoints that are not represented by the font are returned as <code>0</code>.
/// </summary>
/// <param name="codepoints">The codepoints to map.</param>
/// <returns></returns>
public ushort[] GetGlyphs(ReadOnlySpan<uint> codepoints) => PlatformImpl.GetGlyphs(codepoints);
/// <summary>
/// Returns the glyph advance for the specified glyph.
/// </summary>
/// <param name="glyph">The glyph.</param>
/// <returns>
/// The advance.
/// </returns>
public int GetGlyphAdvance(ushort glyph) => PlatformImpl.GetGlyphAdvance(glyph);
/// <summary>
/// Returns an array of glyph advances in design em size.
/// </summary>
/// <param name="glyphs">The glyph indices.</param>
/// <returns></returns>
public int[] GetGlyphAdvances(ReadOnlySpan<ushort> glyphs) => PlatformImpl.GetGlyphAdvances(glyphs);
void IDisposable.Dispose()
{
PlatformImpl?.Dispose();
}
}
}

68
src/Avalonia.Base/Platform/IGlyphTypefaceImpl.cs → src/Avalonia.Base/Media/IGlyphTypeface.cs

@ -1,55 +1,23 @@
using System;
using Avalonia.Metadata;
namespace Avalonia.Platform
namespace Avalonia.Media
{
[Unstable]
public interface IGlyphTypefaceImpl : IDisposable
public interface IGlyphTypeface : IDisposable
{
/// <summary>
/// Gets the font design units per em.
/// Gets the number of glyphs held by this glyph typeface.
/// </summary>
short DesignEmHeight { get; }
int GlyphCount { get; }
/// <summary>
/// Gets the recommended distance above the baseline in design em size.
/// Gets the font metrics.
/// </summary>
int Ascent { get; }
/// <summary>
/// Gets the recommended distance under the baseline in design em size.
/// </summary>
int Descent { get; }
/// <summary>
/// Gets the recommended additional space between two lines of text in design em size.
/// </summary>
int LineGap { get; }
/// <summary>
/// Gets a value that indicates the distance of the underline from the baseline in design em size.
/// </summary>
int UnderlinePosition { get; }
/// <summary>
/// Gets a value that indicates the thickness of the underline in design em size.
/// </summary>
int UnderlineThickness { get; }
/// <summary>
/// Gets a value that indicates the distance of the strikethrough from the baseline in design em size.
/// </summary>
int StrikethroughPosition { get; }
/// <summary>
/// Gets a value that indicates the thickness of the underline in design em size.
/// </summary>
int StrikethroughThickness { get; }
/// <summary>
/// A <see cref="bool"/> value indicating whether all glyphs in the font have the same advancement.
/// </summary>
bool IsFixedPitch { get; }
/// <returns>
/// The font metrics.
/// </returns>
FontMetrics Metrics { get; }
/// <summary>
/// Returns an glyph index for the specified codepoint.
@ -63,6 +31,16 @@ namespace Avalonia.Platform
/// </returns>
ushort GetGlyph(uint codepoint);
/// <summary>
/// Tries to get an glyph index for specified codepoint.
/// </summary>
/// <param name="codepoint">The codepoint.</param>
/// <param name="glyph">A glyph index.</param>
/// <returns>
/// <c>true</c> if an glyph index was found, <c>false</c> otherwise.
/// </returns>
bool TryGetGlyph(uint codepoint, out ushort glyph);
/// <summary>
/// Returns an array of glyph indices. Codepoints that are not represented by the font are returned as <code>0</code>.
/// </summary>
@ -89,5 +67,13 @@ namespace Avalonia.Platform
/// An array of glyph advances.
/// </returns>
int[] GetGlyphAdvances(ReadOnlySpan<ushort> glyphs);
/// <summary>
/// Returns the contents of the table data for the specified tag.
/// </summary>
/// <param name="tag">The table tag to get the data for.</param>
/// <param name="table">The contents of the table data for the specified tag.</param>
/// <returns>Returns <c>true</c> if the content exists, otherwise <c>false</c>.</returns>
bool TryGetTable(uint tag, out byte[] table);
}
}

2
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

16
src/Avalonia.Base/Media/TextDecoration.cs

@ -155,9 +155,9 @@ namespace Avalonia.Media
/// </summary>
/// <param name="drawingContext">The drawing context.</param>
/// <param name="glyphRun">The decorated run.</param>
/// <param name="fontMetrics">The font metrics of the decorated run.</param>
/// <param name="textMetrics">The font metrics of the decorated run.</param>
/// <param name="defaultBrush">The default brush that is used to draw the decoration.</param>
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);

6
src/Avalonia.Base/Media/TextFormatting/ShapedBuffer.cs

@ -8,13 +8,13 @@ namespace Avalonia.Media.TextFormatting
{
private static readonly IComparer<GlyphInfo> s_clusterComparer = new CompareClusters();
public ShapedBuffer(ReadOnlySlice<char> text, int length, GlyphTypeface glyphTypeface, double fontRenderingEmSize, sbyte bidiLevel)
public ShapedBuffer(ReadOnlySlice<char> text, int length, IGlyphTypeface glyphTypeface, double fontRenderingEmSize, sbyte bidiLevel)
: this(text, new GlyphInfo[length], glyphTypeface, fontRenderingEmSize, bidiLevel)
{
}
internal ShapedBuffer(ReadOnlySlice<char> text, ArraySlice<GlyphInfo> glyphInfos, GlyphTypeface glyphTypeface, double fontRenderingEmSize, sbyte bidiLevel)
internal ShapedBuffer(ReadOnlySlice<char> text, ArraySlice<GlyphInfo> 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; }

9
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
/// <inheritdoc/>
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);
}
}
}

26
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)

24
src/Avalonia.Base/Media/TextFormatting/FontMetrics.cs → src/Avalonia.Base/Media/TextFormatting/TextMetrics.cs

@ -1,33 +1,33 @@
namespace Avalonia.Media.TextFormatting
{
/// <summary>
/// A metric that holds information about font specific measurements.
/// A metric that holds information about text specific measurements.
/// </summary>
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;
}
/// <summary>

4
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
/// <summary>
/// Get the typeface.
/// </summary>
public GlyphTypeface Typeface { get; }
public IGlyphTypeface Typeface { get; }
/// <summary>
/// Get the font rendering em size.
/// </summary>

2
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

7
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
/// <summary>
/// Gets the canonical representation of a given codepoint.
/// <see href="http://www.unicode.org/L2/L2013/13123-norm-and-bpa.pdf"/>
/// <see href="https://www.unicode.org/L2/L2013/13123-norm-and-bpa.pdf"/>
/// </summary>
/// <param name="codePoint">The code point to be mapped.</param>
/// <returns>The mapped canonical code point, or the passed <paramref name="codePoint"/>.</returns>
@ -165,7 +166,7 @@ namespace Avalonia.Media.TextFormatting.Unicode
/// <param name="index">The index to read at.</param>
/// <param name="count">The count of character that were read.</param>
/// <returns></returns>
public static Codepoint ReadAt(ReadOnlySlice<char> text, int index, out int count)
public static Codepoint ReadAt(ReadOnlySpan<char> text, int index, out int count)
{
count = 1;

2
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<byte>();
}
}

2
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

2
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

2
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

2
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

2
src/Avalonia.Base/Media/Typeface.cs

@ -81,7 +81,7 @@ namespace Avalonia.Media
/// <value>
/// The glyph typeface.
/// </value>
public GlyphTypeface GlyphTypeface => FontManager.Current.GetOrAddGlyphTypeface(this);
public IGlyphTypeface GlyphTypeface => FontManager.Current.GetOrAddGlyphTypeface(this);
public static bool operator !=(Typeface a, Typeface b)
{

2
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;

2
src/Avalonia.Base/Platform/IFontManagerImpl.cs

@ -43,6 +43,6 @@ namespace Avalonia.Platform
/// <returns>0
/// The created glyph typeface. Can be <c>Null</c> if it was not possible to create a glyph typeface.
/// </returns>
IGlyphTypefaceImpl CreateGlyphTypeface(Typeface typeface);
IGlyphTypeface CreateGlyphTypeface(Typeface typeface);
}
}

22
src/Avalonia.Base/Platform/IGlyphRunBuffer.cs

@ -0,0 +1,22 @@
using System;
using System.Drawing;
namespace Avalonia.Platform
{
public interface IGlyphRunBuffer
{
Span<ushort> GlyphIndices { get; }
IGlyphRunImpl Build();
}
public interface IHorizontalGlyphRunBuffer : IGlyphRunBuffer
{
Span<float> GlyphPositions { get; }
}
public interface IPositionedGlyphRunBuffer : IGlyphRunBuffer
{
Span<PointF> GlyphPositions { get; }
}
}

37
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);
/// <summary>
/// Creates a platform implementation of a glyph run.
/// Allocates a platform glyph run buffer.
/// </summary>
/// <param name="glyphRun">The glyph run.</param>
/// <returns></returns>
IGlyphRunImpl CreateGlyphRun(GlyphRun glyphRun);
/// <param name="glyphTypeface">The glyph typeface.</param>
/// <param name="fontRenderingEmSize">The font rendering em size.</param>
/// <param name="length">The length.</param>
/// <returns>An <see cref="IGlyphRunBuffer"/>.</returns>
/// <remarks>
/// This buffer only holds glyph indices.
/// </remarks>
IGlyphRunBuffer AllocateGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length);
/// <summary>
/// Allocates a horizontal platform glyph run buffer.
/// </summary>
/// <param name="glyphTypeface">The glyph typeface.</param>
/// <param name="fontRenderingEmSize">The font rendering em size.</param>
/// <param name="length">The length.</param>
/// <returns>An <see cref="IGlyphRunBuffer"/>.</returns>
/// <remarks>
/// This buffer holds glyph indices and glyph advances.
/// </remarks>
IHorizontalGlyphRunBuffer AllocateHorizontalGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length);
/// <summary>
/// Allocates a positioned platform glyph run buffer.
/// </summary>
/// <param name="glyphTypeface">The glyph typeface.</param>
/// <param name="fontRenderingEmSize">The font rendering em size.</param>
/// <param name="length">The length.</param>
/// <returns>An <see cref="IGlyphRunBuffer"/>.</returns>
/// <remarks>
/// This buffer holds glyph indices, glyph advances and glyph positions.
/// </remarks>
IPositionedGlyphRunBuffer AllocatePositionedGlyphRun(IGlyphTypeface glyphTypeface, float fontRenderingEmSize, int length);
/// <summary>
/// Gets a value indicating whether the platform directly supports rectangles with rounded corners.

2
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++)
{

2
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

2
src/Avalonia.Base/Utilities/IdentifierParser.cs

@ -8,7 +8,7 @@ namespace Avalonia.Utilities
#endif
static class IdentifierParser
{
public static ReadOnlySpan<char> ParseIdentifier(this ref CharacterReader r)
public static ReadOnlySpan<char> ParseIdentifier(this scoped ref CharacterReader r)
{
if (IsValidIdentifierStart(r.Peek))
{

2
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;

2
src/Avalonia.Base/Utilities/ReadOnlySlice.cs

@ -214,6 +214,8 @@ namespace Avalonia.Utilities
return new ReadOnlySlice<T>(memory);
}
public static implicit operator ReadOnlySpan<T>(ReadOnlySlice<T> slice) => slice.Span;
internal class ReadOnlySliceDebugView
{
private readonly ReadOnlySlice<T> _readOnlySlice;

5
src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj

@ -9,6 +9,11 @@
<NoWarn>NU1605;CS8632</NoWarn>
</PropertyGroup>
<!--Disable Net Perf. analyzer for submodule to avoid commit issue -->
<PropertyGroup>
<EnableNETAnalyzers>false</EnableNETAnalyzers>
</PropertyGroup>
<ItemGroup>
<Compile Include="../Avalonia.Base/Utilities/AvaloniaResourcesIndex.cs">
<Link>Shared/AvaloniaResourcesIndex.cs</Link>

5
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; }

6
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<MessageImportance>(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))

3
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;

32
src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.Helpers.cs

@ -24,38 +24,6 @@ namespace Avalonia.Build.Tasks
string Name { get; }
IEnumerable<IResource> Resources { get; }
}
class EmbeddedResources : IResourceGroup
{
private readonly AssemblyDefinition _asm;
public string Name => "EmbeddedResource";
public IEnumerable<IResource> Resources => _asm.MainModule.Resources.OfType<EmbeddedResource>()
.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
{

17
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))

781
src/Avalonia.Controls.ColorPicker/ColorPalettes/FlatColorPalette.cs

@ -17,268 +17,603 @@ namespace Avalonia.Controls
/// </remarks>
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[,]
/// <summary>
/// Defines all colors in the <see cref="FlatColorPalette"/>.
/// </summary>
/// <remarks>
/// This is done in an enum to ensure it is compiled into the assembly improving
/// startup performance.
/// </remarks>
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,
};
/// <summary>
/// Gets the index of the default shade of colors in this palette.
/// </summary>
public const int DefaultShadeIndex = 2;
/// <summary>
/// The index in the color palette of the 'Pomegranate' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int PomegranateIndex = 0;
/// <summary>
/// The index in the color palette of the 'Amethyst' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int AmethystIndex = 1;
/// <summary>
/// The index in the color palette of the 'BelizeHole' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int BelizeHoleIndex = 2;
/// <summary>
/// The index in the color palette of the 'Turquoise' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int TurquoiseIndex = 3;
/// <summary>
/// The index in the color palette of the 'Nephritis' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int NephritisIndex = 4;
/// <summary>
/// The index in the color palette of the 'Sunflower' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int SunflowerIndex = 5;
/// <summary>
/// The index in the color palette of the 'Carrot' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int CarrotIndex = 6;
/// <summary>
/// The index in the color palette of the 'Clouds' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int CloudsIndex = 7;
/// <summary>
/// The index in the color palette of the 'Concrete' color.
/// This index can correspond to multiple color shades.
/// </summary>
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();
/// <summary>
/// The index in the color palette of the 'WetAsphalt' color.
/// This index can correspond to multiple color shades.
/// Initializes all color chart colors.
/// </summary>
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;
}
/// <inheritdoc/>
public int ColorCount
{
// Table is transposed compared to the reference chart
get => colorChart.GetLength(0);
get => 20;
}
/// <inheritdoc/>
public int ShadeCount
{
// Table is transposed compared to the reference chart
get => colorChart.GetLength(1);
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FFC0392B.
/// </summary>
public static Color Pomegranate
{
get => colorChart[PomegranateIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FF9B59B6.
/// </summary>
public static Color Amethyst
{
get => colorChart[AmethystIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FF2980B9.
/// </summary>
public static Color BelizeHole
{
get => colorChart[BelizeHoleIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FF1ABC9C.
/// </summary>
public static Color Turquoise
{
get => colorChart[TurquoiseIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FF27AE60.
/// </summary>
public static Color Nephritis
{
get => colorChart[NephritisIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FFF1C40F.
/// </summary>
public static Color Sunflower
{
get => colorChart[SunflowerIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FFE67E22.
/// </summary>
public static Color Carrot
{
get => colorChart[CarrotIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FFECF0F1.
/// </summary>
public static Color Clouds
{
get => colorChart[CloudsIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FF95A5A6.
/// </summary>
public static Color Concrete
{
get => colorChart[ConcreteIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FF34495E.
/// </summary>
public static Color WetAsphalt
{
get => colorChart[WetAsphaltIndex, DefaultShadeIndex];
get => 10;
}
/// <inheritdoc/>
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)];
}
}
}

150
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
{
/// <summary>
/// Implements half of the <see cref="FlatColorPalette"/> for improved usability.
/// </summary>
/// <inheritdoc cref="FlatColorPalette"/>
public class FlatHalfColorPalette : IColorPalette
{
protected static Color[,]? _colorChart = null;
protected static object _colorChartMutex = new object();
/// <summary>
/// Initializes all color chart colors.
/// </summary>
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;
}
/// <inheritdoc/>
public int ColorCount
{
get => 10;
}
/// <inheritdoc/>
public int ShadeCount
{
get => 5;
}
/// <inheritdoc/>
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)];
}
}
}

577
src/Avalonia.Controls.ColorPicker/ColorPalettes/MaterialColorPalette.cs

@ -0,0 +1,577 @@
using Avalonia.Media;
using Avalonia.Utilities;
namespace Avalonia.Controls
{
/// <summary>
/// Implements a reduced version of the 2014 Material Design color palette.
/// </summary>
/// <remarks>
/// 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.
///
/// </remarks>
public class MaterialColorPalette : IColorPalette
{
/// <summary>
/// Defines all colors in the <see cref="MaterialColorPalette"/>.
/// </summary>
/// <remarks>
/// This is done in an enum to ensure it is compiled into the assembly improving
/// startup performance.
/// </remarks>
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();
/// <summary>
/// Initializes all color chart colors.
/// </summary>
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;
}
/// <inheritdoc/>
public int ColorCount
{
get => 19;
}
/// <inheritdoc/>
public int ShadeCount
{
get => 10;
}
/// <inheritdoc/>
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)];
}
}
}

150
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
{
/// <summary>
/// Implements half of the <see cref="MaterialColorPalette"/> for improved usability.
/// </summary>
/// <inheritdoc cref="MaterialColorPalette"/>
public class MaterialHalfColorPalette : IColorPalette
{
protected static Color[,]? _colorChart = null;
protected static object _colorChartMutex = new object();
/// <summary>
/// Initializes all color chart colors.
/// </summary>
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;
}
/// <inheritdoc/>
public int ColorCount
{
get => 10;
}
/// <inheritdoc/>
public int ShadeCount
{
get => 5;
}
/// <inheritdoc/>
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)];
}
}
}

229
src/Avalonia.Controls.ColorPicker/ColorPalettes/SixteenColorPalette.cs

@ -50,107 +50,6 @@ namespace Avalonia.Controls
}
};
/// <summary>
/// Gets the index of the default shade of colors in this palette.
/// </summary>
public const int DefaultShadeIndex = 0;
/// <summary>
/// The index in the color palette of the 'White' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int WhiteIndex = 0;
/// <summary>
/// The index in the color palette of the 'Silver' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int SilverIndex = 1;
/// <summary>
/// The index in the color palette of the 'Gray' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int GrayIndex = 2;
/// <summary>
/// The index in the color palette of the 'Black' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int BlackIndex = 3;
/// <summary>
/// The index in the color palette of the 'Red' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int RedIndex = 4;
/// <summary>
/// The index in the color palette of the 'Maroon' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int MaroonIndex = 5;
/// <summary>
/// The index in the color palette of the 'Yellow' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int YellowIndex = 6;
/// <summary>
/// The index in the color palette of the 'Olive' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int OliveIndex = 7;
/// <summary>
/// The index in the color palette of the 'Lime' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int LimeIndex = 8;
/// <summary>
/// The index in the color palette of the 'Green' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int GreenIndex = 9;
/// <summary>
/// The index in the color palette of the 'Aqua' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int AquaIndex = 10;
/// <summary>
/// The index in the color palette of the 'Teal' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int TealIndex = 11;
/// <summary>
/// The index in the color palette of the 'Blue' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int BlueIndex = 12;
/// <summary>
/// The index in the color palette of the 'Navy' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int NavyIndex = 13;
/// <summary>
/// The index in the color palette of the 'Fuchsia' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int FuchsiaIndex = 14;
/// <summary>
/// The index in the color palette of the 'Purple' color.
/// This index can correspond to multiple color shades.
/// </summary>
public const int PurpleIndex = 15;
/// <inheritdoc/>
public int ColorCount
{
@ -163,134 +62,6 @@ namespace Avalonia.Controls
get => colorChart.GetLength(1);
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FFFFFFFF.
/// </summary>
public static Color White
{
get => colorChart[WhiteIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FFC0C0C0.
/// </summary>
public static Color Silver
{
get => colorChart[SilverIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FF808080.
/// </summary>
public static Color Gray
{
get => colorChart[GrayIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FF000000.
/// </summary>
public static Color Black
{
get => colorChart[BlackIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FFFF0000.
/// </summary>
public static Color Red
{
get => colorChart[RedIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FF800000.
/// </summary>
public static Color Maroon
{
get => colorChart[MaroonIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FFFFFF00.
/// </summary>
public static Color Yellow
{
get => colorChart[YellowIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FF808000.
/// </summary>
public static Color Olive
{
get => colorChart[OliveIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FF00FF00.
/// </summary>
public static Color Lime
{
get => colorChart[LimeIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FF008000.
/// </summary>
public static Color Green
{
get => colorChart[GreenIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FF00FFFF.
/// </summary>
public static Color Aqua
{
get => colorChart[AquaIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FF008080.
/// </summary>
public static Color Teal
{
get => colorChart[TealIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FF0000FF.
/// </summary>
public static Color Blue
{
get => colorChart[BlueIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FF000080.
/// </summary>
public static Color Navy
{
get => colorChart[NavyIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FFFF00FF.
/// </summary>
public static Color Fuchsia
{
get => colorChart[FuchsiaIndex, DefaultShadeIndex];
}
/// <summary>
/// Gets the palette defined color that has an ARGB value of #FF800080.
/// </summary>
public static Color Purple
{
get => colorChart[PurpleIndex, DefaultShadeIndex];
}
/// <inheritdoc/>
public Color GetColor(int colorIndex, int shadeIndex)
{

33
src/Avalonia.Controls.ColorPicker/ColorPicker/ColorPicker.cs

@ -1,4 +1,6 @@
namespace Avalonia.Controls
using Avalonia.Controls.Primitives;
namespace Avalonia.Controls
{
/// <summary>
/// Presents a color for user editing using a spectrum, palette and component sliders within a drop down.
@ -11,8 +13,33 @@
/// </summary>
public ColorPicker() : base()
{
// Completely ignore property changes here
// The ColorView in the control template is responsible to manage this
}
/// <inheritdoc/>
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;
}
}

53
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;
/// <summary>
/// Initializes a new instance of the <see cref="ColorSlider"/> class.
/// </summary>
@ -38,6 +41,18 @@ namespace Avalonia.Controls.Primitives
{
}
/// <inheritdoc/>
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
}
/// <inheritdoc/>
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnDetachedFromVisualTree(e);
}
/// <summary>
/// Updates the visual state of the control by applying latest PseudoClasses.
/// </summary>
@ -98,7 +113,7 @@ namespace Avalonia.Controls.Primitives
if (pixelWidth != 0 && pixelHeight != 0)
{
var bitmap = await ColorPickerHelpers.CreateComponentBitmapAsync(
ArrayList<byte> 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 ||

24
src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs

@ -93,6 +93,14 @@ namespace Avalonia.Controls.Primitives
nameof(Shape),
ColorSpectrumShape.Box);
/// <summary>
/// Defines the <see cref="ThirdComponent"/> property.
/// </summary>
public static readonly StyledProperty<ColorComponent> ThirdComponentProperty =
AvaloniaProperty.Register<ColorSpectrum, ColorComponent>(
nameof(ThirdComponent),
ColorComponent.Component3); // Value
/// <summary>
/// Gets or sets the currently selected color in the RGB color model.
/// </summary>
@ -218,5 +226,21 @@ namespace Avalonia.Controls.Primitives
get => GetValue(ShapeProperty);
set => SetValue(ShapeProperty, value);
}
/// <summary>
/// Gets the third HSV color component that is NOT displayed by the spectrum.
/// This is automatically calculated from the <see cref="Components"/> property.
/// </summary>
/// <remarks>
/// 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
/// <see cref="ColorComponent"/> type instead of the more accurate <see cref="HsvComponent"/>
/// to allow direct usage by the generalized color sliders.
/// </remarks>
public ColorComponent ThirdComponent
{
get => GetValue(ThirdComponentProperty);
private set => SetValue(ThirdComponentProperty, value);
}
}
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save