Browse Source

Merge branch 'master' into fixes/rtbMeasureArrange

pull/9147/head
Benedikt Stebner 4 years ago
committed by GitHub
parent
commit
c1bbc0419b
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 10
      .editorconfig
  2. 39
      Avalonia.sln
  3. 4
      NOTICE.md
  4. 5
      build/NetAnalyzers.props
  5. 13
      dirs.proj
  6. 12
      samples/ControlCatalog.Android/MainActivity.cs
  7. 13
      samples/ControlCatalog.Android/SplashActivity.cs
  8. 0
      samples/ControlCatalog.Blazor.Web/App.razor
  9. 17
      samples/ControlCatalog.Blazor.Web/App.razor.cs
  10. 29
      samples/ControlCatalog.Blazor.Web/ControlCatalog.Blazor.Web.csproj
  11. 0
      samples/ControlCatalog.Blazor.Web/Pages/Index.razor
  12. 29
      samples/ControlCatalog.Blazor.Web/Program.cs
  13. 0
      samples/ControlCatalog.Blazor.Web/Properties/launchSettings.json
  14. 0
      samples/ControlCatalog.Blazor.Web/Shared/MainLayout.razor
  15. 3
      samples/ControlCatalog.Blazor.Web/_Imports.razor
  16. 0
      samples/ControlCatalog.Blazor.Web/wwwroot/css/app.css
  17. 0
      samples/ControlCatalog.Blazor.Web/wwwroot/favicon.ico
  18. 0
      samples/ControlCatalog.Blazor.Web/wwwroot/index.html
  19. 20
      samples/ControlCatalog.Web/App.razor.cs
  20. 68
      samples/ControlCatalog.Web/ControlCatalog.Web.csproj
  21. 32
      samples/ControlCatalog.Web/EmbedSample.Browser.cs
  22. 0
      samples/ControlCatalog.Web/Logo.svg
  23. 37
      samples/ControlCatalog.Web/Program.cs
  24. 6
      samples/ControlCatalog.Web/Roots.xml
  25. 23
      samples/ControlCatalog.Web/app.css
  26. 0
      samples/ControlCatalog.Web/embed.js
  27. 0
      samples/ControlCatalog.Web/favicon.ico
  28. 2
      samples/ControlCatalog.Web/index.html
  29. 4
      samples/ControlCatalog.Web/main.js
  30. 0
      samples/ControlCatalog.Web/runtimeconfig.template.json
  31. 24
      samples/ControlCatalog/Pages/DateTimePickerPage.xaml
  32. 4
      samples/MobileSandbox.Android/MainActivity.cs
  33. 4
      samples/MobileSandbox.Android/SplashActivity.cs
  34. 4
      samples/RenderDemo/Pages/GlyphRunPage.xaml.cs
  35. 105
      src/Android/Avalonia.Android/AvaloniaActivity.cs
  36. 70
      src/Android/Avalonia.Android/AvaloniaMainActivity.cs
  37. 34
      src/Android/Avalonia.Android/AvaloniaSplashActivity.cs
  38. 3
      src/Android/Avalonia.Android/AvaloniaView.cs
  39. 11
      src/Android/Avalonia.Android/AvaloniaViewModel.cs
  40. 11
      src/Android/Avalonia.Android/IActivityResultHandler.cs
  41. 6
      src/Android/Avalonia.Android/OpenGL/GlPlatformSurface.cs
  42. 13
      src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
  43. 6
      src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidMotionEventsHelper.cs
  44. 15
      src/Android/Avalonia.Android/Platform/Storage/AndroidStorageProvider.cs
  45. 26
      src/Android/Avalonia.Android/SingleViewLifetime.cs
  46. 5
      src/Avalonia.Base/Avalonia.Base.csproj
  47. 2
      src/Avalonia.Base/Input/Cursor.cs
  48. 12
      src/Avalonia.Base/Media/FontManager.cs
  49. 58
      src/Avalonia.Base/Media/FontMetrics.cs
  50. 95
      src/Avalonia.Base/Media/GlyphRun.cs
  51. 125
      src/Avalonia.Base/Media/GlyphTypeface.cs
  52. 68
      src/Avalonia.Base/Media/IGlyphTypeface.cs
  53. 2
      src/Avalonia.Base/Media/PreciseEllipticArcHelper.cs
  54. 16
      src/Avalonia.Base/Media/TextDecoration.cs
  55. 6
      src/Avalonia.Base/Media/TextFormatting/ShapedBuffer.cs
  56. 9
      src/Avalonia.Base/Media/TextFormatting/ShapedTextCharacters.cs
  57. 26
      src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs
  58. 24
      src/Avalonia.Base/Media/TextFormatting/TextMetrics.cs
  59. 4
      src/Avalonia.Base/Media/TextFormatting/TextShaperOptions.cs
  60. 2
      src/Avalonia.Base/Media/TextFormatting/Unicode/BinaryReaderExtensions.cs
  61. 7
      src/Avalonia.Base/Media/TextFormatting/Unicode/Codepoint.cs
  62. 2
      src/Avalonia.Base/Media/TextFormatting/Unicode/GraphemeBreak.cs
  63. 2
      src/Avalonia.Base/Media/TextFormatting/Unicode/LineBreak.cs
  64. 2
      src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeTrie.cs
  65. 2
      src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeTrieBuilder.Constants.cs
  66. 2
      src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeTrieBuilder.cs
  67. 2
      src/Avalonia.Base/Media/Typeface.cs
  68. 2
      src/Avalonia.Base/Platform/IFontManagerImpl.cs
  69. 22
      src/Avalonia.Base/Platform/IGlyphRunBuffer.cs
  70. 37
      src/Avalonia.Base/Platform/IPlatformRenderInterface.cs
  71. 2
      src/Avalonia.Base/Rendering/Composition/Server/FpsCounter.cs
  72. 2
      src/Avalonia.Base/Utilities/BinarySearchExtension.cs
  73. 2
      src/Avalonia.Base/Utilities/MathUtilities.cs
  74. 2
      src/Avalonia.Base/Utilities/ReadOnlySlice.cs
  75. 5
      src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
  76. 2
      src/Avalonia.Controls.ColorPicker/Helpers/ColorPickerHelpers.cs
  77. 29
      src/Avalonia.Controls.DataGrid/Collections/DataGridCollectionView.cs
  78. 463
      src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs
  79. 756
      src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs
  80. 131
      src/Avalonia.Controls/AutoCompleteBox/AutoCompleteFilterMode.cs
  81. 39
      src/Avalonia.Controls/AutoCompleteBox/PopulatedEventArgs.cs
  82. 39
      src/Avalonia.Controls/AutoCompleteBox/PopulatingEventArgs.cs
  83. 2
      src/Avalonia.Controls/Calendar/Calendar.cs
  84. 2
      src/Avalonia.Controls/Calendar/CalendarBlackoutDatesCollection.cs
  85. 2
      src/Avalonia.Controls/Calendar/CalendarButton.cs
  86. 2
      src/Avalonia.Controls/Calendar/CalendarDateRange.cs
  87. 2
      src/Avalonia.Controls/Calendar/CalendarDayButton.cs
  88. 2
      src/Avalonia.Controls/Calendar/CalendarExtensions.cs
  89. 2
      src/Avalonia.Controls/Calendar/CalendarItem.cs
  90. 2
      src/Avalonia.Controls/Calendar/DateTimeHelper.cs
  91. 2
      src/Avalonia.Controls/Calendar/SelectedDatesCollection.cs
  92. 2
      src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.cs
  93. 2
      src/Avalonia.Controls/CalendarDatePicker/CalendarDatePickerDateValidationErrorEventArgs.cs
  94. 2
      src/Avalonia.Controls/CalendarDatePicker/CalendarDatePickerFormat.cs
  95. 30
      src/Avalonia.Controls/DateTimePickers/DatePicker.cs
  96. 30
      src/Avalonia.Controls/DateTimePickers/TimePicker.cs
  97. 2
      src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs
  98. 2
      src/Avalonia.Controls/Primitives/RangeBase.cs
  99. 91
      src/Avalonia.Controls/TextBox.cs
  100. 2
      src/Avalonia.Controls/TextBoxTextInputMethodClient.cs

10
.editorconfig

@ -134,6 +134,16 @@ 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
# 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.

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" />

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

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

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/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/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/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>

2
src/Avalonia.Controls.ColorPicker/Helpers/ColorPickerHelpers.cs

@ -49,7 +49,7 @@ namespace Avalonia.Controls.Primitives
{
if (width == 0 || height == 0)
{
return new byte[0];
return Array.Empty<byte>();
}
var bitmap = await Task.Run<byte[]>(() =>

29
src/Avalonia.Controls.DataGrid/Collections/DataGridCollectionView.cs

@ -3412,23 +3412,30 @@ namespace Avalonia.Collections
RefreshOrDefer();
return;
}
object addedItem = args.NewItems?[0];
object removedItem = args.OldItems?[0];
// fire notifications for removes
if (args.Action == NotifyCollectionChangedAction.Remove ||
args.Action == NotifyCollectionChangedAction.Replace)
if (args.OldItems != null &&
(args.Action == NotifyCollectionChangedAction.Remove ||
args.Action == NotifyCollectionChangedAction.Replace))
{
ProcessRemoveEvent(removedItem, args.Action == NotifyCollectionChangedAction.Replace);
foreach (var removedItem in args.OldItems)
{
ProcessRemoveEvent(removedItem, args.Action == NotifyCollectionChangedAction.Replace);
}
}
// fire notifications for adds
if ((args.Action == NotifyCollectionChangedAction.Add ||
args.Action == NotifyCollectionChangedAction.Replace) &&
(Filter == null || PassesFilter(addedItem)))
if (args.NewItems != null &&
(args.Action == NotifyCollectionChangedAction.Add ||
args.Action == NotifyCollectionChangedAction.Replace))
{
ProcessAddEvent(addedItem, args.NewStartingIndex);
for (var i = 0; i < args.NewItems.Count; i++)
{
if (Filter == null || PassesFilter(args.NewItems[i]))
{
ProcessAddEvent(args.NewItems[i], args.NewStartingIndex + i);
}
}
}
if (args.Action != NotifyCollectionChangedAction.Replace)
{

463
src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs

@ -0,0 +1,463 @@
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see https://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Avalonia.Controls.Templates;
using Avalonia.Data;
namespace Avalonia.Controls
{
public partial class AutoCompleteBox
{
public static readonly StyledProperty<string?> WatermarkProperty =
TextBox.WatermarkProperty.AddOwner<AutoCompleteBox>();
/// <summary>
/// Identifies the <see cref="MinimumPrefixLength" /> property.
/// </summary>
/// <value>The identifier for the <see cref="MinimumPrefixLength" /> property.</value>
public static readonly StyledProperty<int> MinimumPrefixLengthProperty =
AvaloniaProperty.Register<AutoCompleteBox, int>(
nameof(MinimumPrefixLength), 1,
validate: IsValidMinimumPrefixLength);
/// <summary>
/// Identifies the <see cref="MinimumPopulateDelay" /> property.
/// </summary>
/// <value>The identifier for the <see cref="MinimumPopulateDelay" /> property.</value>
public static readonly StyledProperty<TimeSpan> MinimumPopulateDelayProperty =
AvaloniaProperty.Register<AutoCompleteBox, TimeSpan>(
nameof(MinimumPopulateDelay),
TimeSpan.Zero,
validate: IsValidMinimumPopulateDelay);
/// <summary>
/// Identifies the <see cref="MaxDropDownHeight" /> property.
/// </summary>
/// <value>The identifier for the <see cref="MaxDropDownHeight" /> property.</value>
public static readonly StyledProperty<double> MaxDropDownHeightProperty =
AvaloniaProperty.Register<AutoCompleteBox, double>(
nameof(MaxDropDownHeight),
double.PositiveInfinity,
validate: IsValidMaxDropDownHeight);
/// <summary>
/// Identifies the <see cref="IsTextCompletionEnabled" /> property.
/// </summary>
/// <value>The identifier for the <see cref="IsTextCompletionEnabled" /> property.</value>
public static readonly StyledProperty<bool> IsTextCompletionEnabledProperty =
AvaloniaProperty.Register<AutoCompleteBox, bool>(nameof(IsTextCompletionEnabled));
/// <summary>
/// Identifies the <see cref="ItemTemplate" /> property.
/// </summary>
/// <value>The identifier for the <see cref="ItemTemplate" /> property.</value>
public static readonly StyledProperty<IDataTemplate> ItemTemplateProperty =
AvaloniaProperty.Register<AutoCompleteBox, IDataTemplate>(nameof(ItemTemplate));
/// <summary>
/// Identifies the <see cref="IsDropDownOpen" /> property.
/// </summary>
/// <value>The identifier for the <see cref="IsDropDownOpen" /> property.</value>
public static readonly DirectProperty<AutoCompleteBox, bool> IsDropDownOpenProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, bool>(
nameof(IsDropDownOpen),
o => o.IsDropDownOpen,
(o, v) => o.IsDropDownOpen = v);
/// <summary>
/// Identifies the <see cref="SelectedItem" /> property.
/// </summary>
/// <value>The identifier the <see cref="SelectedItem" /> property.</value>
public static readonly DirectProperty<AutoCompleteBox, object?> SelectedItemProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, object?>(
nameof(SelectedItem),
o => o.SelectedItem,
(o, v) => o.SelectedItem = v,
defaultBindingMode: BindingMode.TwoWay,
enableDataValidation: true);
/// <summary>
/// Identifies the <see cref="Text" /> property.
/// </summary>
/// <value>The identifier for the <see cref="Text" /> property.</value>
public static readonly DirectProperty<AutoCompleteBox, string?> TextProperty =
TextBlock.TextProperty.AddOwnerWithDataValidation<AutoCompleteBox>(
o => o.Text,
(o, v) => o.Text = v,
defaultBindingMode: BindingMode.TwoWay,
enableDataValidation: true);
/// <summary>
/// Identifies the <see cref="SearchText" /> property.
/// </summary>
/// <value>The identifier for the <see cref="SearchText" /> property.</value>
public static readonly DirectProperty<AutoCompleteBox, string?> SearchTextProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, string?>(
nameof(SearchText),
o => o.SearchText,
unsetValue: string.Empty);
/// <summary>
/// Gets the identifier for the <see cref="FilterMode" /> property.
/// </summary>
public static readonly StyledProperty<AutoCompleteFilterMode> FilterModeProperty =
AvaloniaProperty.Register<AutoCompleteBox, AutoCompleteFilterMode>(
nameof(FilterMode),
defaultValue: AutoCompleteFilterMode.StartsWith,
validate: IsValidFilterMode);
/// <summary>
/// Identifies the <see cref="ItemFilter" /> property.
/// </summary>
/// <value>The identifier for the <see cref="ItemFilter" /> property.</value>
public static readonly DirectProperty<AutoCompleteBox, AutoCompleteFilterPredicate<object?>?> ItemFilterProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, AutoCompleteFilterPredicate<object?>?>(
nameof(ItemFilter),
o => o.ItemFilter,
(o, v) => o.ItemFilter = v);
/// <summary>
/// Identifies the <see cref="TextFilter" /> property.
/// </summary>
/// <value>The identifier for the <see cref="TextFilter" /> property.</value>
public static readonly DirectProperty<AutoCompleteBox, AutoCompleteFilterPredicate<string?>?> TextFilterProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, AutoCompleteFilterPredicate<string?>?>(
nameof(TextFilter),
o => o.TextFilter,
(o, v) => o.TextFilter = v,
unsetValue: AutoCompleteSearch.GetFilter(AutoCompleteFilterMode.StartsWith));
/// <summary>
/// Identifies the <see cref="ItemSelector" /> property.
/// </summary>
/// <value>The identifier for the <see cref="ItemSelector" /> property.</value>
public static readonly DirectProperty<AutoCompleteBox, AutoCompleteSelector<object>?> ItemSelectorProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, AutoCompleteSelector<object>?>(
nameof(ItemSelector),
o => o.ItemSelector,
(o, v) => o.ItemSelector = v);
/// <summary>
/// Identifies the <see cref="TextSelector" /> property.
/// </summary>
/// <value>The identifier for the <see cref="TextSelector" /> property.</value>
public static readonly DirectProperty<AutoCompleteBox, AutoCompleteSelector<string?>?> TextSelectorProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, AutoCompleteSelector<string?>?>(
nameof(TextSelector),
o => o.TextSelector,
(o, v) => o.TextSelector = v);
/// <summary>
/// Identifies the <see cref="Items" /> property.
/// </summary>
/// <value>The identifier for the <see cref="Items" /> property.</value>
public static readonly DirectProperty<AutoCompleteBox, IEnumerable?> ItemsProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, IEnumerable?>(
nameof(Items),
o => o.Items,
(o, v) => o.Items = v);
public static readonly DirectProperty<AutoCompleteBox, Func<string?, CancellationToken, Task<IEnumerable<object>>>?> AsyncPopulatorProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, Func<string?, CancellationToken, Task<IEnumerable<object>>>?>(
nameof(AsyncPopulator),
o => o.AsyncPopulator,
(o, v) => o.AsyncPopulator = v);
/// <summary>
/// Gets or sets the minimum number of characters required to be entered
/// in the text box before the <see cref="AutoCompleteBox" /> displays possible matches.
/// </summary>
/// <value>
/// The minimum number of characters to be entered in the text box
/// before the <see cref="AutoCompleteBox" />
/// displays possible matches. The default is 1.
/// </value>
/// <remarks>
/// If you set MinimumPrefixLength to -1, the AutoCompleteBox will
/// not provide possible matches. There is no maximum value, but
/// setting MinimumPrefixLength to value that is too large will
/// prevent the AutoCompleteBox from providing possible matches as well.
/// </remarks>
public int MinimumPrefixLength
{
get => GetValue(MinimumPrefixLengthProperty);
set => SetValue(MinimumPrefixLengthProperty, value);
}
/// <summary>
/// Gets or sets a value indicating whether the first possible match
/// found during the filtering process will be displayed automatically
/// in the text box.
/// </summary>
/// <value>
/// True if the first possible match found will be displayed
/// automatically in the text box; otherwise, false. The default is
/// false.
/// </value>
public bool IsTextCompletionEnabled
{
get => GetValue(IsTextCompletionEnabledProperty);
set => SetValue(IsTextCompletionEnabledProperty, value);
}
/// <summary>
/// Gets or sets the <see cref="T:Avalonia.DataTemplate" /> used
/// to display each item in the drop-down portion of the control.
/// </summary>
/// <value>The <see cref="T:Avalonia.DataTemplate" /> used to
/// display each item in the drop-down. The default is null.</value>
/// <remarks>
/// You use the ItemTemplate property to specify the visualization
/// of the data objects in the drop-down portion of the AutoCompleteBox
/// control. If your AutoCompleteBox is bound to a collection and you
/// do not provide specific display instructions by using a
/// DataTemplate, the resulting UI of each item is a string
/// representation of each object in the underlying collection.
/// </remarks>
public IDataTemplate ItemTemplate
{
get => GetValue(ItemTemplateProperty);
set => SetValue(ItemTemplateProperty, value);
}
/// <summary>
/// Gets or sets the minimum delay, after text is typed
/// in the text box before the
/// <see cref="AutoCompleteBox" /> control
/// populates the list of possible matches in the drop-down.
/// </summary>
/// <value>The minimum delay, after text is typed in
/// the text box, but before the
/// <see cref="AutoCompleteBox" /> populates
/// the list of possible matches in the drop-down. The default is 0.</value>
public TimeSpan MinimumPopulateDelay
{
get => GetValue(MinimumPopulateDelayProperty);
set => SetValue(MinimumPopulateDelayProperty, value);
}
/// <summary>
/// Gets or sets the maximum height of the drop-down portion of the
/// <see cref="AutoCompleteBox" /> control.
/// </summary>
/// <value>The maximum height of the drop-down portion of the
/// <see cref="AutoCompleteBox" /> control.
/// The default is <see cref="F:System.Double.PositiveInfinity" />.</value>
/// <exception cref="T:System.ArgumentException">The specified value is less than 0.</exception>
public double MaxDropDownHeight
{
get => GetValue(MaxDropDownHeightProperty);
set => SetValue(MaxDropDownHeightProperty, value);
}
/// <summary>
/// Gets or sets a value indicating whether the drop-down portion of
/// the control is open.
/// </summary>
/// <value>
/// True if the drop-down is open; otherwise, false. The default is
/// false.
/// </value>
public bool IsDropDownOpen
{
get => _isDropDownOpen;
set => SetAndRaise(IsDropDownOpenProperty, ref _isDropDownOpen, value);
}
/// <summary>
/// Gets or sets the <see cref="T:Avalonia.Data.Binding" /> that
/// is used to get the values for display in the text portion of
/// the <see cref="AutoCompleteBox" />
/// control.
/// </summary>
/// <value>The <see cref="T:Avalonia.Data.IBinding" /> object used
/// when binding to a collection property.</value>
[AssignBinding]
public IBinding? ValueMemberBinding
{
get => _valueBindingEvaluator?.ValueBinding;
set
{
if (ValueMemberBinding != value)
{
_valueBindingEvaluator = new BindingEvaluator<string>(value);
OnValueMemberBindingChanged(value);
}
}
}
/// <summary>
/// Gets or sets the selected item in the drop-down.
/// </summary>
/// <value>The selected item in the drop-down.</value>
/// <remarks>
/// If the IsTextCompletionEnabled property is true and text typed by
/// the user matches an item in the ItemsSource collection, which is
/// then displayed in the text box, the SelectedItem property will be
/// a null reference.
/// </remarks>
public object? SelectedItem
{
get => _selectedItem;
set => SetAndRaise(SelectedItemProperty, ref _selectedItem, value);
}
/// <summary>
/// Gets or sets the text in the text box portion of the
/// <see cref="AutoCompleteBox" /> control.
/// </summary>
/// <value>The text in the text box portion of the
/// <see cref="AutoCompleteBox" /> control.</value>
public string? Text
{
get => _text;
set => SetAndRaise(TextProperty, ref _text, value);
}
/// <summary>
/// Gets the text that is used to filter items in the
/// <see cref="Items" /> item collection.
/// </summary>
/// <value>The text that is used to filter items in the
/// <see cref="Items" /> item collection.</value>
/// <remarks>
/// The SearchText value is typically the same as the
/// Text property, but is set after the TextChanged event occurs
/// and before the Populating event.
/// </remarks>
public string? SearchText
{
get => _searchText;
private set
{
try
{
_allowWrite = true;
SetAndRaise(SearchTextProperty, ref _searchText, value);
}
finally
{
_allowWrite = false;
}
}
}
/// <summary>
/// Gets or sets how the text in the text box is used to filter items
/// specified by the <see cref="Items" />
/// property for display in the drop-down.
/// </summary>
/// <value>One of the <see cref="AutoCompleteFilterMode" />
/// values The default is <see cref="AutoCompleteFilterMode.StartsWith" />.</value>
/// <exception cref="T:System.ArgumentException">The specified value is not a valid
/// <see cref="AutoCompleteFilterMode" />.</exception>
/// <remarks>
/// Use the FilterMode property to specify how possible matches are
/// filtered. For example, possible matches can be filtered in a
/// predefined or custom way. The search mode is automatically set to
/// Custom if you set the ItemFilter property.
/// </remarks>
public AutoCompleteFilterMode FilterMode
{
get => GetValue(FilterModeProperty);
set => SetValue(FilterModeProperty, value);
}
public string? Watermark
{
get => GetValue(WatermarkProperty);
set => SetValue(WatermarkProperty, value);
}
/// <summary>
/// Gets or sets the custom method that uses user-entered text to filter
/// the items specified by the <see cref="Items" />
/// property for display in the drop-down.
/// </summary>
/// <value>The custom method that uses the user-entered text to filter
/// the items specified by the <see cref="Items" />
/// property. The default is null.</value>
/// <remarks>
/// The filter mode is automatically set to Custom if you set the
/// ItemFilter property.
/// </remarks>
public AutoCompleteFilterPredicate<object?>? ItemFilter
{
get => _itemFilter;
set => SetAndRaise(ItemFilterProperty, ref _itemFilter, value);
}
/// <summary>
/// Gets or sets the custom method that uses the user-entered text to
/// filter items specified by the <see cref="Items" />
/// property in a text-based way for display in the drop-down.
/// </summary>
/// <value>The custom method that uses the user-entered text to filter
/// items specified by the <see cref="Items" />
/// property in a text-based way for display in the drop-down.</value>
/// <remarks>
/// The search mode is automatically set to Custom if you set the
/// TextFilter property.
/// </remarks>
public AutoCompleteFilterPredicate<string?>? TextFilter
{
get => _textFilter;
set => SetAndRaise(TextFilterProperty, ref _textFilter, value);
}
/// <summary>
/// Gets or sets the custom method that combines the user-entered
/// text and one of the items specified by the <see cref="Items" />.
/// </summary>
/// <value>
/// The custom method that combines the user-entered
/// text and one of the items specified by the <see cref="Items" />.
/// </value>
public AutoCompleteSelector<object>? ItemSelector
{
get => _itemSelector;
set => SetAndRaise(ItemSelectorProperty, ref _itemSelector, value);
}
/// <summary>
/// Gets or sets the custom method that combines the user-entered
/// text and one of the items specified by the
/// <see cref="Items" /> in a text-based way.
/// </summary>
/// <value>
/// The custom method that combines the user-entered
/// text and one of the items specified by the <see cref="Items" />
/// in a text-based way.
/// </value>
public AutoCompleteSelector<string?>? TextSelector
{
get => _textSelector;
set => SetAndRaise(TextSelectorProperty, ref _textSelector, value);
}
public Func<string?, CancellationToken, Task<IEnumerable<object>>>? AsyncPopulator
{
get => _asyncPopulator;
set => SetAndRaise(AsyncPopulatorProperty, ref _asyncPopulator, value);
}
/// <summary>
/// Gets or sets a collection that is used to generate the items for the
/// drop-down portion of the <see cref="AutoCompleteBox" /> control.
/// </summary>
/// <value>The collection that is used to generate the items of the
/// drop-down portion of the <see cref="AutoCompleteBox" /> control.</value>
public IEnumerable? Items
{
get => _itemsEnumerable;
set => SetAndRaise(ItemsProperty, ref _itemsEnumerable, value);
}
}
}

756
src/Avalonia.Controls/AutoCompleteBox.cs → src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs

@ -1,6 +1,6 @@
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// Please see https://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
using System;
@ -26,65 +26,6 @@ using Avalonia.VisualTree;
namespace Avalonia.Controls
{
/// <summary>
/// Provides data for the
/// <see cref="E:Avalonia.Controls.AutoCompleteBox.Populated" />
/// event.
/// </summary>
public class PopulatedEventArgs : EventArgs
{
/// <summary>
/// Gets the list of possible matches added to the drop-down portion of
/// the <see cref="T:Avalonia.Controls.AutoCompleteBox" />
/// control.
/// </summary>
/// <value>The list of possible matches added to the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" />.</value>
public IEnumerable Data { get; private set; }
/// <summary>
/// Initializes a new instance of the
/// <see cref="T:Avalonia.Controls.PopulatedEventArgs" />.
/// </summary>
/// <param name="data">The list of possible matches added to the
/// drop-down portion of the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> control.</param>
public PopulatedEventArgs(IEnumerable data)
{
Data = data;
}
}
/// <summary>
/// Provides data for the
/// <see cref="E:Avalonia.Controls.AutoCompleteBox.Populating" />
/// event.
/// </summary>
public class PopulatingEventArgs : CancelEventArgs
{
/// <summary>
/// Gets the text that is used to determine which items to display in
/// the <see cref="T:Avalonia.Controls.AutoCompleteBox" />
/// control.
/// </summary>
/// <value>The text that is used to determine which items to display in
/// the <see cref="T:Avalonia.Controls.AutoCompleteBox" />.</value>
public string? Parameter { get; private set; }
/// <summary>
/// Initializes a new instance of the
/// <see cref="T:Avalonia.Controls.PopulatingEventArgs" />.
/// </summary>
/// <param name="parameter">The value of the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.SearchText" />
/// property, which is used to filter items for the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> control.</param>
public PopulatingEventArgs(string? parameter)
{
Parameter = parameter;
}
}
/// <summary>
/// Represents the filter used by the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> control to
@ -100,132 +41,6 @@ namespace Avalonia.Controls
/// be either a string or an object.</typeparam>
public delegate bool AutoCompleteFilterPredicate<T>(string? search, T item);
/// <summary>
/// Specifies how text in the text box portion of the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> control is used
/// to filter items specified by the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemsSource" />
/// property for display in the drop-down.
/// </summary>
public enum AutoCompleteFilterMode
{
/// <summary>
/// Specifies that no filter is used. All items are returned.
/// </summary>
None = 0,
/// <summary>
/// Specifies a culture-sensitive, case-insensitive filter where the
/// returned items start with the specified text. The filter uses the
/// <see cref="M:System.String.StartsWith(System.String,System.StringComparison)" />
/// method, specifying
/// <see cref="P:System.StringComparer.CurrentCultureIgnoreCase" /> as
/// the string comparison criteria.
/// </summary>
StartsWith = 1,
/// <summary>
/// Specifies a culture-sensitive, case-sensitive filter where the
/// returned items start with the specified text. The filter uses the
/// <see cref="M:System.String.StartsWith(System.String,System.StringComparison)" />
/// method, specifying
/// <see cref="P:System.StringComparer.CurrentCulture" /> as the string
/// comparison criteria.
/// </summary>
StartsWithCaseSensitive = 2,
/// <summary>
/// Specifies an ordinal, case-insensitive filter where the returned
/// items start with the specified text. The filter uses the
/// <see cref="M:System.String.StartsWith(System.String,System.StringComparison)" />
/// method, specifying
/// <see cref="P:System.StringComparer.OrdinalIgnoreCase" /> as the
/// string comparison criteria.
/// </summary>
StartsWithOrdinal = 3,
/// <summary>
/// Specifies an ordinal, case-sensitive filter where the returned items
/// start with the specified text. The filter uses the
/// <see cref="M:System.String.StartsWith(System.String,System.StringComparison)" />
/// method, specifying <see cref="P:System.StringComparer.Ordinal" /> as
/// the string comparison criteria.
/// </summary>
StartsWithOrdinalCaseSensitive = 4,
/// <summary>
/// Specifies a culture-sensitive, case-insensitive filter where the
/// returned items contain the specified text.
/// </summary>
Contains = 5,
/// <summary>
/// Specifies a culture-sensitive, case-sensitive filter where the
/// returned items contain the specified text.
/// </summary>
ContainsCaseSensitive = 6,
/// <summary>
/// Specifies an ordinal, case-insensitive filter where the returned
/// items contain the specified text.
/// </summary>
ContainsOrdinal = 7,
/// <summary>
/// Specifies an ordinal, case-sensitive filter where the returned items
/// contain the specified text.
/// </summary>
ContainsOrdinalCaseSensitive = 8,
/// <summary>
/// Specifies a culture-sensitive, case-insensitive filter where the
/// returned items equal the specified text. The filter uses the
/// <see cref="M:System.String.Equals(System.String,System.StringComparison)" />
/// method, specifying
/// <see cref="P:System.StringComparer.CurrentCultureIgnoreCase" /> as
/// the search comparison criteria.
/// </summary>
Equals = 9,
/// <summary>
/// Specifies a culture-sensitive, case-sensitive filter where the
/// returned items equal the specified text. The filter uses the
/// <see cref="M:System.String.Equals(System.String,System.StringComparison)" />
/// method, specifying
/// <see cref="P:System.StringComparer.CurrentCulture" /> as the string
/// comparison criteria.
/// </summary>
EqualsCaseSensitive = 10,
/// <summary>
/// Specifies an ordinal, case-insensitive filter where the returned
/// items equal the specified text. The filter uses the
/// <see cref="M:System.String.Equals(System.String,System.StringComparison)" />
/// method, specifying
/// <see cref="P:System.StringComparer.OrdinalIgnoreCase" /> as the
/// string comparison criteria.
/// </summary>
EqualsOrdinal = 11,
/// <summary>
/// Specifies an ordinal, case-sensitive filter where the returned items
/// equal the specified text. The filter uses the
/// <see cref="M:System.String.Equals(System.String,System.StringComparison)" />
/// method, specifying <see cref="P:System.StringComparer.Ordinal" /> as
/// the string comparison criteria.
/// </summary>
EqualsOrdinalCaseSensitive = 12,
/// <summary>
/// Specifies that a custom filter is used. This mode is used when the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.TextFilter" />
/// or
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemFilter" />
/// properties are set.
/// </summary>
Custom = 13,
}
/// <summary>
/// Represents the selector used by the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> control to
@ -257,7 +72,7 @@ namespace Avalonia.Controls
[TemplatePart(ElementSelectionAdapter, typeof(ISelectionAdapter))]
[TemplatePart(ElementTextBox, typeof(TextBox))]
[PseudoClasses(":dropdownopen")]
public class AutoCompleteBox : TemplatedControl
public partial class AutoCompleteBox : TemplatedControl
{
/// <summary>
/// Specifies the name of the selection adapter TemplatePart.
@ -394,221 +209,22 @@ namespace Avalonia.Controls
private readonly EventHandler _populateDropDownHandler;
public static readonly RoutedEvent<SelectionChangedEventArgs> SelectionChangedEvent =
RoutedEvent.Register<SelectionChangedEventArgs>(nameof(SelectionChanged), RoutingStrategies.Bubble, typeof(AutoCompleteBox));
public static readonly StyledProperty<string?> WatermarkProperty =
TextBox.WatermarkProperty.AddOwner<AutoCompleteBox>();
/// <summary>
/// Identifies the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.MinimumPrefixLength" />
/// dependency property.
/// </summary>
/// <value>The identifier for the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.MinimumPrefixLength" />
/// dependency property.</value>
public static readonly StyledProperty<int> MinimumPrefixLengthProperty =
AvaloniaProperty.Register<AutoCompleteBox, int>(
nameof(MinimumPrefixLength), 1,
validate: IsValidMinimumPrefixLength);
/// <summary>
/// Identifies the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.MinimumPopulateDelay" />
/// dependency property.
/// </summary>
/// <value>The identifier for the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.MinimumPopulateDelay" />
/// dependency property.</value>
public static readonly StyledProperty<TimeSpan> MinimumPopulateDelayProperty =
AvaloniaProperty.Register<AutoCompleteBox, TimeSpan>(
nameof(MinimumPopulateDelay),
TimeSpan.Zero,
validate: IsValidMinimumPopulateDelay);
/// <summary>
/// Identifies the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.MaxDropDownHeight" />
/// dependency property.
///
/// </summary>
/// <value>The identifier for the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.MaxDropDownHeight" />
/// dependency property.</value>
public static readonly StyledProperty<double> MaxDropDownHeightProperty =
AvaloniaProperty.Register<AutoCompleteBox, double>(
nameof(MaxDropDownHeight),
double.PositiveInfinity,
validate: IsValidMaxDropDownHeight);
/// <summary>
/// Identifies the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.IsTextCompletionEnabled" />
/// dependency property.
/// </summary>
/// <value>The identifier for the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.IsTextCompletionEnabled" />
/// dependency property.</value>
public static readonly StyledProperty<bool> IsTextCompletionEnabledProperty =
AvaloniaProperty.Register<AutoCompleteBox, bool>(nameof(IsTextCompletionEnabled));
public static readonly RoutedEvent<SelectionChangedEventArgs> SelectionChangedEvent =
RoutedEvent.Register<SelectionChangedEventArgs>(
nameof(SelectionChanged),
RoutingStrategies.Bubble,
typeof(AutoCompleteBox));
/// <summary>
/// Identifies the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemTemplate" />
/// dependency property.
/// Defines the <see cref="TextChanged"/> event.
/// </summary>
/// <value>The identifier for the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemTemplate" />
/// dependency property.</value>
public static readonly StyledProperty<IDataTemplate> ItemTemplateProperty =
AvaloniaProperty.Register<AutoCompleteBox, IDataTemplate>(nameof(ItemTemplate));
/// <summary>
/// Identifies the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.IsDropDownOpen" />
/// dependency property.
/// </summary>
/// <value>The identifier for the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.IsDropDownOpen" />
/// dependency property.</value>
public static readonly DirectProperty<AutoCompleteBox, bool> IsDropDownOpenProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, bool>(
nameof(IsDropDownOpen),
o => o.IsDropDownOpen,
(o, v) => o.IsDropDownOpen = v);
/// <summary>
/// Identifies the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.SelectedItem" />
/// dependency property.
/// </summary>
/// <value>The identifier the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.SelectedItem" />
/// dependency property.</value>
public static readonly DirectProperty<AutoCompleteBox, object?> SelectedItemProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, object?>(
nameof(SelectedItem),
o => o.SelectedItem,
(o, v) => o.SelectedItem = v,
defaultBindingMode: BindingMode.TwoWay,
enableDataValidation: true);
/// <summary>
/// Identifies the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.Text" />
/// dependency property.
/// </summary>
/// <value>The identifier for the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.Text" />
/// dependency property.</value>
public static readonly DirectProperty<AutoCompleteBox, string?> TextProperty =
TextBlock.TextProperty.AddOwnerWithDataValidation<AutoCompleteBox>(
o => o.Text,
(o, v) => o.Text = v,
defaultBindingMode: BindingMode.TwoWay,
enableDataValidation: true);
/// <summary>
/// Identifies the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.SearchText" />
/// dependency property.
/// </summary>
/// <value>The identifier for the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.SearchText" />
/// dependency property.</value>
public static readonly DirectProperty<AutoCompleteBox, string?> SearchTextProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, string?>(
nameof(SearchText),
o => o.SearchText,
unsetValue: string.Empty);
/// <summary>
/// Gets the identifier for the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.FilterMode" />
/// dependency property.
/// </summary>
public static readonly StyledProperty<AutoCompleteFilterMode> FilterModeProperty =
AvaloniaProperty.Register<AutoCompleteBox, AutoCompleteFilterMode>(
nameof(FilterMode),
defaultValue: AutoCompleteFilterMode.StartsWith,
validate: IsValidFilterMode);
/// <summary>
/// Identifies the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemFilter" />
/// dependency property.
/// </summary>
/// <value>The identifier for the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemFilter" />
/// dependency property.</value>
public static readonly DirectProperty<AutoCompleteBox, AutoCompleteFilterPredicate<object?>?> ItemFilterProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, AutoCompleteFilterPredicate<object?>?>(
nameof(ItemFilter),
o => o.ItemFilter,
(o, v) => o.ItemFilter = v);
/// <summary>
/// Identifies the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.TextFilter" />
/// dependency property.
/// </summary>
/// <value>The identifier for the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.TextFilter" />
/// dependency property.</value>
public static readonly DirectProperty<AutoCompleteBox, AutoCompleteFilterPredicate<string?>?> TextFilterProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, AutoCompleteFilterPredicate<string?>?>(
nameof(TextFilter),
o => o.TextFilter,
(o, v) => o.TextFilter = v,
unsetValue: AutoCompleteSearch.GetFilter(AutoCompleteFilterMode.StartsWith));
/// <summary>
/// Identifies the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemSelector" />
/// dependency property.
/// </summary>
/// <value>The identifier for the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemSelector" />
/// dependency property.</value>
public static readonly DirectProperty<AutoCompleteBox, AutoCompleteSelector<object>?> ItemSelectorProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, AutoCompleteSelector<object>?>(
nameof(ItemSelector),
o => o.ItemSelector,
(o, v) => o.ItemSelector = v);
/// <summary>
/// Identifies the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.TextSelector" />
/// dependency property.
/// </summary>
/// <value>The identifier for the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.TextSelector" />
/// dependency property.</value>
public static readonly DirectProperty<AutoCompleteBox, AutoCompleteSelector<string?>?> TextSelectorProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, AutoCompleteSelector<string?>?>(
nameof(TextSelector),
o => o.TextSelector,
(o, v) => o.TextSelector = v);
/// <summary>
/// Identifies the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemsSource" />
/// dependency property.
/// </summary>
/// <value>The identifier for the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemsSource" />
/// dependency property.</value>
public static readonly DirectProperty<AutoCompleteBox, IEnumerable?> ItemsProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, IEnumerable?>(
nameof(Items),
o => o.Items,
(o, v) => o.Items = v);
public static readonly DirectProperty<AutoCompleteBox, Func<string?, CancellationToken, Task<IEnumerable<object>>>?> AsyncPopulatorProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, Func<string?, CancellationToken, Task<IEnumerable<object>>>?>(
nameof(AsyncPopulator),
o => o.AsyncPopulator,
(o, v) => o.AsyncPopulator = v);
public static readonly RoutedEvent<TextChangedEventArgs> TextChangedEvent =
RoutedEvent.Register<AutoCompleteBox, TextChangedEventArgs>(
nameof(TextChanged),
RoutingStrategies.Bubble);
private static bool IsValidMinimumPrefixLength(int value) => value >= -1;
@ -871,315 +487,6 @@ namespace Avalonia.Controls
ClearView();
}
/// <summary>
/// Gets or sets the minimum number of characters required to be entered
/// in the text box before the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> displays
/// possible matches.
/// matches.
/// </summary>
/// <value>
/// The minimum number of characters to be entered in the text box
/// before the <see cref="T:Avalonia.Controls.AutoCompleteBox" />
/// displays possible matches. The default is 1.
/// </value>
/// <remarks>
/// If you set MinimumPrefixLength to -1, the AutoCompleteBox will
/// not provide possible matches. There is no maximum value, but
/// setting MinimumPrefixLength to value that is too large will
/// prevent the AutoCompleteBox from providing possible matches as well.
/// </remarks>
public int MinimumPrefixLength
{
get { return GetValue(MinimumPrefixLengthProperty); }
set { SetValue(MinimumPrefixLengthProperty, value); }
}
/// <summary>
/// Gets or sets a value indicating whether the first possible match
/// found during the filtering process will be displayed automatically
/// in the text box.
/// </summary>
/// <value>
/// True if the first possible match found will be displayed
/// automatically in the text box; otherwise, false. The default is
/// false.
/// </value>
public bool IsTextCompletionEnabled
{
get { return GetValue(IsTextCompletionEnabledProperty); }
set { SetValue(IsTextCompletionEnabledProperty, value); }
}
/// <summary>
/// Gets or sets the <see cref="T:Avalonia.DataTemplate" /> used
/// to display each item in the drop-down portion of the control.
/// </summary>
/// <value>The <see cref="T:Avalonia.DataTemplate" /> used to
/// display each item in the drop-down. The default is null.</value>
/// <remarks>
/// You use the ItemTemplate property to specify the visualization
/// of the data objects in the drop-down portion of the AutoCompleteBox
/// control. If your AutoCompleteBox is bound to a collection and you
/// do not provide specific display instructions by using a
/// DataTemplate, the resulting UI of each item is a string
/// representation of each object in the underlying collection.
/// </remarks>
public IDataTemplate ItemTemplate
{
get { return GetValue(ItemTemplateProperty); }
set { SetValue(ItemTemplateProperty, value); }
}
/// <summary>
/// Gets or sets the minimum delay, after text is typed
/// in the text box before the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> control
/// populates the list of possible matches in the drop-down.
/// </summary>
/// <value>The minimum delay, after text is typed in
/// the text box, but before the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> populates
/// the list of possible matches in the drop-down. The default is 0.</value>
public TimeSpan MinimumPopulateDelay
{
get { return GetValue(MinimumPopulateDelayProperty); }
set { SetValue(MinimumPopulateDelayProperty, value); }
}
/// <summary>
/// Gets or sets the maximum height of the drop-down portion of the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> control.
/// </summary>
/// <value>The maximum height of the drop-down portion of the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> control.
/// The default is <see cref="F:System.Double.PositiveInfinity" />.</value>
/// <exception cref="T:System.ArgumentException">The specified value is less than 0.</exception>
public double MaxDropDownHeight
{
get { return GetValue(MaxDropDownHeightProperty); }
set { SetValue(MaxDropDownHeightProperty, value); }
}
/// <summary>
/// Gets or sets a value indicating whether the drop-down portion of
/// the control is open.
/// </summary>
/// <value>
/// True if the drop-down is open; otherwise, false. The default is
/// false.
/// </value>
public bool IsDropDownOpen
{
get { return _isDropDownOpen; }
set { SetAndRaise(IsDropDownOpenProperty, ref _isDropDownOpen, value); }
}
/// <summary>
/// Gets or sets the <see cref="T:Avalonia.Data.Binding" /> that
/// is used to get the values for display in the text portion of
/// the <see cref="T:Avalonia.Controls.AutoCompleteBox" />
/// control.
/// </summary>
/// <value>The <see cref="T:Avalonia.Data.IBinding" /> object used
/// when binding to a collection property.</value>
[AssignBinding]
public IBinding? ValueMemberBinding
{
get { return _valueBindingEvaluator?.ValueBinding; }
set
{
if (ValueMemberBinding != value)
{
_valueBindingEvaluator = new BindingEvaluator<string>(value);
OnValueMemberBindingChanged(value);
}
}
}
/// <summary>
/// Gets or sets the selected item in the drop-down.
/// </summary>
/// <value>The selected item in the drop-down.</value>
/// <remarks>
/// If the IsTextCompletionEnabled property is true and text typed by
/// the user matches an item in the ItemsSource collection, which is
/// then displayed in the text box, the SelectedItem property will be
/// a null reference.
/// </remarks>
public object? SelectedItem
{
get { return _selectedItem; }
set { SetAndRaise(SelectedItemProperty, ref _selectedItem, value); }
}
/// <summary>
/// Gets or sets the text in the text box portion of the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> control.
/// </summary>
/// <value>The text in the text box portion of the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> control.</value>
public string? Text
{
get { return _text; }
set { SetAndRaise(TextProperty, ref _text, value); }
}
/// <summary>
/// Gets the text that is used to filter items in the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemsSource" />
/// item collection.
/// </summary>
/// <value>The text that is used to filter items in the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemsSource" />
/// item collection.</value>
/// <remarks>
/// The SearchText value is typically the same as the
/// Text property, but is set after the TextChanged event occurs
/// and before the Populating event.
/// </remarks>
public string? SearchText
{
get { return _searchText; }
private set
{
try
{
_allowWrite = true;
SetAndRaise(SearchTextProperty, ref _searchText, value);
}
finally
{
_allowWrite = false;
}
}
}
/// <summary>
/// Gets or sets how the text in the text box is used to filter items
/// specified by the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemsSource" />
/// property for display in the drop-down.
/// </summary>
/// <value>One of the
/// <see cref="T:Avalonia.Controls.AutoCompleteFilterMode" />
/// values The default is
/// <see cref="F:Avalonia.Controls.AutoCompleteFilterMode.StartsWith" />.</value>
/// <exception cref="T:System.ArgumentException">The specified value is
/// not a valid
/// <see cref="T:Avalonia.Controls.AutoCompleteFilterMode" />.</exception>
/// <remarks>
/// Use the FilterMode property to specify how possible matches are
/// filtered. For example, possible matches can be filtered in a
/// predefined or custom way. The search mode is automatically set to
/// Custom if you set the ItemFilter property.
/// </remarks>
public AutoCompleteFilterMode FilterMode
{
get { return GetValue(FilterModeProperty); }
set { SetValue(FilterModeProperty, value); }
}
public string? Watermark
{
get { return GetValue(WatermarkProperty); }
set { SetValue(WatermarkProperty, value); }
}
/// <summary>
/// Gets or sets the custom method that uses user-entered text to filter
/// the items specified by the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemsSource" />
/// property for display in the drop-down.
/// </summary>
/// <value>The custom method that uses the user-entered text to filter
/// the items specified by the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemsSource" />
/// property. The default is null.</value>
/// <remarks>
/// The filter mode is automatically set to Custom if you set the
/// ItemFilter property.
/// </remarks>
public AutoCompleteFilterPredicate<object?>? ItemFilter
{
get { return _itemFilter; }
set { SetAndRaise(ItemFilterProperty, ref _itemFilter, value); }
}
/// <summary>
/// Gets or sets the custom method that uses the user-entered text to
/// filter items specified by the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemsSource" />
/// property in a text-based way for display in the drop-down.
/// </summary>
/// <value>The custom method that uses the user-entered text to filter
/// items specified by the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemsSource" />
/// property in a text-based way for display in the drop-down.</value>
/// <remarks>
/// The search mode is automatically set to Custom if you set the
/// TextFilter property.
/// </remarks>
public AutoCompleteFilterPredicate<string?>? TextFilter
{
get { return _textFilter; }
set { SetAndRaise(TextFilterProperty, ref _textFilter, value); }
}
/// <summary>
/// Gets or sets the custom method that combines the user-entered
/// text and one of the items specified by the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemsSource" />.
/// </summary>
/// <value>
/// The custom method that combines the user-entered
/// text and one of the items specified by the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemsSource" />.
/// </value>
public AutoCompleteSelector<object>? ItemSelector
{
get { return _itemSelector; }
set { SetAndRaise(ItemSelectorProperty, ref _itemSelector, value); }
}
/// <summary>
/// Gets or sets the custom method that combines the user-entered
/// text and one of the items specified by the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemsSource" />
/// in a text-based way.
/// </summary>
/// <value>
/// The custom method that combines the user-entered
/// text and one of the items specified by the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.ItemsSource" />
/// in a text-based way.
/// </value>
public AutoCompleteSelector<string?>? TextSelector
{
get { return _textSelector; }
set { SetAndRaise(TextSelectorProperty, ref _textSelector, value); }
}
public Func<string?, CancellationToken, Task<IEnumerable<object>>>? AsyncPopulator
{
get { return _asyncPopulator; }
set { SetAndRaise(AsyncPopulatorProperty, ref _asyncPopulator, value); }
}
/// <summary>
/// Gets or sets a collection that is used to generate the items for the
/// drop-down portion of the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> control.
/// </summary>
/// <value>The collection that is used to generate the items of the
/// drop-down portion of the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> control.</value>
public IEnumerable? Items
{
get { return _itemsEnumerable; }
set { SetAndRaise(ItemsProperty, ref _itemsEnumerable, value); }
}
/// <summary>
/// Gets or sets the drop down popup control.
/// </summary>
@ -1190,7 +497,7 @@ namespace Avalonia.Controls
/// </summary>
private TextBox? TextBox
{
get { return _textBox; }
get => _textBox;
set
{
_textBoxSubscriptions?.Dispose();
@ -1254,7 +561,7 @@ namespace Avalonia.Controls
/// </remarks>
protected ISelectionAdapter? SelectionAdapter
{
get { return _adapter; }
get => _adapter;
set
{
if (_adapter != null)
@ -1529,10 +836,14 @@ namespace Avalonia.Controls
}
/// <summary>
/// Occurs when the text in the text box portion of the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> changes.
/// Occurs asynchronously when the text in the <see cref="TextBox"/> portion of the
/// <see cref="AutoCompleteBox" /> changes.
/// </summary>
public event EventHandler? TextChanged;
public event EventHandler<TextChangedEventArgs>? TextChanged
{
add => AddHandler(TextChangedEvent, value);
remove => RemoveHandler(TextChangedEvent, value);
}
/// <summary>
/// Occurs when the
@ -1690,15 +1001,12 @@ namespace Avalonia.Controls
}
/// <summary>
/// Raises the
/// <see cref="E:Avalonia.Controls.AutoCompleteBox.TextChanged" />
/// event.
/// Raises the <see cref="TextChanged" /> event.
/// </summary>
/// <param name="e">A <see cref="T:Avalonia.RoutedEventArgs" />
/// that contains the event data.</param>
protected virtual void OnTextChanged(RoutedEventArgs e)
/// <param name="e">A <see cref="TextChangedEventArgs" /> that contains the event data.</param>
protected virtual void OnTextChanged(TextChangedEventArgs e)
{
TextChanged?.Invoke(this, e);
RaiseEvent(e);
}
/// <summary>
@ -1985,7 +1293,7 @@ namespace Avalonia.Controls
if (callTextChanged)
{
OnTextChanged(new RoutedEventArgs());
OnTextChanged(new TextChangedEventArgs(TextChangedEvent));
}
}
@ -2740,8 +2048,6 @@ namespace Avalonia.Controls
/// </summary>
private IBinding? _binding;
#region public T Value
/// <summary>
/// Identifies the Value dependency property.
/// </summary>
@ -2753,18 +2059,16 @@ namespace Avalonia.Controls
/// </summary>
public T Value
{
get { return GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
get => GetValue(ValueProperty);
set => SetValue(ValueProperty, value);
}
#endregion public string Value
/// <summary>
/// Gets or sets the value binding.
/// </summary>
public IBinding? ValueBinding
{
get { return _binding; }
get => _binding;
set
{
_binding = value;

131
src/Avalonia.Controls/AutoCompleteBox/AutoCompleteFilterMode.cs

@ -0,0 +1,131 @@
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see https://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
using System;
namespace Avalonia.Controls
{
/// <summary>
/// Specifies how text in the text box portion of the <see cref="AutoCompleteBox" />
/// control is used to filter items specified by the <see cref="AutoCompleteBox.Items" />
/// property for display in the drop-down.
/// </summary>
public enum AutoCompleteFilterMode
{
/// <summary>
/// Specifies that no filter is used. All items are returned.
/// </summary>
None = 0,
/// <summary>
/// Specifies a culture-sensitive, case-insensitive filter where the
/// returned items start with the specified text. The filter uses the
/// <see cref="String.StartsWith(String,StringComparison)" />
/// method, specifying
/// <see cref="StringComparer.CurrentCultureIgnoreCase" /> as
/// the string comparison criteria.
/// </summary>
StartsWith = 1,
/// <summary>
/// Specifies a culture-sensitive, case-sensitive filter where the
/// returned items start with the specified text. The filter uses the
/// <see cref="String.StartsWith(String,StringComparison)" />
/// method, specifying
/// <see cref="StringComparer.CurrentCulture" /> as the string
/// comparison criteria.
/// </summary>
StartsWithCaseSensitive = 2,
/// <summary>
/// Specifies an ordinal, case-insensitive filter where the returned
/// items start with the specified text. The filter uses the
/// <see cref="String.StartsWith(String,StringComparison)" />
/// method, specifying
/// <see cref="StringComparer.OrdinalIgnoreCase" /> as the
/// string comparison criteria.
/// </summary>
StartsWithOrdinal = 3,
/// <summary>
/// Specifies an ordinal, case-sensitive filter where the returned items
/// start with the specified text. The filter uses the
/// <see cref="String.StartsWith(String,StringComparison)" />
/// method, specifying <see cref="StringComparer.Ordinal" /> as
/// the string comparison criteria.
/// </summary>
StartsWithOrdinalCaseSensitive = 4,
/// <summary>
/// Specifies a culture-sensitive, case-insensitive filter where the
/// returned items contain the specified text.
/// </summary>
Contains = 5,
/// <summary>
/// Specifies a culture-sensitive, case-sensitive filter where the
/// returned items contain the specified text.
/// </summary>
ContainsCaseSensitive = 6,
/// <summary>
/// Specifies an ordinal, case-insensitive filter where the returned
/// items contain the specified text.
/// </summary>
ContainsOrdinal = 7,
/// <summary>
/// Specifies an ordinal, case-sensitive filter where the returned items
/// contain the specified text.
/// </summary>
ContainsOrdinalCaseSensitive = 8,
/// <summary>
/// Specifies a culture-sensitive, case-insensitive filter where the
/// returned items equal the specified text. The filter uses the
/// <see cref="String.Equals(String,StringComparison)" />
/// method, specifying
/// <see cref="StringComparer.CurrentCultureIgnoreCase" /> as
/// the search comparison criteria.
/// </summary>
Equals = 9,
/// <summary>
/// Specifies a culture-sensitive, case-sensitive filter where the
/// returned items equal the specified text. The filter uses the
/// <see cref="String.Equals(String,StringComparison)" />
/// method, specifying
/// <see cref="StringComparer.CurrentCulture" /> as the string
/// comparison criteria.
/// </summary>
EqualsCaseSensitive = 10,
/// <summary>
/// Specifies an ordinal, case-insensitive filter where the returned
/// items equal the specified text. The filter uses the
/// <see cref="String.Equals(String,StringComparison)" />
/// method, specifying
/// <see cref="StringComparer.OrdinalIgnoreCase" /> as the
/// string comparison criteria.
/// </summary>
EqualsOrdinal = 11,
/// <summary>
/// Specifies an ordinal, case-sensitive filter where the returned items
/// equal the specified text. The filter uses the
/// <see cref="String.Equals(String,StringComparison)" />
/// method, specifying <see cref="StringComparer.Ordinal" /> as
/// the string comparison criteria.
/// </summary>
EqualsOrdinalCaseSensitive = 12,
/// <summary>
/// Specifies that a custom filter is used. This mode is used when the
/// <see cref="AutoCompleteBox.TextFilter" /> or <see cref="AutoCompleteBox.ItemFilter" />
/// properties are set.
/// </summary>
Custom = 13,
}
}

39
src/Avalonia.Controls/AutoCompleteBox/PopulatedEventArgs.cs

@ -0,0 +1,39 @@
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see https://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
using System;
using System.Collections;
namespace Avalonia.Controls
{
/// <summary>
/// Provides data for the
/// <see cref="E:Avalonia.Controls.AutoCompleteBox.Populated" />
/// event.
/// </summary>
public class PopulatedEventArgs : EventArgs
{
/// <summary>
/// Gets the list of possible matches added to the drop-down portion of
/// the <see cref="T:Avalonia.Controls.AutoCompleteBox" />
/// control.
/// </summary>
/// <value>The list of possible matches added to the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" />.</value>
public IEnumerable Data { get; private set; }
/// <summary>
/// Initializes a new instance of the
/// <see cref="T:Avalonia.Controls.PopulatedEventArgs" />.
/// </summary>
/// <param name="data">The list of possible matches added to the
/// drop-down portion of the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> control.</param>
public PopulatedEventArgs(IEnumerable data)
{
Data = data;
}
}
}

39
src/Avalonia.Controls/AutoCompleteBox/PopulatingEventArgs.cs

@ -0,0 +1,39 @@
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see https://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
using System.ComponentModel;
namespace Avalonia.Controls
{
/// <summary>
/// Provides data for the
/// <see cref="E:Avalonia.Controls.AutoCompleteBox.Populating" />
/// event.
/// </summary>
public class PopulatingEventArgs : CancelEventArgs
{
/// <summary>
/// Gets the text that is used to determine which items to display in
/// the <see cref="T:Avalonia.Controls.AutoCompleteBox" />
/// control.
/// </summary>
/// <value>The text that is used to determine which items to display in
/// the <see cref="T:Avalonia.Controls.AutoCompleteBox" />.</value>
public string? Parameter { get; private set; }
/// <summary>
/// Initializes a new instance of the
/// <see cref="T:Avalonia.Controls.PopulatingEventArgs" />.
/// </summary>
/// <param name="parameter">The value of the
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.SearchText" />
/// property, which is used to filter items for the
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> control.</param>
public PopulatingEventArgs(string? parameter)
{
Parameter = parameter;
}
}
}

2
src/Avalonia.Controls/Calendar/Calendar.cs

@ -1,6 +1,6 @@
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// Please see https://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
using System;

2
src/Avalonia.Controls/Calendar/CalendarBlackoutDatesCollection.cs

@ -1,6 +1,6 @@
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// Please see https://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
using System;

2
src/Avalonia.Controls/Calendar/CalendarButton.cs

@ -1,6 +1,6 @@
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// Please see https://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
using Avalonia.Controls.Metadata;

2
src/Avalonia.Controls/Calendar/CalendarDateRange.cs

@ -1,6 +1,6 @@
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// Please see https://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
using System;

2
src/Avalonia.Controls/Calendar/CalendarDayButton.cs

@ -1,6 +1,6 @@
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// Please see https://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
using System;

2
src/Avalonia.Controls/Calendar/CalendarExtensions.cs

@ -1,6 +1,6 @@
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// Please see https://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
using Avalonia.Input;

2
src/Avalonia.Controls/Calendar/CalendarItem.cs

@ -1,6 +1,6 @@
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// Please see https://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
using System;

2
src/Avalonia.Controls/Calendar/DateTimeHelper.cs

@ -1,6 +1,6 @@
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// Please see https://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
using System;

2
src/Avalonia.Controls/Calendar/SelectedDatesCollection.cs

@ -1,6 +1,6 @@
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// Please see https://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
using Avalonia.Threading;

2
src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.cs

@ -1,6 +1,6 @@
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// Please see https://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
using System;

2
src/Avalonia.Controls/CalendarDatePicker/CalendarDatePickerDateValidationErrorEventArgs.cs

@ -1,6 +1,6 @@
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// Please see https://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
using System;

2
src/Avalonia.Controls/CalendarDatePicker/CalendarDatePickerFormat.cs

@ -1,6 +1,6 @@
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// Please see https://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
namespace Avalonia.Controls

30
src/Avalonia.Controls/DateTimePickers/DatePicker.cs

@ -40,18 +40,6 @@ namespace Avalonia.Controls
AvaloniaProperty.RegisterDirect<DatePicker, bool>(nameof(DayVisible),
x => x.DayVisible, (x, v) => x.DayVisible = v);
/// <summary>
/// Defines the <see cref="Header"/> Property
/// </summary>
public static readonly StyledProperty<object> HeaderProperty =
AvaloniaProperty.Register<DatePicker, object>(nameof(Header));
/// <summary>
/// Defines the <see cref="HeaderTemplate"/> Property
/// </summary>
public static readonly StyledProperty<IDataTemplate> HeaderTemplateProperty =
AvaloniaProperty.Register<DatePicker, IDataTemplate>(nameof(HeaderTemplate));
/// <summary>
/// Defines the <see cref="MaxYear"/> Property
/// </summary>
@ -152,24 +140,6 @@ namespace Avalonia.Controls
}
}
/// <summary>
/// Gets or sets the DatePicker header
/// </summary>
public object Header
{
get => GetValue(HeaderProperty);
set => SetValue(HeaderProperty, value);
}
/// <summary>
/// Gets or sets the header template
/// </summary>
public IDataTemplate HeaderTemplate
{
get => GetValue(HeaderTemplateProperty);
set => SetValue(HeaderTemplateProperty, value);
}
/// <summary>
/// Gets or sets the maximum year for the picker
/// </summary>

30
src/Avalonia.Controls/DateTimePickers/TimePicker.cs

@ -34,18 +34,6 @@ namespace Avalonia.Controls
AvaloniaProperty.RegisterDirect<TimePicker, int>(nameof(MinuteIncrement),
x => x.MinuteIncrement, (x, v) => x.MinuteIncrement = v);
/// <summary>
/// Defines the <see cref="Header"/> property
/// </summary>
public static readonly StyledProperty<object> HeaderProperty =
AvaloniaProperty.Register<TimePicker, object>(nameof(Header));
/// <summary>
/// Defines the <see cref="HeaderTemplate"/> property
/// </summary>
public static readonly StyledProperty<IDataTemplate> HeaderTemplateProperty =
AvaloniaProperty.Register<TimePicker, IDataTemplate>(nameof(HeaderTemplate));
/// <summary>
/// Defines the <see cref="ClockIdentifier"/> property
/// </summary>
@ -103,24 +91,6 @@ namespace Avalonia.Controls
}
}
/// <summary>
/// Gets or sets the header
/// </summary>
public object Header
{
get => GetValue(HeaderProperty);
set => SetValue(HeaderProperty, value);
}
/// <summary>
/// Gets or sets the header template
/// </summary>
public IDataTemplate HeaderTemplate
{
get => GetValue(HeaderTemplateProperty);
set => SetValue(HeaderTemplateProperty, value);
}
/// <summary>
/// Gets or sets the clock identifier, either 12HourClock or 24HourClock
/// </summary>

2
src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs

@ -35,7 +35,7 @@ DEALINGS IN THE SOFTWARE.
The above is the version of the MIT "Expat" License used by X.org:
http://cgit.freedesktop.org/xorg/xserver/tree/COPYING
https://cgit.freedesktop.org/xorg/xserver/tree/COPYING
Adjustments for Avalonia needs:

2
src/Avalonia.Controls/Primitives/RangeBase.cs

@ -175,7 +175,7 @@ namespace Avalonia.Controls.Primitives
/// <param name="value">The value.</param>
private static bool ValidateDouble(double value)
{
return !double.IsInfinity(value) || !double.IsNaN(value);
return !double.IsInfinity(value) && !double.IsNaN(value);
}
/// <summary>

91
src/Avalonia.Controls/TextBox.cs

@ -18,6 +18,7 @@ using Avalonia.Media.TextFormatting;
using Avalonia.Media.TextFormatting.Unicode;
using Avalonia.Automation.Peers;
using System.Diagnostics;
using Avalonia.Threading;
namespace Avalonia.Controls
{
@ -159,18 +160,41 @@ namespace Avalonia.Controls
(o, v) => o.UndoLimit = v,
unsetValue: -1);
/// <summary>
/// Defines the <see cref="CopyingToClipboard"/> event.
/// </summary>
public static readonly RoutedEvent<RoutedEventArgs> CopyingToClipboardEvent =
RoutedEvent.Register<TextBox, RoutedEventArgs>(
nameof(CopyingToClipboard), RoutingStrategies.Bubble);
/// <summary>
/// Defines the <see cref="CuttingToClipboard"/> event.
/// </summary>
public static readonly RoutedEvent<RoutedEventArgs> CuttingToClipboardEvent =
RoutedEvent.Register<TextBox, RoutedEventArgs>(
nameof(CuttingToClipboard), RoutingStrategies.Bubble);
/// <summary>
/// Defines the <see cref="PastingFromClipboard"/> event.
/// </summary>
public static readonly RoutedEvent<RoutedEventArgs> PastingFromClipboardEvent =
RoutedEvent.Register<TextBox, RoutedEventArgs>(
nameof(PastingFromClipboard), RoutingStrategies.Bubble);
/// <summary>
/// Defines the <see cref="TextChanged"/> event.
/// </summary>
public static readonly RoutedEvent<TextChangedEventArgs> TextChangedEvent =
RoutedEvent.Register<TextBox, TextChangedEventArgs>(
nameof(TextChanged), RoutingStrategies.Bubble);
/// <summary>
/// Defines the <see cref="TextChanging"/> event.
/// </summary>
public static readonly RoutedEvent<TextChangingEventArgs> TextChangingEvent =
RoutedEvent.Register<TextBox, TextChangingEventArgs>(
nameof(TextChanging), RoutingStrategies.Bubble);
readonly struct UndoRedoState : IEquatable<UndoRedoState>
{
public string? Text { get; }
@ -359,8 +383,8 @@ namespace Avalonia.Controls
/// </summary>
public double LineHeight
{
get { return GetValue(LineHeightProperty); }
set { SetValue(LineHeightProperty, value); }
get => GetValue(LineHeightProperty);
set => SetValue(LineHeightProperty, value);
}
[Content]
@ -376,11 +400,19 @@ namespace Avalonia.Controls
CaretIndex = CoerceCaretIndex(caretIndex, value);
SelectionStart = CoerceCaretIndex(selectionStart, value);
SelectionEnd = CoerceCaretIndex(selectionEnd, value);
if (SetAndRaise(TextProperty, ref _text, value) && IsUndoEnabled && !_isUndoingRedoing)
var textChanged = SetAndRaise(TextProperty, ref _text, value);
if (textChanged && IsUndoEnabled && !_isUndoingRedoing)
{
_undoRedoHelper.Clear();
SnapshotUndoRedo(); // so we always have an initial state
}
if (textChanged)
{
RaiseTextChangeEvents();
}
}
}
@ -564,6 +596,27 @@ namespace Avalonia.Controls
remove => RemoveHandler(PastingFromClipboardEvent, value);
}
/// <summary>
/// Occurs asynchronously after text changes and the new text is rendered.
/// </summary>
public event EventHandler<TextChangedEventArgs>? TextChanged
{
add => AddHandler(TextChangedEvent, value);
remove => RemoveHandler(TextChangedEvent, value);
}
/// <summary>
/// Occurs synchronously when text starts to change but before it is rendered.
/// </summary>
/// <remarks>
/// This event occurs just after the <see cref="Text"/> property value has been updated.
/// </remarks>
public event EventHandler<TextChangingEventArgs>? TextChanging
{
add => AddHandler(TextChangingEvent, value);
remove => RemoveHandler(TextChangingEvent, value);
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
_presenter = e.NameScope.Get<TextPresenter>("PART_TextPresenter");
@ -1252,7 +1305,7 @@ namespace Avalonia.Controls
if (text != null && _wordSelectionStart >= 0)
{
var distance = caretIndex - _wordSelectionStart;
var distance = caretIndex - _wordSelectionStart;
if (distance <= 0)
{
@ -1539,11 +1592,39 @@ namespace Avalonia.Controls
return text.Substring(start, end - start);
}
/// <summary>
/// Raises both the <see cref="TextChanging"/> and <see cref="TextChanged"/> events.
/// </summary>
/// <remarks>
/// This must be called after the <see cref="Text"/> property is set.
/// </remarks>
private void RaiseTextChangeEvents()
{
// Note the following sequence of these events (following WinUI)
// 1. TextChanging occurs synchronously when text starts to change but before it is rendered.
// This occurs after the Text property is set.
// 2. TextChanged occurs asynchronously after text changes and the new text is rendered.
var textChangingEventArgs = new TextChangingEventArgs(TextChangingEvent);
RaiseEvent(textChangingEventArgs);
Dispatcher.UIThread.Post(() =>
{
var textChangedEventArgs = new TextChangedEventArgs(TextChangedEvent);
RaiseEvent(textChangedEventArgs);
}, DispatcherPriority.Normal);
}
private void SetTextInternal(string value, bool raiseTextChanged = true)
{
if (raiseTextChanged)
{
SetAndRaise(TextProperty, ref _text, value);
bool textChanged = SetAndRaise(TextProperty, ref _text, value);
if (textChanged)
{
RaiseTextChangeEvents();
}
}
else
{

2
src/Avalonia.Controls/TextBoxTextInputMethodClient.cs

@ -64,7 +64,7 @@ namespace Avalonia.Controls
return new TextInputMethodSurroundingText
{
Text = lineText ?? "",
Text = lineText ?? "",
AnchorOffset = anchorOffset,
CursorOffset = cursorOffset
};

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

Loading…
Cancel
Save