Browse Source

Merge branch 'master' into refactor/new-value-store

Steven Kirk 3 years ago
parent
commit
7211f031ac
100 changed files with 1017 additions and 496 deletions
  1. 1 1
      Avalonia.sln
  2. 3 3
      azure-pipelines.yml
  3. 3 3
      samples/BindingDemo/App.xaml
  4. 1 1
      samples/BindingDemo/BindingDemo.csproj
  5. 0 1
      samples/ControlCatalog.Android/ControlCatalog.Android.csproj
  6. 10 10
      samples/ControlCatalog/App.xaml.cs
  7. 1 1
      samples/ControlCatalog/ControlCatalog.csproj
  8. 2 2
      samples/ControlCatalog/MainView.xaml
  9. 10 10
      samples/ControlCatalog/MainView.xaml.cs
  10. 3 7
      samples/ControlCatalog/Models/CatalogTheme.cs
  11. 5 4
      samples/ControlCatalog/Pages/DataGridPage.xaml
  12. 19 7
      samples/ControlCatalog/Pages/DialogsPage.xaml.cs
  13. 7 37
      samples/ControlCatalog/Pages/OpenGlPage.xaml.cs
  14. 1 1
      samples/ControlCatalog/Pages/TextBlockPage.xaml
  15. 1 2
      samples/PlatformSanityChecks/App.xaml
  16. 1 1
      samples/PlatformSanityChecks/PlatformSanityChecks.csproj
  17. 2 3
      samples/Previewer/App.xaml
  18. 1 1
      samples/Previewer/Previewer.csproj
  19. 7 1
      samples/RenderDemo/App.xaml
  20. 7 9
      samples/VirtualizationDemo/App.xaml
  21. 1 1
      samples/VirtualizationDemo/VirtualizationDemo.csproj
  22. 2 3
      samples/interop/Direct3DInteropSample/App.paml
  23. 1 1
      samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj
  24. 29 4
      src/Android/Avalonia.Android/Platform/Storage/AndroidStorageItem.cs
  25. 97 14
      src/Avalonia.Base/Media/GlyphRun.cs
  26. 1 1
      src/Avalonia.Base/Media/TextDecoration.cs
  27. 24 0
      src/Avalonia.Base/Media/TextFormatting/FormattedTextSource.cs
  28. 12 5
      src/Avalonia.Base/Media/TextFormatting/InterWordJustification.cs
  29. 13 13
      src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs
  30. 31 27
      src/Avalonia.Base/Media/TextFormatting/TextLayout.cs
  31. 1 1
      src/Avalonia.Base/Media/TextFormatting/TextLine.cs
  32. 502 235
      src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs
  33. 1 1
      src/Avalonia.Base/Media/TextFormatting/Unicode/Codepoint.cs
  34. 2 2
      src/Avalonia.Base/Platform/IGeometryImpl.cs
  35. 4 4
      src/Avalonia.Base/Platform/Storage/FileIO/BclStorageFile.cs
  36. 14 2
      src/Avalonia.Base/Platform/Storage/FileIO/BclStorageFolder.cs
  37. 1 1
      src/Avalonia.Base/Platform/Storage/IStorageBookmarkItem.cs
  38. 2 2
      src/Avalonia.Base/Platform/Storage/IStorageFile.cs
  39. 10 1
      src/Avalonia.Base/Platform/Storage/IStorageFolder.cs
  40. 1 1
      src/Avalonia.Base/Platform/Storage/IStorageItem.cs
  41. 3 2
      src/Avalonia.Base/Rendering/Composition/Animations/AnimationInstanceBase.cs
  42. 5 6
      src/Avalonia.Base/Rendering/Composition/Animations/CompositionAnimation.cs
  43. 3 1
      src/Avalonia.Base/Rendering/Composition/Animations/CompositionAnimationGroup.cs
  44. 4 2
      src/Avalonia.Base/Rendering/Composition/Animations/ExpressionAnimation.cs
  45. 3 1
      src/Avalonia.Base/Rendering/Composition/Animations/ExpressionAnimationInstance.cs
  46. 3 1
      src/Avalonia.Base/Rendering/Composition/Animations/IAnimationInstance.cs
  47. 3 1
      src/Avalonia.Base/Rendering/Composition/Animations/ICompositionAnimationBase.cs
  48. 3 1
      src/Avalonia.Base/Rendering/Composition/Animations/ImplicitAnimationCollection.cs
  49. 3 1
      src/Avalonia.Base/Rendering/Composition/Animations/Interpolators.cs
  50. 5 3
      src/Avalonia.Base/Rendering/Composition/Animations/KeyFrameAnimation.cs
  51. 3 1
      src/Avalonia.Base/Rendering/Composition/Animations/KeyFrameAnimationInstance.cs
  52. 3 1
      src/Avalonia.Base/Rendering/Composition/Animations/KeyFrames.cs
  53. 3 1
      src/Avalonia.Base/Rendering/Composition/Animations/PropertySetSnapshot.cs
  54. 2 0
      src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs
  55. 3 1
      src/Avalonia.Base/Rendering/Composition/CompositionDrawListVisual.cs
  56. 3 1
      src/Avalonia.Base/Rendering/Composition/CompositionObject.cs
  57. 3 1
      src/Avalonia.Base/Rendering/Composition/CompositionPropertySet.cs
  58. 3 1
      src/Avalonia.Base/Rendering/Composition/CompositionTarget.cs
  59. 3 1
      src/Avalonia.Base/Rendering/Composition/Compositor.cs
  60. 3 1
      src/Avalonia.Base/Rendering/Composition/ContainerVisual.cs
  61. 3 1
      src/Avalonia.Base/Rendering/Composition/Drawing/CompositionDrawList.cs
  62. 4 1
      src/Avalonia.Base/Rendering/Composition/Drawing/CompositionDrawingContext.cs
  63. 3 1
      src/Avalonia.Base/Rendering/Composition/ElementCompositionPreview.cs
  64. 3 1
      src/Avalonia.Base/Rendering/Composition/Enums.cs
  65. 3 1
      src/Avalonia.Base/Rendering/Composition/Expressions/BuiltInExpressionFfi.cs
  66. 3 1
      src/Avalonia.Base/Rendering/Composition/Expressions/DelegateExpressionFfi.cs
  67. 3 1
      src/Avalonia.Base/Rendering/Composition/Expressions/Expression.cs
  68. 3 1
      src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionEvaluationContext.cs
  69. 3 1
      src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionParseException.cs
  70. 3 1
      src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionParser.cs
  71. 3 1
      src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionTrackedValues.cs
  72. 3 1
      src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionVariant.cs
  73. 3 1
      src/Avalonia.Base/Rendering/Composition/Expressions/TokenParser.cs
  74. 3 1
      src/Avalonia.Base/Rendering/Composition/ICompositionTargetDebugEvents.cs
  75. 7 0
      src/Avalonia.Base/Rendering/Composition/License.md
  76. 3 1
      src/Avalonia.Base/Rendering/Composition/MatrixUtils.cs
  77. 3 1
      src/Avalonia.Base/Rendering/Composition/Server/CompositionProperty.cs
  78. 3 1
      src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs
  79. 3 1
      src/Avalonia.Base/Rendering/Composition/Server/FpsCounter.cs
  80. 3 1
      src/Avalonia.Base/Rendering/Composition/Server/ReadbackIndices.cs
  81. 3 1
      src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionContainerVisual.cs
  82. 3 1
      src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionDrawListVisual.cs
  83. 3 1
      src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionSurface.cs
  84. 3 1
      src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs
  85. 3 1
      src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.DirtyProperties.cs
  86. 3 1
      src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs
  87. 3 1
      src/Avalonia.Base/Rendering/Composition/Server/ServerCompositor.cs
  88. 3 1
      src/Avalonia.Base/Rendering/Composition/Server/ServerList.cs
  89. 3 1
      src/Avalonia.Base/Rendering/Composition/Server/ServerObject.cs
  90. 3 1
      src/Avalonia.Base/Rendering/Composition/Transport/Batch.cs
  91. 2 0
      src/Avalonia.Base/Rendering/Composition/Transport/BatchStream.cs
  92. 3 1
      src/Avalonia.Base/Rendering/Composition/Transport/BatchStreamArrayPool.cs
  93. 3 1
      src/Avalonia.Base/Rendering/Composition/Transport/BatchStreamDebugMarker.cs
  94. 3 1
      src/Avalonia.Base/Rendering/Composition/Transport/ServerListProxyHelper.cs
  95. 3 1
      src/Avalonia.Base/Rendering/Composition/Visual.cs
  96. 3 1
      src/Avalonia.Base/Rendering/Composition/VisualCollection.cs
  97. 3 0
      src/Avalonia.Base/Rendering/Composition/readme.md
  98. 1 1
      src/Avalonia.Base/Rendering/SceneGraph/ExperimentalAcrylicNode.cs
  99. 1 3
      src/Avalonia.Base/Rendering/SceneGraph/GeometryNode.cs
  100. 2 6
      src/Avalonia.Base/Rendering/SceneGraph/GlyphRunNode.cs

+ 1 - 1
Avalonia.sln

@@ -13,7 +13,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Direct2D1", "src\W
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls", "src\Avalonia.Controls\Avalonia.Controls.csproj", "{D2221C82-4A25-4583-9B43-D791E3F6820C}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Themes.Default", "src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj", "{3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Themes.Simple", "src\Avalonia.Themes.Simple\Avalonia.Themes.Simple.csproj", "{3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}"
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Diagnostics", "src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj", "{7062AE20-5DCC-4442-9645-8195BDECE63E}"
 EndProject

+ 3 - 3
azure-pipelines.yml

@@ -59,7 +59,7 @@ jobs:
   variables:
     SolutionDir: '$(Build.SourcesDirectory)'
   pool:
-    vmImage: 'macOS-10.15'
+    vmImage: 'macos-12'
   steps:
   - task: UseDotNet@2
     displayName: 'Use .NET Core SDK 3.1.418'
@@ -91,10 +91,10 @@ jobs:
     inputs:
       actions: 'build'
       scheme: ''
-      sdk: 'macosx11.1'
+      sdk: 'macosx12.3'
       configuration: 'Release'
       xcWorkspacePath: '**/*.xcodeproj/project.xcworkspace'
-      xcodeVersion: '12' # Options: 8, 9, default, specifyPath
+      xcodeVersion: '13' # Options: 8, 9, default, specifyPath
       args: '-derivedDataPath ./'
 
   - task: CmdLine@2

+ 3 - 3
samples/BindingDemo/App.xaml

@@ -3,7 +3,7 @@
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     x:Class="BindingDemo.App">
     <Application.Styles>
-        <StyleInclude Source="avares://Avalonia.Themes.Default/DefaultTheme.xaml"/>
-        <StyleInclude Source="avares://Avalonia.Themes.Default/Accents/BaseLight.xaml"/>
+        <FluentTheme />
+        <StyleInclude Source="avares://Avalonia.Themes.Simple/Accents/BaseLight.xaml"/>
     </Application.Styles>
-</Application>
+</Application>

+ 1 - 1
samples/BindingDemo/BindingDemo.csproj

@@ -5,7 +5,7 @@
   </PropertyGroup>
   <ItemGroup>
     <ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
-    <ProjectReference Include="..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj" />
+    <ProjectReference Include="..\..\src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj" />
     <ProjectReference Include="..\..\src\Linux\Avalonia.LinuxFramebuffer\Avalonia.LinuxFramebuffer.csproj" />
     <ProjectReference Include="..\MiniMvvm\MiniMvvm.csproj" />
   </ItemGroup>

+ 0 - 1
samples/ControlCatalog.Android/ControlCatalog.Android.csproj

@@ -9,7 +9,6 @@
     <ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
     <AndroidPackageFormat>apk</AndroidPackageFormat>
     <MSBuildEnableWorkloadResolver>true</MSBuildEnableWorkloadResolver>
-    <RuntimeIdentifiers>android-arm64;android-x64</RuntimeIdentifiers>
   </PropertyGroup>
   <ItemGroup>
     <AndroidResource Include="..\..\build\Assets\Icon.png">

+ 10 - 10
samples/ControlCatalog/App.xaml.cs

@@ -5,7 +5,7 @@ using Avalonia.Controls.ApplicationLifetimes;
 using Avalonia.Markup.Xaml;
 using Avalonia.Markup.Xaml.Styling;
 using Avalonia.Styling;
-using Avalonia.Themes.Default;
+using Avalonia.Themes.Simple;
 using Avalonia.Themes.Fluent;
 using ControlCatalog.ViewModels;
 
@@ -23,9 +23,9 @@ namespace ControlCatalog
             Source = new Uri("avares://Avalonia.Controls.ColorPicker/Themes/Fluent/Fluent.xaml")
         };
 
-        public static readonly StyleInclude ColorPickerDefault = new StyleInclude(new Uri("avares://ControlCatalog/Styles"))
+        public static readonly StyleInclude ColorPickerSimple = new StyleInclude(new Uri("avares://ControlCatalog/Styles"))
         {
-            Source = new Uri("avares://Avalonia.Controls.ColorPicker/Themes/Default/Default.xaml")
+            Source = new Uri("avares://Avalonia.Controls.ColorPicker/Themes/Simple/Simple.xaml")
         };
 
         public static readonly StyleInclude DataGridFluent = new StyleInclude(new Uri("avares://ControlCatalog/Styles"))
@@ -33,16 +33,16 @@ namespace ControlCatalog
             Source = new Uri("avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml")
         };
 
-        public static readonly StyleInclude DataGridDefault = new StyleInclude(new Uri("avares://ControlCatalog/Styles"))
+        public static readonly StyleInclude DataGridSimple = new StyleInclude(new Uri("avares://ControlCatalog/Styles"))
         {
-            Source = new Uri("avares://Avalonia.Controls.DataGrid/Themes/Default.xaml")
+            Source = new Uri("avares://Avalonia.Controls.DataGrid/Themes/Simple.xaml")
         };
 
         public static FluentTheme Fluent = new FluentTheme(new Uri("avares://ControlCatalog/Styles"));
 
-        public static SimpleTheme Default = new SimpleTheme(new Uri("avares://ControlCatalog/Styles"));
+        public static SimpleTheme Simple = new SimpleTheme(new Uri("avares://ControlCatalog/Styles"));
 
-        public static Styles DefaultLight = new Styles
+        public static Styles SimpleLight = new Styles
         {
             new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
             {
@@ -56,10 +56,10 @@ namespace ControlCatalog
             {
                 Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/BaseLight.xaml")
             },
-            Default
+            Simple
         };
 
-        public static Styles DefaultDark = new Styles
+        public static Styles SimpleDark = new Styles
         {
             new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
             {
@@ -73,7 +73,7 @@ namespace ControlCatalog
             {
                 Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/BaseDark.xaml")
             },
-            Default
+            Simple
         };
 
         public override void Initialize()

+ 1 - 1
samples/ControlCatalog/ControlCatalog.csproj

@@ -29,7 +29,7 @@
     <ProjectReference Include="..\..\packages\Avalonia\Avalonia.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.Controls.ColorPicker\Avalonia.Controls.ColorPicker.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.Controls.DataGrid\Avalonia.Controls.DataGrid.csproj" />
-    <ProjectReference Include="..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj" />
+    <ProjectReference Include="..\..\src\Avalonia.Themes.Simple\Avalonia.Themes.Simple.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj" />
     <ProjectReference Include="..\MiniMvvm\MiniMvvm.csproj" />
     <ProjectReference Include="..\SampleControls\ControlSamples.csproj" />

+ 2 - 2
samples/ControlCatalog/MainView.xaml

@@ -187,8 +187,8 @@
               <ComboBox.Items>
                 <models:CatalogTheme>FluentLight</models:CatalogTheme>
                 <models:CatalogTheme>FluentDark</models:CatalogTheme>
-                <models:CatalogTheme>DefaultLight</models:CatalogTheme>
-                <models:CatalogTheme>DefaultDark</models:CatalogTheme>
+                <models:CatalogTheme>SimpleLight</models:CatalogTheme>
+                <models:CatalogTheme>SimpleDark</models:CatalogTheme>
               </ComboBox.Items>
             </ComboBox>
             <ComboBox x:Name="TransparencyLevels"

+ 10 - 10
samples/ControlCatalog/MainView.xaml.cs

@@ -58,19 +58,19 @@ namespace ControlCatalog
                         Application.Current.Styles[1] = App.ColorPickerFluent;
                         Application.Current.Styles[2] = App.DataGridFluent;
                     }
-                    else if (theme == CatalogTheme.DefaultLight)
+                    else if (theme == CatalogTheme.SimpleLight)
                     {
-                        App.Default.Mode = Avalonia.Themes.Default.SimpleThemeMode.Light;
-                        Application.Current.Styles[0] = App.DefaultLight;
-                        Application.Current.Styles[1] = App.ColorPickerDefault;
-                        Application.Current.Styles[2] = App.DataGridDefault;
+                        App.Simple.Mode = Avalonia.Themes.Simple.SimpleThemeMode.Light;
+                        Application.Current.Styles[0] = App.SimpleLight;
+                        Application.Current.Styles[1] = App.ColorPickerSimple;
+                        Application.Current.Styles[2] = App.DataGridSimple;
                     }
-                    else if (theme == CatalogTheme.DefaultDark)
+                    else if (theme == CatalogTheme.SimpleDark)
                     {
-                        App.Default.Mode = Avalonia.Themes.Default.SimpleThemeMode.Dark;
-                        Application.Current.Styles[0] = App.DefaultDark;
-                        Application.Current.Styles[1] = App.ColorPickerDefault;
-                        Application.Current.Styles[2] = App.DataGridDefault;
+                        App.Simple.Mode = Avalonia.Themes.Simple.SimpleThemeMode.Dark;
+                        Application.Current.Styles[0] = App.SimpleDark;
+                        Application.Current.Styles[1] = App.ColorPickerSimple;
+                        Application.Current.Styles[2] = App.DataGridSimple;
                     }
                 }
             };

+ 3 - 7
samples/ControlCatalog/Models/CatalogTheme.cs

@@ -1,14 +1,10 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace ControlCatalog.Models
+namespace ControlCatalog.Models
 {
     public enum CatalogTheme
     {
         FluentLight,
         FluentDark,
-        DefaultLight,
-        DefaultDark
+        SimpleLight,
+        SimpleDark
     }
 }

+ 5 - 4
samples/ControlCatalog/Pages/DataGridPage.xaml

@@ -10,11 +10,11 @@
         <TextBlock Text="{Binding}"/>
       </StackPanel>
     </DataTemplate>
+    <ControlTheme x:Key="GdpCell" TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
+      <Setter Property="Background" Value="{Binding Path=GDP, Mode=OneWay, Converter={StaticResource GDPConverter}}" />
+    </ControlTheme>
   </UserControl.Resources>
   <UserControl.Styles>
-    <Style Selector="DataGridCell.gdp">
-      <Setter Property="Background" Value="{Binding Path=GDP, Mode=OneWay, Converter={StaticResource GDPConverter}}" />
-    </Style>
     <Style Selector="DataGridColumnHeader:nth-last-child(1)">
       <Setter Property="FontWeight" Value="Bold" />
     </Style>
@@ -54,7 +54,8 @@
               <DataGridTextColumn Header="Region" Binding="{CompiledBinding Region}" Width="4*" x:DataType="local:Country" />
               <DataGridTextColumn Header="Population" Binding="{Binding Population}" Width="3*" />
               <DataGridTextColumn Header="Area" Binding="{Binding Area}" Width="3*" />
-              <DataGridTextColumn Header="GDP" Binding="{Binding GDP}" Width="3*" CellStyleClasses="gdp"
+              <DataGridTextColumn Header="GDP" Binding="{Binding GDP}" Width="3*"
+                                  CellTheme="{StaticResource GdpCell}"
                                   MinWidth="200"
                                   IsVisible="{Binding #ShowGDP.IsChecked}"/>
             </DataGrid.Columns>

+ 19 - 7
samples/ControlCatalog/Pages/DialogsPage.xaml.cs

@@ -195,10 +195,10 @@ namespace ControlCatalog.Pages
                 {
                     // Sync disposal of StreamWriter is not supported on WASM
 #if NET6_0_OR_GREATER
-                    await using var stream = await file.OpenWrite();
+                    await using var stream = await file.OpenWriteAsync();
                     await using var reader = new System.IO.StreamWriter(stream);
 #else
-                    using var stream = await file.OpenWrite();
+                    using var stream = await file.OpenWriteAsync();
                     using var reader = new System.IO.StreamWriter(stream);
 #endif
                     await reader.WriteLineAsync(openedFileContent.Text);
@@ -243,8 +243,8 @@ namespace ControlCatalog.Pages
             async Task SetPickerResult(IReadOnlyCollection<IStorageItem>? items)
             {
                 items ??= Array.Empty<IStorageItem>();
-                var mappedResults = items.Select(FullPathOrName).ToList();
-                bookmarkContainer.Text = items.FirstOrDefault(f => f.CanBookmark) is { } f ? await f.SaveBookmark() : "Can't bookmark";
+                bookmarkContainer.Text = items.FirstOrDefault(f => f.CanBookmark) is { } f ? await f.SaveBookmarkAsync() : "Can't bookmark";
+                var mappedResults = new List<string>();
 
                 if (items.FirstOrDefault() is IStorageItem item)
                 {
@@ -267,9 +267,9 @@ Content:
                         if (file.CanOpenRead)
                         {
 #if NET6_0_OR_GREATER
-                            await using var stream = await file.OpenRead();
+                            await using var stream = await file.OpenReadAsync();
 #else
-                            using var stream = await file.OpenRead();
+                            using var stream = await file.OpenReadAsync();
 #endif
                             using var reader = new System.IO.StreamReader(stream);
 
@@ -293,7 +293,19 @@ Content:
                     lastSelectedDirectory = await item.GetParentAsync();
                     if (lastSelectedDirectory is not null)
                     {
-                        mappedResults.Insert(0,  "Parent: " + FullPathOrName(lastSelectedDirectory));
+                        mappedResults.Add(FullPathOrName(lastSelectedDirectory));
+                    }
+
+                    foreach (var selectedItem in items)
+                    {
+                        mappedResults.Add("+> " + FullPathOrName(selectedItem));
+                        if (selectedItem is IStorageFolder folder)
+                        {
+                            foreach (var innerItems in await folder.GetItemsAsync())
+                            {
+                                mappedResults.Add("++> " + FullPathOrName(innerItems));
+                            }
+                        }
                     }
                 }
 

+ 7 - 37
samples/ControlCatalog/Pages/OpenGlPage.xaml.cs

@@ -90,7 +90,6 @@ namespace ControlCatalog.Pages
         private int _vertexBufferObject;
         private int _indexBufferObject;
         private int _vertexArrayObject;
-        private GlExtrasInterface _glExt;
 
         private string GetShader(bool fragment, string shader)
         {
@@ -258,7 +257,6 @@ namespace ControlCatalog.Pages
         protected unsafe override void OnOpenGlInit(GlInterface GL, int fb)
         {
             CheckError(GL);
-            _glExt = new GlExtrasInterface(GL);
 
             Info = $"Renderer: {GL.GetString(GL_RENDERER)} Version: {GL.GetString(GL_VERSION)}";
             
@@ -298,8 +296,8 @@ namespace ControlCatalog.Pages
                 GL.BufferData(GL_ELEMENT_ARRAY_BUFFER, new IntPtr(_indices.Length * sizeof(ushort)), new IntPtr(pdata),
                     GL_STATIC_DRAW);
             CheckError(GL);
-            _vertexArrayObject = _glExt.GenVertexArray();
-            _glExt.BindVertexArray(_vertexArrayObject);
+            _vertexArrayObject = GL.GenVertexArray();
+            GL.BindVertexArray(_vertexArrayObject);
             CheckError(GL);
             GL.VertexAttribPointer(positionLocation, 3, GL_FLOAT,
                 0, vertexSize, IntPtr.Zero);
@@ -316,12 +314,13 @@ namespace ControlCatalog.Pages
             // Unbind everything
             GL.BindBuffer(GL_ARRAY_BUFFER, 0);
             GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
-            _glExt.BindVertexArray(0);
+            GL.BindVertexArray(0);
             GL.UseProgram(0);
 
             // Delete all resources.
-            GL.DeleteBuffers(2, new[] { _vertexBufferObject, _indexBufferObject });
-            _glExt.DeleteVertexArrays(1, new[] { _vertexArrayObject });
+            GL.DeleteBuffer(_vertexBufferObject);
+            GL.DeleteBuffer(_indexBufferObject);
+            GL.DeleteVertexArray(_vertexArrayObject);
             GL.DeleteProgram(_shaderProgram);
             GL.DeleteShader(_fragmentShader);
             GL.DeleteShader(_vertexShader);
@@ -338,7 +337,7 @@ namespace ControlCatalog.Pages
 
             GL.BindBuffer(GL_ARRAY_BUFFER, _vertexBufferObject);
             GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBufferObject);
-            _glExt.BindVertexArray(_vertexArrayObject);
+            GL.BindVertexArray(_vertexArrayObject);
             GL.UseProgram(_shaderProgram);
             CheckError(GL);
             var projection =
@@ -369,34 +368,5 @@ namespace ControlCatalog.Pages
             if (_disco > 0.01)
                 Dispatcher.UIThread.Post(InvalidateVisual, DispatcherPriority.Background);
         }
-
-        class GlExtrasInterface : GlInterfaceBase<GlInterface.GlContextInfo>
-        {
-            public GlExtrasInterface(GlInterface gl) : base(gl.GetProcAddress, gl.ContextInfo)
-            {
-            }
-            
-            public delegate void GlDeleteVertexArrays(int count, int[] buffers);
-            [GlMinVersionEntryPoint("glDeleteVertexArrays", 3,0)]
-            [GlExtensionEntryPoint("glDeleteVertexArraysOES", "GL_OES_vertex_array_object")]
-            public GlDeleteVertexArrays DeleteVertexArrays { get; }
-            
-            public delegate void GlBindVertexArray(int array);
-            [GlMinVersionEntryPoint("glBindVertexArray", 3,0)]
-            [GlExtensionEntryPoint("glBindVertexArrayOES", "GL_OES_vertex_array_object")]
-            public GlBindVertexArray BindVertexArray { get; }
-            public delegate void GlGenVertexArrays(int n, int[] rv);
-        
-            [GlMinVersionEntryPoint("glGenVertexArrays",3,0)]
-            [GlExtensionEntryPoint("glGenVertexArraysOES", "GL_OES_vertex_array_object")]
-            public GlGenVertexArrays GenVertexArrays { get; }
-            
-            public int GenVertexArray()
-            {
-                var rv = new int[1];
-                GenVertexArrays(1, rv);
-                return rv[0];
-            }
-        }
     }
 }

+ 1 - 1
samples/ControlCatalog/Pages/TextBlockPage.xaml

@@ -118,7 +118,7 @@
         </StackPanel>
       </Border>
       <Border>
-        <RichTextBlock Margin="10" TextWrapping="Wrap">
+        <RichTextBlock SelectionBrush="LightBlue" IsTextSelectionEnabled="True" Margin="10" TextWrapping="Wrap">
           This <Span FontWeight="Bold">is</Span> a
           <Span Background="Silver" Foreground="Maroon">TextBlock</Span>
           with <Span TextDecorations="Underline">several</Span>

+ 1 - 2
samples/PlatformSanityChecks/App.xaml

@@ -1,6 +1,5 @@
 <Application xmlns="https://github.com/avaloniaui">
     <Application.Styles>
-        <StyleInclude Source="resm:Avalonia.Themes.Default.DefaultTheme.xaml?assembly=Avalonia.Themes.Default"/>
-        <StyleInclude Source="resm:Avalonia.Themes.Default.Accents.BaseLight.xaml?assembly=Avalonia.Themes.Default"/>
+        <SimpleTheme Mode="Light" />
     </Application.Styles>
 </Application>

+ 1 - 1
samples/PlatformSanityChecks/PlatformSanityChecks.csproj

@@ -7,7 +7,7 @@
 
   <ItemGroup>
     <ProjectReference Include="..\..\src\Avalonia.Desktop\Avalonia.Desktop.csproj" />
-    <ProjectReference Include="..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj" />
+    <ProjectReference Include="..\..\src\Avalonia.Themes.Simple\Avalonia.Themes.Simple.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.X11\Avalonia.X11.csproj" />
   </ItemGroup>
 

+ 2 - 3
samples/Previewer/App.xaml

@@ -1,6 +1,5 @@
 <Application xmlns="https://github.com/avaloniaui">
     <Application.Styles>
-        <StyleInclude Source="resm:Avalonia.Themes.Default.DefaultTheme.xaml?assembly=Avalonia.Themes.Default"/>
-        <StyleInclude Source="resm:Avalonia.Themes.Default.Accents.BaseLight.xaml?assembly=Avalonia.Themes.Default"/>
+        <SimpleTheme Mode="Light" />
     </Application.Styles>
-</Application>
+</Application>

+ 1 - 1
samples/Previewer/Previewer.csproj

@@ -10,7 +10,7 @@
     <EmbeddedResource Include="**\*.xaml" />
   </ItemGroup>
   <ItemGroup>
-    <ProjectReference Include="..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj" />
+    <ProjectReference Include="..\..\src\Avalonia.Themes.Simple\Avalonia.Themes.Simple.csproj" />
   </ItemGroup>
   
   <Import Project="..\..\build\SampleApp.props" />

+ 7 - 1
samples/RenderDemo/App.xaml

@@ -3,6 +3,12 @@
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
   <Application.Styles>
     <FluentTheme />
-    <StyleInclude Source="avares://ControlSamples/HamburgerMenu/HamburgerMenu.xaml" />
   </Application.Styles>
+  <Application.Resources>
+    <ResourceDictionary>
+      <ResourceDictionary.MergedDictionaries>
+        <ResourceInclude Source="avares://ControlSamples/HamburgerMenu/HamburgerMenu.xaml" />
+      </ResourceDictionary.MergedDictionaries>
+    </ResourceDictionary>
+  </Application.Resources>
 </Application>

+ 7 - 9
samples/VirtualizationDemo/App.xaml

@@ -1,9 +1,7 @@
-<Application
-    xmlns="https://github.com/avaloniaui" 
-    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-    x:Class="VirtualizationDemo.App">
-    <Application.Styles>
-        <StyleInclude Source="avares://Avalonia.Themes.Default/DefaultTheme.xaml"/>
-        <StyleInclude Source="avares://Avalonia.Themes.Default/Accents/BaseLight.xaml"/>
-    </Application.Styles>
-</Application>
+<Application xmlns="https://github.com/avaloniaui"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             x:Class="VirtualizationDemo.App">
+  <Application.Styles>
+    <SimpleTheme />
+  </Application.Styles>
+</Application>

+ 1 - 1
samples/VirtualizationDemo/VirtualizationDemo.csproj

@@ -5,7 +5,7 @@
   </PropertyGroup>
   <ItemGroup>
     <ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
-    <ProjectReference Include="..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj" />
+    <ProjectReference Include="..\..\src\Avalonia.Themes.Simple\Avalonia.Themes.Simple.csproj" />
     <ProjectReference Include="..\..\src\Linux\Avalonia.LinuxFramebuffer\Avalonia.LinuxFramebuffer.csproj" />
     <ProjectReference Include="..\MiniMvvm\MiniMvvm.csproj" />
   </ItemGroup>

+ 2 - 3
samples/interop/Direct3DInteropSample/App.paml

@@ -1,6 +1,5 @@
 <Application xmlns="https://github.com/avaloniaui">
   <Application.Styles>
-    <StyleInclude Source="resm:Avalonia.Themes.Default.DefaultTheme.xaml?assembly=Avalonia.Themes.Default"/>
-    <StyleInclude Source="resm:Avalonia.Themes.Default.Accents.BaseLight.xaml?assembly=Avalonia.Themes.Default"/>
+    <SimpleTheme Mode="Light" />
   </Application.Styles>
-</Application>
+</Application>

+ 1 - 1
samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj

@@ -22,7 +22,7 @@
       </EmbeddedResource>
     </ItemGroup>
     <ItemGroup>
-        <ProjectReference Include="..\..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj" />
+        <ProjectReference Include="..\..\..\src\Avalonia.Themes.Simple\Avalonia.Themes.Simple.csproj" />
         <ProjectReference Include="..\..\..\src\Windows\Avalonia.Direct2D1\Avalonia.Direct2D1.csproj" />
         <ProjectReference Include="..\..\..\src\Windows\Avalonia.Win32\Avalonia.Win32.csproj" />
         <ProjectReference Include="..\..\MiniMvvm\MiniMvvm.csproj" />

+ 29 - 4
src/Android/Avalonia.Android/Platform/Storage/AndroidStorageItem.cs

@@ -1,6 +1,7 @@
 #nullable enable
 
 using System;
+using System.Collections.Generic;
 using System.Diagnostics.CodeAnalysis;
 using System.IO;
 using System.Linq;
@@ -35,13 +36,13 @@ internal abstract class AndroidStorageItem : IStorageBookmarkItem
 
     public bool CanBookmark => true;
 
-    public Task<string?> SaveBookmark()
+    public Task<string?> SaveBookmarkAsync()
     {
         Context.ContentResolver?.TakePersistableUriPermission(Uri, ActivityFlags.GrantWriteUriPermission | ActivityFlags.GrantReadUriPermission);
         return Task.FromResult(Uri.ToString());
     }
 
-    public Task ReleaseBookmark()
+    public Task ReleaseBookmarkAsync()
     {
         Context.ContentResolver?.ReleasePersistableUriPermission(Uri, ActivityFlags.GrantWriteUriPermission | ActivityFlags.GrantReadUriPermission);
         return Task.CompletedTask;
@@ -106,6 +107,30 @@ internal sealed class AndroidStorageFolder : AndroidStorageItem, IStorageBookmar
     {
         return Task.FromResult(new StorageItemProperties());
     }
+
+    public async Task<IReadOnlyList<IStorageItem>> GetItemsAsync()
+    {
+        using var javaFile = new JavaFile(Uri.Path!);
+
+        // Java file represents files AND directories. Don't be confused.
+        var files = await javaFile.ListFilesAsync().ConfigureAwait(false);
+        if (files is null)
+        {
+            return Array.Empty<IStorageItem>();
+        }
+
+        return files
+            .Select(f => (file: f, uri: AndroidUri.FromFile(f)))
+            .Where(t => t.uri is not null)
+            .Select(t => t.file switch
+            {
+                { IsFile: true } => (IStorageItem)new AndroidStorageFile(Context, t.uri!),
+                { IsDirectory: true } => new AndroidStorageFolder(Context, t.uri!),
+                _ => null
+            })
+            .Where(i => i is not null)
+            .ToArray()!;
+    }
 }
 
 internal sealed class AndroidStorageFile : AndroidStorageItem, IStorageBookmarkFile
@@ -118,10 +143,10 @@ internal sealed class AndroidStorageFile : AndroidStorageItem, IStorageBookmarkF
 
     public bool CanOpenWrite => true;
 
-    public Task<Stream> OpenRead() => Task.FromResult(OpenContentStream(Context, Uri, false)
+    public Task<Stream> OpenReadAsync() => Task.FromResult(OpenContentStream(Context, Uri, false)
         ?? throw new InvalidOperationException("Failed to open content stream"));
 
-    public Task<Stream> OpenWrite() => Task.FromResult(OpenContentStream(Context, Uri, true)
+    public Task<Stream> OpenWriteAsync() => Task.FromResult(OpenContentStream(Context, Uri, true)
         ?? throw new InvalidOperationException("Failed to open content stream"));
 
     private Stream? OpenContentStream(Context context, AndroidUri uri, bool isOutput)

+ 97 - 14
src/Avalonia.Base/Media/GlyphRun.cs

@@ -265,7 +265,7 @@ namespace Avalonia.Media
                 //RightToLeft
                 var glyphIndex = FindGlyphIndex(characterIndex);
 
-                if (GlyphClusters != null)
+                if (GlyphClusters != null && GlyphClusters.Count > 0)
                 {
                     if (characterIndex > GlyphClusters[0])
                     {
@@ -445,7 +445,7 @@ namespace Avalonia.Media
         /// </returns>
         public int FindGlyphIndex(int characterIndex)
         {
-            if (GlyphClusters == null)
+            if (GlyphClusters == null || GlyphClusters.Count == 0)
             {
                 return characterIndex;
             }
@@ -614,17 +614,29 @@ namespace Avalonia.Media
 
         private GlyphRunMetrics CreateGlyphRunMetrics()
         {
+            var firstCluster = 0;
+            var lastCluster = Characters.Length - 1;
+
+            if (!IsLeftToRight)
+            {
+                var cluster = firstCluster;
+                firstCluster = lastCluster;
+                lastCluster = cluster;
+            }
+
             if (GlyphClusters != null && GlyphClusters.Count > 0)
             {
-                var firstCluster = GlyphClusters[0];
+                firstCluster = GlyphClusters[0];
+                lastCluster = GlyphClusters[GlyphClusters.Count - 1];
 
                 _offsetToFirstCharacter = Math.Max(0, Characters.Start - firstCluster);
             }
 
+            var isReversed = firstCluster > lastCluster;
             var height = (GlyphTypeface.Descent - GlyphTypeface.Ascent + GlyphTypeface.LineGap) * Scale;
             var widthIncludingTrailingWhitespace = 0d;
 
-            var trailingWhitespaceLength = GetTrailingWhitespaceLength(out var newLineLength, out var glyphCount);
+            var trailingWhitespaceLength = GetTrailingWhitespaceLength(isReversed, out var newLineLength, out var glyphCount);
 
             for (var index = 0; index < GlyphIndices.Count; index++)
             {
@@ -635,16 +647,16 @@ namespace Avalonia.Media
 
             var width = widthIncludingTrailingWhitespace;
 
-            if (IsLeftToRight)
+            if (isReversed)
             {
-                for (var index = GlyphIndices.Count - glyphCount; index < GlyphIndices.Count; index++)
+                for (var index = 0; index < glyphCount; index++)
                 {
                     width -= GetGlyphAdvance(index, out _);
                 }
             }
             else
             {
-                for (var index = 0; index < glyphCount; index++)
+                for (var index = GlyphIndices.Count - glyphCount; index < GlyphIndices.Count; index++)
                 {
                     width -= GetGlyphAdvance(index, out _);
                 }
@@ -654,16 +666,15 @@ namespace Avalonia.Media
                 height);
         }
 
-        private int GetTrailingWhitespaceLength(out int newLineLength, out int glyphCount)
-        {
-            glyphCount = 0;
-            newLineLength = 0;
-
-            if (Characters.IsEmpty)
+        private int GetTrailingWhitespaceLength(bool isReversed, out int newLineLength, out int glyphCount)
+        {          
+            if (isReversed)
             {
-                return 0;
+                return GetTralingWhitespaceLengthRightToLeft(out newLineLength, out glyphCount);
             }
 
+            glyphCount = 0;
+            newLineLength = 0;
             var trailingWhitespaceLength = 0;
 
             if (GlyphClusters == null)
@@ -732,6 +743,78 @@ namespace Avalonia.Media
             return trailingWhitespaceLength;
         }
 
+        private int GetTralingWhitespaceLengthRightToLeft(out int newLineLength, out int glyphCount)
+        {
+            glyphCount = 0;
+            newLineLength = 0;
+            var trailingWhitespaceLength = 0;
+
+            if (GlyphClusters == null)
+            {
+                for (var i = 0; i < Characters.Length;)
+                {
+                    var codepoint = Codepoint.ReadAt(_characters, i, out var count);
+
+                    if (!codepoint.IsWhiteSpace)
+                    {
+                        break;
+                    }
+
+                    if (codepoint.IsBreakChar)
+                    {
+                        newLineLength++;
+                    }
+
+                    trailingWhitespaceLength++;
+
+                    i += count;
+                    glyphCount++;
+                }
+            }
+            else
+            {
+                for (var i = 0; i < GlyphClusters.Count; i++)
+                {
+                    var currentCluster = GlyphClusters[i];
+                    var characterIndex = Math.Max(0, currentCluster - _characters.BufferOffset);
+                    var codepoint = Codepoint.ReadAt(_characters, characterIndex, out _);
+
+                    if (!codepoint.IsWhiteSpace)
+                    {
+                        break;
+                    }
+
+                    var clusterLength = 1;
+
+                    while (i - 1 >= 0)
+                    {
+                        var nextCluster = GlyphClusters[i - 1];
+
+                        if (currentCluster == nextCluster)
+                        {
+                            clusterLength++;
+                            i--;
+
+                            continue;
+                        }
+
+                        break;
+                    }
+
+                    if (codepoint.IsBreakChar)
+                    {
+                        newLineLength += clusterLength;
+                    }
+
+                    trailingWhitespaceLength += clusterLength;
+
+                    glyphCount++;
+                }
+            }
+
+            return trailingWhitespaceLength;
+        }
+
         private void Set<T>(ref T field, T value)
         {
             _glyphRunImpl?.Dispose();

+ 1 - 1
src/Avalonia.Base/Media/TextDecoration.cs

@@ -209,7 +209,7 @@ namespace Avalonia.Media
             var pen = new Pen(Stroke ?? defaultBrush, thickness,
                 new DashStyle(StrokeDashArray, StrokeDashOffset), StrokeLineCap);
 
-            drawingContext.DrawLine(pen, origin, origin + new Point(glyphRun.Size.Width, 0));
+            drawingContext.DrawLine(pen, origin, origin + new Point(glyphRun.Metrics.Width, 0));
         }
     }
 }

+ 24 - 0
src/Avalonia.Base/Media/TextFormatting/FormattedTextSource.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Generic;
+using Avalonia.Media.TextFormatting.Unicode;
 using Avalonia.Utilities;
 
 namespace Avalonia.Media.TextFormatting
@@ -116,7 +117,30 @@ namespace Avalonia.Media.TextFormatting
                 length = text.Length;
             }
 
+            length = CoerceLength(text, length);
+
             return new ValueSpan<TextRunProperties>(firstTextSourceIndex, length, currentProperties);
         }
+
+        private static int CoerceLength(ReadOnlySlice<char> text, int length)
+        {
+            var finalLength = 0;
+
+            var graphemeEnumerator = new GraphemeEnumerator(text);
+
+            while (graphemeEnumerator.MoveNext())
+            {
+                var grapheme = graphemeEnumerator.Current;
+
+                finalLength += grapheme.Text.Length;
+
+                if (finalLength >= length)
+                {
+                    return finalLength;
+                }
+            }
+
+            return length;
+        }
     }
 }

+ 12 - 5
src/Avalonia.Base/Media/TextFormatting/InterWordJustification.cs

@@ -15,6 +15,13 @@ namespace Avalonia.Media.TextFormatting
 
         public override void Justify(TextLine textLine)
         {
+            var lineImpl = textLine as TextLineImpl;
+
+            if(lineImpl is null)
+            {
+                return;
+            }
+
             var paragraphWidth = Width;
 
             if (double.IsInfinity(paragraphWidth))
@@ -22,12 +29,12 @@ namespace Avalonia.Media.TextFormatting
                 return;
             }
 
-            if (textLine.NewLineLength > 0)
+            if (lineImpl.NewLineLength > 0)
             {
                 return;
             }
 
-            var textLineBreak = textLine.TextLineBreak;
+            var textLineBreak = lineImpl.TextLineBreak;
 
             if (textLineBreak is not null && textLineBreak.TextEndOfLine is not null)
             {
@@ -39,7 +46,7 @@ namespace Avalonia.Media.TextFormatting
 
             var breakOportunities = new Queue<int>();
 
-            foreach (var textRun in textLine.TextRuns)
+            foreach (var textRun in lineImpl.TextRuns)
             {
                 var text = textRun.Text;
 
@@ -68,10 +75,10 @@ namespace Avalonia.Media.TextFormatting
                 return;
             }
 
-            var remainingSpace = Math.Max(0, paragraphWidth - textLine.WidthIncludingTrailingWhitespace);
+            var remainingSpace = Math.Max(0, paragraphWidth - lineImpl.WidthIncludingTrailingWhitespace);
             var spacing = remainingSpace / breakOportunities.Count;
 
-            foreach (var textRun in textLine.TextRuns)
+            foreach (var textRun in lineImpl.TextRuns)
             {
                 var text = textRun.Text;
 

+ 13 - 13
src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs

@@ -38,7 +38,7 @@ namespace Avalonia.Media.TextFormatting
         /// Gets a list of <see cref="ShapeableTextCharacters"/>.
         /// </summary>
         /// <returns>The shapeable text characters.</returns>
-        internal IReadOnlyList<ShapeableTextCharacters> GetShapeableCharacters(ReadOnlySlice<char> runText, sbyte biDiLevel, 
+        internal IReadOnlyList<ShapeableTextCharacters> GetShapeableCharacters(ReadOnlySlice<char> runText, sbyte biDiLevel,
             ref TextRunProperties? previousProperties)
         {
             var shapeableCharacters = new List<ShapeableTextCharacters>(2);
@@ -65,7 +65,7 @@ namespace Avalonia.Media.TextFormatting
         /// <param name="biDiLevel">The bidi level of the run.</param>
         /// <param name="previousProperties"></param>
         /// <returns>A list of shapeable text runs.</returns>
-        private static ShapeableTextCharacters CreateShapeableRun(ReadOnlySlice<char> text, 
+        private static ShapeableTextCharacters CreateShapeableRun(ReadOnlySlice<char> text,
             TextRunProperties defaultProperties, sbyte biDiLevel, ref TextRunProperties? previousProperties)
         {
             var defaultTypeface = defaultProperties.Typeface;
@@ -76,7 +76,7 @@ namespace Avalonia.Media.TextFormatting
             {
                 if (script == Script.Common && previousTypeface is not null)
                 {
-                    if(TryGetShapeableLength(text, previousTypeface.Value, defaultTypeface, out var fallbackCount, out _))
+                    if (TryGetShapeableLength(text, previousTypeface.Value, defaultTypeface, out var fallbackCount, out _))
                     {
                         return new ShapeableTextCharacters(text.Take(fallbackCount),
                             defaultProperties.WithTypeface(previousTypeface.Value), biDiLevel);
@@ -86,10 +86,10 @@ namespace Avalonia.Media.TextFormatting
                 return new ShapeableTextCharacters(text.Take(count), defaultProperties.WithTypeface(currentTypeface),
                     biDiLevel);
             }
-            
+
             if (previousTypeface is not null)
             {
-                if(TryGetShapeableLength(text, previousTypeface.Value, defaultTypeface, out count, out _))
+                if (TryGetShapeableLength(text, previousTypeface.Value, defaultTypeface, out count, out _))
                 {
                     return new ShapeableTextCharacters(text.Take(count),
                         defaultProperties.WithTypeface(previousTypeface.Value), biDiLevel);
@@ -106,12 +106,12 @@ namespace Avalonia.Media.TextFormatting
                 {
                     continue;
                 }
-                
+
                 codepoint = codepointEnumerator.Current;
-                    
+
                 break;
             }
-            
+
             //ToDo: Fix FontFamily fallback
             var matchFound =
                 FontManager.Current.TryMatchCharacter(codepoint, defaultTypeface.Style, defaultTypeface.Weight,
@@ -157,14 +157,14 @@ namespace Avalonia.Media.TextFormatting
         /// <param name="script"></param>
         /// <returns></returns>
         protected static bool TryGetShapeableLength(
-            ReadOnlySlice<char> text, 
-            Typeface typeface, 
+            ReadOnlySlice<char> text,
+            Typeface typeface,
             Typeface? defaultTypeface,
             out int length,
             out Script script)
         {
             length = 0;
-            script = Script.Unknown;         
+            script = Script.Unknown;
 
             if (text.Length == 0)
             {
@@ -182,7 +182,7 @@ namespace Avalonia.Media.TextFormatting
 
                 var currentScript = currentGrapheme.FirstCodepoint.Script;
 
-                if (currentScript != Script.Common && defaultFont != null && defaultFont.TryGetGlyph(currentGrapheme.FirstCodepoint, out _))
+                if (!currentGrapheme.FirstCodepoint.IsWhiteSpace && defaultFont != null && defaultFont.TryGetGlyph(currentGrapheme.FirstCodepoint, out _))
                 {
                     break;
                 }
@@ -192,7 +192,7 @@ namespace Avalonia.Media.TextFormatting
                 {
                     break;
                 }
-                
+
                 if (currentScript != script)
                 {
                     if (script is Script.Unknown || currentScript != Script.Common &&

+ 31 - 27
src/Avalonia.Base/Media/TextFormatting/TextLayout.cs

@@ -63,7 +63,7 @@ namespace Avalonia.Media.TextFormatting
 
             MaxHeight = maxHeight;
 
-            MaxLines = maxLines;      
+            MaxLines = maxLines;
 
             TextLines = CreateTextLines();
         }
@@ -80,7 +80,7 @@ namespace Avalonia.Media.TextFormatting
         /// <param name="maxLines">The maximum number of text lines.</param>
         public TextLayout(
             ITextSource textSource,
-            TextParagraphProperties paragraphProperties, 
+            TextParagraphProperties paragraphProperties,
             TextTrimming? textTrimming = null,
             double maxWidth = double.PositiveInfinity,
             double maxHeight = double.PositiveInfinity,
@@ -178,24 +178,18 @@ namespace Avalonia.Media.TextFormatting
                 return new Rect();
             }
 
-            if (textPosition < 0 || textPosition >= _textSourceLength)
+            if (textPosition < 0)
             {
-                var lastLine = TextLines[TextLines.Count - 1];
-
-                var lineX = lastLine.Width;
-
-                var lineY = Bounds.Bottom - lastLine.Height;
-
-                return new Rect(lineX, lineY, 0, lastLine.Height);
+                textPosition = _textSourceLength;
             }
 
             var currentY = 0.0;
 
             foreach (var textLine in TextLines)
             {
-                var end = textLine.FirstTextSourceIndex + textLine.Length - 1;
+                var end = textLine.FirstTextSourceIndex + textLine.Length;
 
-                if (end < textPosition)
+                if (end <= textPosition && end < _textSourceLength)
                 {
                     currentY += textLine.Height;
 
@@ -224,7 +218,7 @@ namespace Avalonia.Media.TextFormatting
             }
 
             var result = new List<Rect>(TextLines.Count);
-            
+
             var currentY = 0d;
 
             foreach (var textLine in TextLines)
@@ -239,7 +233,7 @@ namespace Avalonia.Media.TextFormatting
 
                 var textBounds = textLine.GetTextBounds(start, length);
 
-                if(textBounds.Count > 0)
+                if (textBounds.Count > 0)
                 {
                     foreach (var bounds in textBounds)
                     {
@@ -262,7 +256,7 @@ namespace Avalonia.Media.TextFormatting
                     }
                 }
 
-                if(textLine.FirstTextSourceIndex + textLine.Length >= start + length)
+                if (textLine.FirstTextSourceIndex + textLine.Length >= start + length)
                 {
                     break;
                 }
@@ -305,7 +299,7 @@ namespace Avalonia.Media.TextFormatting
             return GetHitTestResult(currentLine, characterHit, point);
         }
 
-        
+
         public int GetLineIndexFromCharacterIndex(int charIndex, bool trailingEdge)
         {
             if (charIndex < 0)
@@ -327,7 +321,7 @@ namespace Avalonia.Media.TextFormatting
                     continue;
                 }
 
-                if (charIndex >= textLine.FirstTextSourceIndex && 
+                if (charIndex >= textLine.FirstTextSourceIndex &&
                     charIndex <= textLine.FirstTextSourceIndex + textLine.Length - (trailingEdge ? 0 : 1))
                 {
                     return index;
@@ -398,7 +392,7 @@ namespace Avalonia.Media.TextFormatting
         /// <param name="left">The current left.</param>
         /// <param name="width">The current width.</param>
         /// <param name="height">The current height.</param>
-        private static void UpdateBounds(TextLine textLine,ref double left,  ref double width, ref double height)
+        private static void UpdateBounds(TextLine textLine, ref double left, ref double width, ref double height)
         {
             var lineWidth = textLine.WidthIncludingTrailingWhitespace;
 
@@ -421,7 +415,7 @@ namespace Avalonia.Media.TextFormatting
             {
                 var textLine = TextFormatterImpl.CreateEmptyTextLine(0, double.PositiveInfinity, _paragraphProperties);
 
-                Bounds = new Rect(0,0,0, textLine.Height);
+                Bounds = new Rect(0, 0, 0, textLine.Height);
 
                 return new List<TextLine> { textLine };
             }
@@ -439,9 +433,9 @@ namespace Avalonia.Media.TextFormatting
                 var textLine = TextFormatter.Current.FormatLine(_textSource, _textSourceLength, MaxWidth,
                     _paragraphProperties, previousLine?.TextLineBreak);
 
-                if(textLine == null || textLine.Length == 0 || textLine.TextRuns.Count == 0 && textLine.TextLineBreak?.TextEndOfLine is TextEndOfParagraph)
+                if (textLine == null || textLine.Length == 0 || textLine.TextRuns.Count == 0 && textLine.TextLineBreak?.TextEndOfLine is TextEndOfParagraph)
                 {
-                    if(previousLine != null && previousLine.NewLineLength  > 0)
+                    if (previousLine != null && previousLine.NewLineLength > 0)
                     {
                         var emptyTextLine = TextFormatterImpl.CreateEmptyTextLine(_textSourceLength, MaxWidth, _paragraphProperties);
 
@@ -454,7 +448,7 @@ namespace Avalonia.Media.TextFormatting
                 }
 
                 _textSourceLength += textLine.Length;
-                
+
                 //Fulfill max height constraint
                 if (textLines.Count > 0 && !double.IsPositiveInfinity(MaxHeight) && height + textLine.Height > MaxHeight)
                 {
@@ -485,12 +479,17 @@ namespace Avalonia.Media.TextFormatting
                 //Fulfill max lines constraint
                 if (MaxLines > 0 && textLines.Count >= MaxLines)
                 {
+                    if(textLine.TextLineBreak is TextLineBreak lineBreak && lineBreak.RemainingRuns != null)
+                    {
+                        textLines[textLines.Count - 1] = textLine.Collapse(GetCollapsingProperties(width));
+                    }
+
                     break;
                 }
             }
 
             //Make sure the TextLayout always contains at least on empty line
-            if(textLines.Count == 0)
+            if (textLines.Count == 0)
             {
                 var textLine = TextFormatterImpl.CreateEmptyTextLine(0, MaxWidth, _paragraphProperties);
 
@@ -501,7 +500,7 @@ namespace Avalonia.Media.TextFormatting
 
             Bounds = new Rect(left, 0, width, height);
 
-            if(_paragraphProperties.TextAlignment == TextAlignment.Justify)
+            if (_paragraphProperties.TextAlignment == TextAlignment.Justify)
             {
                 var whitespaceWidth = 0d;
 
@@ -509,7 +508,7 @@ namespace Avalonia.Media.TextFormatting
                 {
                     var lineWhitespaceWidth = line.Width - line.WidthIncludingTrailingWhitespace;
 
-                    if(lineWhitespaceWidth > whitespaceWidth)
+                    if (lineWhitespaceWidth > whitespaceWidth)
                     {
                         whitespaceWidth = lineWhitespaceWidth;
                     }
@@ -517,7 +516,7 @@ namespace Avalonia.Media.TextFormatting
 
                 var justificationWidth = width - whitespaceWidth;
 
-                if(justificationWidth > 0)
+                if (justificationWidth > 0)
                 {
                     var justificationProperties = new InterWordJustification(justificationWidth);
 
@@ -538,8 +537,13 @@ namespace Avalonia.Media.TextFormatting
         /// </summary>
         /// <param name="width">The collapsing width.</param>
         /// <returns>The <see cref="TextCollapsingProperties"/>.</returns>
-        private TextCollapsingProperties GetCollapsingProperties(double width)
+        private TextCollapsingProperties? GetCollapsingProperties(double width)
         {
+            if(_textTrimming == TextTrimming.None)
+            {
+                return null;
+            }
+
             return _textTrimming.CreateCollapsingProperties(new TextCollapsingCreateInfo(width, _paragraphProperties.DefaultTextRunProperties));
         }
     }

+ 1 - 1
src/Avalonia.Base/Media/TextFormatting/TextLine.cs

@@ -153,7 +153,7 @@ namespace Avalonia.Media.TextFormatting
         /// <returns>
         /// A <see cref="TextLine"/> value that represents a collapsed line that can be displayed.
         /// </returns>
-        public abstract TextLine Collapse(params TextCollapsingProperties[] collapsingPropertiesList);
+        public abstract TextLine Collapse(params TextCollapsingProperties?[] collapsingPropertiesList);
 
         /// <summary>
         /// Create a justified line based on justification text properties.

+ 502 - 235
src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs

@@ -119,7 +119,7 @@ namespace Avalonia.Media.TextFormatting
         }
 
         /// <inheritdoc/>
-        public override TextLine Collapse(params TextCollapsingProperties[] collapsingPropertiesList)
+        public override TextLine Collapse(params TextCollapsingProperties?[] collapsingPropertiesList)
         {
             if (collapsingPropertiesList.Length == 0)
             {
@@ -128,6 +128,11 @@ namespace Avalonia.Media.TextFormatting
 
             var collapsingProperties = collapsingPropertiesList[0];
 
+            if(collapsingProperties is null)
+            {
+                return this;
+            }
+
             var collapsedRuns = collapsingProperties.Collapse(this);
 
             if (collapsedRuns is null)
@@ -166,58 +171,122 @@ namespace Avalonia.Media.TextFormatting
 
             if (distance <= 0)
             {
-                // hit happens before the line, return the first position
                 var firstRun = _textRuns[0];
 
-                if (firstRun is ShapedTextCharacters shapedTextCharacters)
-                {
-                    return shapedTextCharacters.GlyphRun.GetCharacterHitFromDistance(distance, out _);
-                }
+                return GetRunCharacterHit(firstRun, FirstTextSourceIndex, 0);
+            }
 
-                return _resolvedFlowDirection == FlowDirection.LeftToRight ?
-                    new CharacterHit(FirstTextSourceIndex) :
-                    new CharacterHit(FirstTextSourceIndex + Length);
+            if (distance >= WidthIncludingTrailingWhitespace)
+            {
+                var lastRun = _textRuns[_textRuns.Count - 1];
+
+                return GetRunCharacterHit(lastRun, FirstTextSourceIndex + Length - lastRun.TextSourceLength, lastRun.Size.Width);
             }
 
             // process hit that happens within the line
             var characterHit = new CharacterHit();
             var currentPosition = FirstTextSourceIndex;
+            var currentDistance = 0.0;
 
-            foreach (var currentRun in _textRuns)
+            for (var i = 0; i < _textRuns.Count; i++)
             {
-                switch (currentRun)
+                var currentRun = _textRuns[i];
+
+                if(currentRun is ShapedTextCharacters shapedRun && !shapedRun.ShapedBuffer.IsLeftToRight)
                 {
-                    case ShapedTextCharacters shapedRun:
+                    var rightToLeftIndex = i;
+                    currentPosition += currentRun.TextSourceLength;
+
+                    while (rightToLeftIndex + 1 <= _textRuns.Count - 1)
+                    {
+                        var nextShaped = _textRuns[rightToLeftIndex + 1] as ShapedTextCharacters;
+
+                        if (nextShaped == null || nextShaped.ShapedBuffer.IsLeftToRight)
                         {
-                            characterHit = shapedRun.GlyphRun.GetCharacterHitFromDistance(distance, out _);
+                            break;
+                        }
 
-                            var offset = Math.Max(0, currentPosition - shapedRun.Text.Start);
+                        currentPosition += nextShaped.TextSourceLength;
 
-                            characterHit = new CharacterHit(characterHit.FirstCharacterIndex + offset, characterHit.TrailingLength);
+                        rightToLeftIndex++;
+                    }
 
+                    for (var j = i; i <= rightToLeftIndex; j++)
+                    {
+                        if(j > _textRuns.Count - 1)
+                        {
                             break;
                         }
-                    default:
+
+                        currentRun = _textRuns[j];
+
+                        if(currentDistance + currentRun.Size.Width <= distance)
                         {
-                            if (distance < currentRun.Size.Width / 2)
-                            {
-                                characterHit = new CharacterHit(currentPosition);
-                            }
-                            else
-                            {
-                                characterHit = new CharacterHit(currentPosition, currentRun.TextSourceLength);
-                            }
-                            break;
+                            currentDistance += currentRun.Size.Width;
+                            currentPosition -= currentRun.TextSourceLength;
+
+                            continue;
                         }
+
+                        characterHit = GetRunCharacterHit(currentRun, currentPosition, distance - currentDistance);
+
+                        break;
+                    }
                 }
 
-                if (distance <= currentRun.Size.Width)
+                if (currentDistance + currentRun.Size.Width < distance)
                 {
-                    break;
+                    currentDistance += currentRun.Size.Width;
+                    currentPosition += currentRun.TextSourceLength;
+
+                    continue;
                 }
 
-                distance -= currentRun.Size.Width;
-                currentPosition += currentRun.TextSourceLength;
+                characterHit = GetRunCharacterHit(currentRun, currentPosition, distance - currentDistance);
+
+                break;
+            }
+
+            return characterHit;
+        }
+
+        private static CharacterHit GetRunCharacterHit(DrawableTextRun run, int currentPosition, double distance)
+        {
+            CharacterHit characterHit;
+
+            switch (run)
+            {
+                case ShapedTextCharacters shapedRun:
+                    {
+                        characterHit = shapedRun.GlyphRun.GetCharacterHitFromDistance(distance, out _);
+
+                        var offset = 0;
+
+                        if (shapedRun.GlyphRun.IsLeftToRight)
+                        {
+                            offset = Math.Max(0, currentPosition - shapedRun.Text.Start);
+                        }
+                        //else
+                        //{
+                        //    offset = Math.Max(0, currentPosition - shapedRun.Text.Start + shapedRun.Text.Length);
+                        //}
+
+                        characterHit = new CharacterHit(characterHit.FirstCharacterIndex + offset, characterHit.TrailingLength);
+
+                        break;
+                    }
+                default:
+                    {
+                        if (distance < run.Size.Width / 2)
+                        {
+                            characterHit = new CharacterHit(currentPosition);
+                        }
+                        else
+                        {
+                            characterHit = new CharacterHit(currentPosition, run.TextSourceLength);
+                        }
+                        break;
+                    }
             }
 
             return characterHit;
@@ -226,136 +295,168 @@ namespace Avalonia.Media.TextFormatting
         /// <inheritdoc/>
         public override double GetDistanceFromCharacterHit(CharacterHit characterHit)
         {
-            var isTrailingHit = characterHit.TrailingLength > 0;
+            var flowDirection = _paragraphProperties.FlowDirection;
             var characterIndex = characterHit.FirstCharacterIndex + characterHit.TrailingLength;
-            var currentDistance = Start;
             var currentPosition = FirstTextSourceIndex;
             var remainingLength = characterIndex - FirstTextSourceIndex;
 
-            GlyphRun? lastRun = null;
+            var currentDistance = Start;
 
-            for (var index = 0; index < _textRuns.Count; index++)
+            if (flowDirection == FlowDirection.LeftToRight)
             {
-                var textRun = _textRuns[index];
-
-                switch (textRun)
+                for (var index = 0; index < _textRuns.Count; index++)
                 {
-                    case ShapedTextCharacters shapedTextCharacters:
-                        {
-                            var currentRun = shapedTextCharacters.GlyphRun;
+                    var currentRun = _textRuns[index];
 
-                            if (lastRun != null)
-                            {
-                                if (!lastRun.IsLeftToRight && currentRun.IsLeftToRight &&
-                                    currentRun.Characters.Start == characterHit.FirstCharacterIndex &&
-                                    characterHit.TrailingLength == 0)
-                                {
-                                    return currentDistance;
-                                }
-                            }
+                    if (currentRun is ShapedTextCharacters shapedRun && !shapedRun.ShapedBuffer.IsLeftToRight)
+                    {
+                        var i = index;
+
+                        var rightToLeftWidth = currentRun.Size.Width;
 
-                            //Look for a hit in within the current run
-                            if (currentPosition + remainingLength <= currentPosition + textRun.Text.Length)
+                        while (i + 1 <= _textRuns.Count - 1)
+                        {
+                            var nextRun = _textRuns[i + 1];
+
+                            if (nextRun is ShapedTextCharacters nextShapedRun && !nextShapedRun.ShapedBuffer.IsLeftToRight)
                             {
-                                characterHit = new CharacterHit(textRun.Text.Start + remainingLength);
+                                i++;
 
-                                var distance = currentRun.GetDistanceFromCharacterHit(characterHit);
+                                rightToLeftWidth += nextRun.Size.Width;
 
-                                return currentDistance + distance;
+                                continue;
                             }
+                            
+                            break;
+                        }
 
-                            //Look at the left and right edge of the current run
-                            if (currentRun.IsLeftToRight)
+                        if(i > index)
+                        {
+                            while (i >= index)
                             {
-                                if (_resolvedFlowDirection == FlowDirection.LeftToRight && (lastRun == null || lastRun.IsLeftToRight))
-                                {
-                                    if (characterIndex <= currentPosition)
-                                    {
-                                        return currentDistance;
-                                    }
-                                }
-                                else
-                                {
-                                    if (characterIndex == currentPosition)
-                                    {
-                                        return currentDistance;
-                                    }
-                                }
+                                currentRun = _textRuns[i];
 
-                                if (characterIndex == currentPosition + textRun.Text.Length && isTrailingHit)
-                                {
-                                    return currentDistance + currentRun.Size.Width;
-                                }
-                            }
-                            else
-                            {
-                                if (characterIndex == currentPosition)
+                                rightToLeftWidth -= currentRun.Size.Width;
+
+                                if (currentPosition + currentRun.TextSourceLength >= characterIndex)
                                 {
-                                    return currentDistance + currentRun.Size.Width;
+                                    break;
                                 }
 
-                                var nextRun = index + 1 < _textRuns.Count ?
-                                    _textRuns[index + 1] as ShapedTextCharacters :
-                                    null;
+                                currentPosition += currentRun.TextSourceLength;
 
-                                if (nextRun != null)
-                                {
-                                    if (nextRun.ShapedBuffer.IsLeftToRight)
-                                    {
-                                        if (characterIndex == currentPosition + textRun.Text.Length)
-                                        {
-                                            return currentDistance;
-                                        }
-                                    }
-                                    else
-                                    {
-                                        if (currentPosition + nextRun.Text.Length == characterIndex)
-                                        {
-                                            return currentDistance;
-                                        }
-                                    }
-                                }
-                                else
-                                {
-                                    if (characterIndex > currentPosition + textRun.Text.Length)
-                                    {
-                                        return currentDistance;
-                                    }
-                                }
+                                remainingLength -= currentRun.TextSourceLength;
+
+                                i--;
                             }
 
-                            lastRun = currentRun;
+                            currentDistance += rightToLeftWidth;
+                        }
+                    }
+
+                    if (currentPosition + currentRun.TextSourceLength >= characterIndex && 
+                        TryGetDistanceFromCharacterHit(currentRun, characterHit, currentPosition, remainingLength, flowDirection, out var distance, out _))
+                    {
+                        return Math.Max(0, currentDistance + distance);
+                    }
 
-                            break;
+                    //No hit hit found so we add the full width
+                    currentDistance += currentRun.Size.Width;
+                    currentPosition += currentRun.TextSourceLength;
+                    remainingLength -= currentRun.TextSourceLength;
+                }
+            }
+            else
+            {
+                currentDistance += WidthIncludingTrailingWhitespace;
+
+                for (var index = _textRuns.Count - 1; index >= 0; index--)
+                {
+                    var currentRun = _textRuns[index];
+
+                    if (TryGetDistanceFromCharacterHit(currentRun, characterHit, currentPosition, remainingLength,
+                        flowDirection, out var distance, out var currentGlyphRun))
+                    {
+                        if (currentGlyphRun != null)
+                        {
+                            distance = currentGlyphRun.Size.Width - distance;
                         }
-                    default:
+
+                        return Math.Max(0, currentDistance - distance);
+                    }
+
+                    //No hit hit found so we add the full width
+                    currentDistance -= currentRun.Size.Width;
+                    currentPosition += currentRun.TextSourceLength;
+                    remainingLength -= currentRun.TextSourceLength;
+                }
+            }
+
+            return Math.Max(0, currentDistance);
+        }
+
+        private static bool TryGetDistanceFromCharacterHit(
+            DrawableTextRun currentRun,
+            CharacterHit characterHit,
+            int currentPosition,
+            int remainingLength,
+            FlowDirection flowDirection,
+            out double distance,
+            out GlyphRun? currentGlyphRun)
+        {
+            var characterIndex = characterHit.FirstCharacterIndex + characterHit.TrailingLength;
+            var isTrailingHit = characterHit.TrailingLength > 0;
+
+            distance = 0;
+            currentGlyphRun = null;
+
+            switch (currentRun)
+            {
+                case ShapedTextCharacters shapedTextCharacters:
+                    {
+                        currentGlyphRun = shapedTextCharacters.GlyphRun;
+
+                        if (currentPosition + remainingLength <= currentPosition + currentRun.Text.Length)
                         {
-                            if (characterIndex == currentPosition)
-                            {
-                                return currentDistance;
-                            }
+                            characterHit = new CharacterHit(currentRun.Text.Start + remainingLength);
+
+                            distance = currentGlyphRun.GetDistanceFromCharacterHit(characterHit);
+
+                            return true;
+                        }
 
-                            if (characterIndex == currentPosition + textRun.TextSourceLength)
+                        if (currentPosition + remainingLength == currentPosition + currentRun.Text.Length && isTrailingHit)
+                        {
+                            if (currentGlyphRun.IsLeftToRight || flowDirection == FlowDirection.RightToLeft)
                             {
-                                return currentDistance + textRun.Size.Width;
+                                distance = currentGlyphRun.Size.Width;
                             }
 
-                            break;
+                            return true;
                         }
-                }
 
-                //No hit hit found so we add the full width
-                currentDistance += textRun.Size.Width;
-                currentPosition += textRun.TextSourceLength;
-                remainingLength -= textRun.TextSourceLength;
+                        break;
+                    }
+                default:
+                    {
+                        if (characterIndex == currentPosition)
+                        {
+                            return true;
+                        }
 
-                if (remainingLength <= 0)
-                {
-                    break;
-                }
+                        if (characterIndex == currentPosition + currentRun.TextSourceLength)
+                        {
+                            distance = currentRun.Size.Width;
+
+                            return true;
+
+                        }
+
+                        break;
+                    }
             }
 
-            return currentDistance;
+            return false;
         }
 
         /// <inheritdoc/>
@@ -440,121 +541,168 @@ namespace Avalonia.Media.TextFormatting
                     continue;
                 }
 
-                if (currentPosition + currentRun.TextSourceLength <= firstTextSourceIndex)
-                {
-                    startX += currentRun.Size.Width;
-
-                    currentPosition += currentRun.TextSourceLength;
-
-                    continue;
-                }
-
                 var characterLength = 0;
                 var endX = startX;
+                var runWidth = 0.0;
+                TextRunBounds? currentRunBounds = null;
 
-                if (currentRun is ShapedTextCharacters currentShapedRun)
-                {
-                    var offset = Math.Max(0, firstTextSourceIndex - currentPosition);
-
-                    currentPosition += offset;
+                var currentShapedRun = currentRun as ShapedTextCharacters;
 
-                    var startIndex = currentRun.Text.Start + offset;
+                if (currentShapedRun != null && !currentShapedRun.ShapedBuffer.IsLeftToRight)
+                {
+                    var rightToLeftIndex = index;
+                    startX += currentShapedRun.Size.Width;
 
-                    var endOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit(
-                       currentShapedRun.ShapedBuffer.IsLeftToRight ?
-                            new CharacterHit(startIndex + remainingLength) :
-                            new CharacterHit(startIndex));
+                    while (rightToLeftIndex + 1 <= _textRuns.Count - 1)
+                    {
+                        var nextShapedRun = _textRuns[rightToLeftIndex + 1] as ShapedTextCharacters;
 
-                    endX += endOffset;
+                        if (nextShapedRun == null || nextShapedRun.ShapedBuffer.IsLeftToRight)
+                        {
+                            break;
+                        }
 
-                    var startOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit(
-                        currentShapedRun.ShapedBuffer.IsLeftToRight ?
-                            new CharacterHit(startIndex) :
-                            new CharacterHit(startIndex + remainingLength));
+                        startX += nextShapedRun.Size.Width;
 
-                    startX += startOffset;
+                        rightToLeftIndex++;
+                    }
 
-                    var endHit = currentShapedRun.GlyphRun.GetCharacterHitFromDistance(endOffset, out _);
-                    var startHit = currentShapedRun.GlyphRun.GetCharacterHitFromDistance(startOffset, out _);
+                    if (TryGetTextRunBoundsRightToLeft(startX, firstTextSourceIndex, characterIndex, rightToLeftIndex, ref currentPosition, ref remainingLength, out currentRunBounds))
+                    {
+                        startX = currentRunBounds!.Rectangle.Left;
+                        endX = currentRunBounds.Rectangle.Right;
 
-                    characterLength = Math.Abs(endHit.FirstCharacterIndex + endHit.TrailingLength - startHit.FirstCharacterIndex - startHit.TrailingLength);
+                        runWidth = currentRunBounds.Rectangle.Width;
+                    }
 
-                    currentDirection = currentShapedRun.ShapedBuffer.IsLeftToRight ?
-                        FlowDirection.LeftToRight :
-                        FlowDirection.RightToLeft;
+                    currentDirection = FlowDirection.RightToLeft;
                 }
                 else
                 {
-                    if (currentPosition < firstTextSourceIndex)
+                    if (currentShapedRun != null)
                     {
-                        startX += currentRun.Size.Width;
-                    }
+                        if (currentPosition + currentRun.TextSourceLength <= firstTextSourceIndex)
+                        {
+                            startX += currentRun.Size.Width;
 
-                    if (currentPosition + currentRun.TextSourceLength <= characterIndex)
-                    {
-                        endX += currentRun.Size.Width;
+                            currentPosition += currentRun.TextSourceLength;
 
-                        characterLength = currentRun.TextSourceLength;
+                            continue;
+                        }
+
+                        var offset = Math.Max(0, firstTextSourceIndex - currentPosition);
+
+                        currentPosition += offset;
+
+                        var startIndex = currentRun.Text.Start + offset;
+
+                        double startOffset;
+                        double endOffset;
+
+                        if (currentShapedRun.ShapedBuffer.IsLeftToRight)
+                        {
+                            startOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit(new CharacterHit(startIndex));
+
+                            endOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit(new CharacterHit(startIndex + remainingLength));
+                        }
+                        else
+                        {
+                            endOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit(new CharacterHit(startIndex));
+
+                            if (currentPosition < startIndex)
+                            {
+                                startOffset = endOffset;
+                            }
+                            else
+                            {
+                                startOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit(new CharacterHit(startIndex + remainingLength));
+                            }
+                        }
+
+                        startX += startOffset;
+
+                        endX += endOffset;
+
+                        var endHit = currentShapedRun.GlyphRun.GetCharacterHitFromDistance(endOffset, out _);
+                        var startHit = currentShapedRun.GlyphRun.GetCharacterHitFromDistance(startOffset, out _);
+
+                        characterLength = Math.Abs(endHit.FirstCharacterIndex + endHit.TrailingLength - startHit.FirstCharacterIndex - startHit.TrailingLength);
+
+                        currentDirection = FlowDirection.LeftToRight;
                     }
-                }
+                    else
+                    {
+                        if (currentPosition + currentRun.TextSourceLength <= firstTextSourceIndex)
+                        {
+                            startX += currentRun.Size.Width;
 
-                if (endX < startX)
-                {
-                    (endX, startX) = (startX, endX);
-                }
+                            currentPosition += currentRun.TextSourceLength;
 
-                //Lines that only contain a linebreak need to be covered here
-                if(characterLength == 0)
-                {
-                    characterLength = NewLineLength;
-                }
+                            continue;
+                        }
 
-                var runwidth = endX - startX;
-                var currentRunBounds = new TextRunBounds(new Rect(startX, 0, runwidth, Height), currentPosition, characterLength, currentRun);
+                        if (currentPosition < firstTextSourceIndex)
+                        {
+                            startX += currentRun.Size.Width;
+                        }
 
-                if (lastDirection == currentDirection && result.Count > 0 && MathUtilities.AreClose(currentRect.Right, startX))
-                {
-                    currentRect = currentRect.WithWidth(currentWidth + runwidth);
+                        if (currentPosition + currentRun.TextSourceLength <= characterIndex)
+                        {
+                            endX += currentRun.Size.Width;
 
-                    var textBounds = result[result.Count - 1];
+                            characterLength = currentRun.TextSourceLength;
+                        }
+                    }
 
-                    textBounds.Rectangle = currentRect;
+                    if (endX < startX)
+                    {
+                        (endX, startX) = (startX, endX);
+                    }
 
-                    textBounds.TextRunBounds.Add(currentRunBounds);
-                }
-                else
-                {
-                    currentRect = currentRunBounds.Rectangle;
+                    //Lines that only contain a linebreak need to be covered here
+                    if (characterLength == 0)
+                    {
+                        characterLength = NewLineLength;
+                    }
 
-                    result.Add(new TextBounds(currentRect, currentDirection, new List<TextRunBounds> { currentRunBounds }));
-                }
+                    runWidth = endX - startX;
+                    currentRunBounds = new TextRunBounds(new Rect(startX, 0, runWidth, Height), currentPosition, characterLength, currentRun);
 
-                currentWidth += runwidth;
-                currentPosition += characterLength;
+                    currentPosition += characterLength;
+
+                    remainingLength -= characterLength;
+                }                 
 
-                if (currentDirection == FlowDirection.LeftToRight)
+                if (currentRunBounds != null && !MathUtilities.IsZero(runWidth) || NewLineLength > 0)
                 {
-                    if (currentPosition > characterIndex)
+                    if (lastDirection == currentDirection && result.Count > 0 && MathUtilities.AreClose(currentRect.Right, startX))
                     {
-                        break;
+                        currentRect = currentRect.WithWidth(currentWidth + runWidth);
+
+                        var textBounds = result[result.Count - 1];
+
+                        textBounds.Rectangle = currentRect;
+
+                        textBounds.TextRunBounds.Add(currentRunBounds!);
                     }
-                }
-                else
-                {
-                    if (currentPosition <= firstTextSourceIndex)
+                    else
                     {
-                        break;
+                        currentRect = currentRunBounds!.Rectangle;
+
+                        result.Add(new TextBounds(currentRect, currentDirection, new List<TextRunBounds> { currentRunBounds }));
                     }
                 }
 
-                startX = endX;
-                lastDirection = currentDirection;
-                remainingLength -= characterLength;
+                currentWidth += runWidth;
+              
 
-                if (remainingLength <= 0)
+                if (remainingLength <= 0 || currentPosition >= characterIndex)
                 {
                     break;
                 }
+
+                startX = endX;
+                lastDirection = currentDirection;
             }
 
             return result;
@@ -571,7 +719,7 @@ namespace Avalonia.Media.TextFormatting
             var currentPosition = FirstTextSourceIndex;
             var remainingLength = textLength;
 
-            var startX = Start + WidthIncludingTrailingWhitespace;
+            var startX = WidthIncludingTrailingWhitespace;
             double currentWidth = 0;
             var currentRect = Rect.Empty;
 
@@ -582,7 +730,7 @@ namespace Avalonia.Media.TextFormatting
                     continue;
                 }
 
-                if (currentPosition + currentRun.TextSourceLength <= firstTextSourceIndex)
+                if (currentPosition + currentRun.TextSourceLength < firstTextSourceIndex)
                 {
                     startX -= currentRun.Size.Width;
 
@@ -601,20 +749,31 @@ namespace Avalonia.Media.TextFormatting
                     currentPosition += offset;
 
                     var startIndex = currentRun.Text.Start + offset;
+                    double startOffset;
+                    double endOffset;
 
-                    var endOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit(
-                        currentShapedRun.ShapedBuffer.IsLeftToRight ?
-                            new CharacterHit(startIndex + remainingLength) :
-                            new CharacterHit(startIndex));
+                    if (currentShapedRun.ShapedBuffer.IsLeftToRight)
+                    {
+                        if (currentPosition < startIndex)
+                        {
+                            startOffset = endOffset = 0;
+                        }
+                        else
+                        {
+                            endOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit(new CharacterHit(startIndex + remainingLength));
 
-                    endX += endOffset - currentShapedRun.Size.Width;
+                            startOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit(new CharacterHit(startIndex));
+                        }
+                    }
+                    else
+                    {
+                        endOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit(new CharacterHit(startIndex));
 
-                    var startOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit(
-                        currentShapedRun.ShapedBuffer.IsLeftToRight ?
-                            new CharacterHit(startIndex) :
-                            new CharacterHit(startIndex + remainingLength));
+                        startOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit(new CharacterHit(startIndex + remainingLength));
+                    }
 
-                    startX += startOffset - currentShapedRun.Size.Width;
+                    startX -= currentRun.Size.Width - startOffset;
+                    endX -= currentRun.Size.Width - endOffset;
 
                     var endHit = currentShapedRun.GlyphRun.GetCharacterHitFromDistance(endOffset, out _);
                     var startHit = currentShapedRun.GlyphRun.GetCharacterHitFromDistance(startOffset, out _);
@@ -652,53 +811,150 @@ namespace Avalonia.Media.TextFormatting
                 }
 
                 var runWidth = endX - startX;
-                var currentRunBounds = new TextRunBounds(new Rect(startX, 0, runWidth, Height), currentPosition, characterLength, currentRun);
 
-                if (lastDirection == currentDirection && result.Count > 0 && MathUtilities.AreClose(currentRect.Right, startX))
+                var currentRunBounds = new TextRunBounds(new Rect(Start + startX, 0, runWidth, Height), currentPosition, characterLength, currentRun);
+
+                if (!MathUtilities.IsZero(runWidth) || NewLineLength > 0)
                 {
-                    currentRect = currentRect.WithWidth(currentWidth + runWidth);
+                    if (lastDirection == currentDirection && result.Count > 0 && MathUtilities.AreClose(currentRect.Right, Start + startX))
+                    {
+                        currentRect = currentRect.WithWidth(currentWidth + runWidth);
 
-                    var textBounds = result[result.Count - 1];
+                        var textBounds = result[result.Count - 1];
 
-                    textBounds.Rectangle = currentRect;
+                        textBounds.Rectangle = currentRect;
 
-                    textBounds.TextRunBounds.Add(currentRunBounds);
-                }
-                else
-                {
-                    currentRect = currentRunBounds.Rectangle;
+                        textBounds.TextRunBounds.Add(currentRunBounds);
+                    }
+                    else
+                    {
+                        currentRect = currentRunBounds.Rectangle;
 
-                    result.Add(new TextBounds(currentRect, currentDirection, new List<TextRunBounds> { currentRunBounds }));
+                        result.Add(new TextBounds(currentRect, currentDirection, new List<TextRunBounds> { currentRunBounds }));
+                    }
                 }
 
                 currentWidth += runWidth;
                 currentPosition += characterLength;
 
-                if (currentDirection == FlowDirection.LeftToRight)
+                if (currentPosition > characterIndex)
+                {
+                    break;
+                }
+
+                lastDirection = currentDirection;
+                remainingLength -= characterLength;
+
+                if (remainingLength <= 0)
+                {
+                    break;
+                }
+            }
+
+            result.Reverse();
+
+            return result;
+        }
+
+        private bool TryGetTextRunBoundsRightToLeft(double startX, int firstTextSourceIndex, int characterIndex, int runIndex, ref int currentPosition, ref int remainingLength, out TextRunBounds? textRunBounds)
+        {
+            textRunBounds = null;
+
+            for (var index = runIndex; index >= 0; index--)
+            {
+                if (TextRuns[index] is not DrawableTextRun currentRun)
+                {
+                    continue;
+                }
+
+                if (currentPosition + currentRun.TextSourceLength <= firstTextSourceIndex)
                 {
-                    if (currentPosition > characterIndex)
+                    startX -= currentRun.Size.Width;
+
+                    currentPosition += currentRun.TextSourceLength;
+
+                    continue;
+                }
+
+                var characterLength = 0;
+                var endX = startX;
+
+                if (currentRun is ShapedTextCharacters currentShapedRun)
+                {
+                    var offset = Math.Max(0, firstTextSourceIndex - currentPosition);
+
+                    currentPosition += offset;
+
+                    var startIndex = currentRun.Text.Start + offset;
+                    double startOffset;
+                    double endOffset;
+
+                    if (currentShapedRun.ShapedBuffer.IsLeftToRight)
                     {
-                        break;
+                        if (currentPosition < startIndex)
+                        {
+                            startOffset = endOffset = 0;
+                        }
+                        else
+                        {
+                            endOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit(new CharacterHit(startIndex + remainingLength));
+
+                            startOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit(new CharacterHit(startIndex));
+                        }
                     }
+                    else
+                    {
+                        endOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit(new CharacterHit(startIndex));
+
+                        startOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit(new CharacterHit(startIndex + remainingLength));
+                    }
+
+                    startX -= currentRun.Size.Width - startOffset;
+                    endX -= currentRun.Size.Width - endOffset;
+
+                    var endHit = currentShapedRun.GlyphRun.GetCharacterHitFromDistance(endOffset, out _);
+                    var startHit = currentShapedRun.GlyphRun.GetCharacterHitFromDistance(startOffset, out _);
+
+                    characterLength = Math.Abs(startHit.FirstCharacterIndex + startHit.TrailingLength - endHit.FirstCharacterIndex - endHit.TrailingLength);
                 }
                 else
                 {
-                    if (currentPosition <= firstTextSourceIndex)
+                    if (currentPosition + currentRun.TextSourceLength <= characterIndex)
                     {
-                        break;
+                        endX -= currentRun.Size.Width;
+                    }
+
+                    if (currentPosition < firstTextSourceIndex)
+                    {
+                        startX -= currentRun.Size.Width;
+
+                        characterLength = currentRun.TextSourceLength;
                     }
                 }
 
-                lastDirection = currentDirection;
-                remainingLength -= characterLength;
+                if (endX < startX)
+                {
+                    (endX, startX) = (startX, endX);
+                }
 
-                if (remainingLength <= 0)
+                //Lines that only contain a linebreak need to be covered here
+                if (characterLength == 0)
                 {
-                    break;
+                    characterLength = NewLineLength;
                 }
+
+                var runWidth = endX - startX;
+
+                remainingLength -= characterLength;
+
+                currentPosition += characterLength;
+
+                textRunBounds = new TextRunBounds(new Rect(Start + startX, 0, runWidth, Height), currentPosition, characterLength, currentRun);
+
+                return true;
             }
 
-            return result;
+            return false;
         }
 
         public override IReadOnlyList<TextBounds> GetTextBounds(int firstTextSourceIndex, int textLength)
@@ -1280,6 +1536,11 @@ namespace Avalonia.Media.TextFormatting
             var textAlignment = _paragraphProperties.TextAlignment;
             var paragraphFlowDirection = _paragraphProperties.FlowDirection;
 
+            if(textAlignment == TextAlignment.Justify)
+            {
+                textAlignment = TextAlignment.Start;
+            }
+
             switch (textAlignment)
             {
                 case TextAlignment.Start:
@@ -1302,8 +1563,14 @@ namespace Avalonia.Media.TextFormatting
             switch (textAlignment)
             {
                 case TextAlignment.Center:
-                    return Math.Max(0, (_paragraphWidth - width) / 2);
+                    var start = (_paragraphWidth - width) / 2;
+
+                    if (paragraphFlowDirection == FlowDirection.RightToLeft)
+                    {
+                        start -= (widthIncludingTrailingWhitespace - width);
+                    }
 
+                    return Math.Max(0, start);
                 case TextAlignment.Right:
                     return Math.Max(0, _paragraphWidth - widthIncludingTrailingWhitespace);
 

+ 1 - 1
src/Avalonia.Base/Media/TextFormatting/Unicode/Codepoint.cs

@@ -224,7 +224,7 @@ namespace Avalonia.Media.TextFormatting.Unicode
         }
 
         /// <summary>
-        /// Returns <see langword="true"/> if <paramref name="value"/> is between
+        /// Returns <see langword="true"/> if <paramref name="cp"/> is between
         /// <paramref name="lowerBound"/> and <paramref name="upperBound"/>, inclusive.
         /// </summary>
         [MethodImpl(MethodImplOptions.AggressiveInlining)]

+ 2 - 2
src/Avalonia.Base/Platform/IGeometryImpl.cs

@@ -38,8 +38,8 @@ namespace Avalonia.Platform
         /// Intersects the geometry with another geometry.
         /// </summary>
         /// <param name="geometry">The other geometry.</param>
-        /// <returns>A new <see cref="IGeometryImpl"/> representing the intersection.</returns>
-        IGeometryImpl Intersect(IGeometryImpl geometry);
+        /// <returns>A new <see cref="IGeometryImpl"/> representing the intersection or <c>null</c> when the operation failed.</returns>
+        IGeometryImpl? Intersect(IGeometryImpl geometry);
 
         /// <summary>
         /// Indicates whether the geometry's stroke contains the specified point.

+ 4 - 4
src/Avalonia.Base/Platform/Storage/FileIO/BclStorageFile.cs

@@ -47,22 +47,22 @@ public class BclStorageFile : IStorageBookmarkFile
         return Task.FromResult<IStorageFolder?>(null);
     }
 
-    public Task<Stream> OpenRead()
+    public Task<Stream> OpenReadAsync()
     {
         return Task.FromResult<Stream>(_fileInfo.OpenRead());
     }
 
-    public Task<Stream> OpenWrite()
+    public Task<Stream> OpenWriteAsync()
     {
         return Task.FromResult<Stream>(_fileInfo.OpenWrite());
     }
 
-    public virtual Task<string?> SaveBookmark()
+    public virtual Task<string?> SaveBookmarkAsync()
     {
         return Task.FromResult<string?>(_fileInfo.FullName);
     }
 
-    public Task ReleaseBookmark()
+    public Task ReleaseBookmarkAsync()
     {
         // No-op
         return Task.CompletedTask;

+ 14 - 2
src/Avalonia.Base/Platform/Storage/FileIO/BclStorageFolder.cs

@@ -1,6 +1,8 @@
 using System;
+using System.Collections.Generic;
 using System.Diagnostics.CodeAnalysis;
 using System.IO;
+using System.Linq;
 using System.Security;
 using System.Threading.Tasks;
 using Avalonia.Metadata;
@@ -43,12 +45,22 @@ public class BclStorageFolder : IStorageBookmarkFolder
         return Task.FromResult<IStorageFolder?>(null);
     }
 
-    public virtual Task<string?> SaveBookmark()
+    public Task<IReadOnlyList<IStorageItem>> GetItemsAsync()
+    {
+         var items = _directoryInfo.GetDirectories()
+            .Select(d => (IStorageItem)new BclStorageFolder(d))
+            .Concat(_directoryInfo.GetFiles().Select(f => new BclStorageFile(f)))
+            .ToArray();
+
+         return Task.FromResult<IReadOnlyList<IStorageItem>>(items);
+    }
+
+    public virtual Task<string?> SaveBookmarkAsync()
     {
         return Task.FromResult<string?>(_directoryInfo.FullName);
     }
     
-    public Task ReleaseBookmark()
+    public Task ReleaseBookmarkAsync()
     {
         // No-op
         return Task.CompletedTask;

+ 1 - 1
src/Avalonia.Base/Platform/Storage/IStorageBookmarkItem.cs

@@ -6,7 +6,7 @@ namespace Avalonia.Platform.Storage;
 [NotClientImplementable]
 public interface IStorageBookmarkItem : IStorageItem
 {
-    Task ReleaseBookmark();
+    Task ReleaseBookmarkAsync();
 }
 
 [NotClientImplementable]

+ 2 - 2
src/Avalonia.Base/Platform/Storage/IStorageFile.cs

@@ -18,7 +18,7 @@ public interface IStorageFile : IStorageItem
     /// <summary>
     /// Opens a stream for read access.
     /// </summary>
-    Task<Stream> OpenRead();
+    Task<Stream> OpenReadAsync();
 
     /// <summary>
     /// Returns true, if file is writeable. 
@@ -28,5 +28,5 @@ public interface IStorageFile : IStorageItem
     /// <summary>
     /// Opens stream for writing to the file.
     /// </summary>
-    Task<Stream> OpenWrite();
+    Task<Stream> OpenWriteAsync();
 }

+ 10 - 1
src/Avalonia.Base/Platform/Storage/IStorageFolder.cs

@@ -1,4 +1,6 @@
-using Avalonia.Metadata;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Avalonia.Metadata;
 
 namespace Avalonia.Platform.Storage;
 
@@ -8,4 +10,11 @@ namespace Avalonia.Platform.Storage;
 [NotClientImplementable]
 public interface IStorageFolder : IStorageItem
 {
+    /// <summary>
+    /// Gets the files and subfolders in the current folder.
+    /// </summary>
+    /// <returns>
+    /// When this method completes successfully, it returns a list of the files and folders in the current folder. Each item in the list is represented by an <see cref="IStorageItem"/> implementation object.
+    /// </returns>
+    Task<IReadOnlyList<IStorageItem>> GetItemsAsync();
 }

+ 1 - 1
src/Avalonia.Base/Platform/Storage/IStorageItem.cs

@@ -44,7 +44,7 @@ public interface IStorageItem : IDisposable
     /// <returns>
     /// Returns identifier of a bookmark. Can be null if OS denied request.
     /// </returns>
-    Task<string?> SaveBookmark();
+    Task<string?> SaveBookmarkAsync();
 
     /// <summary>
     /// Gets the parent folder of the current storage item.

+ 3 - 2
src/Avalonia.Base/Rendering/Composition/Animations/AnimationInstanceBase.cs

@@ -3,8 +3,9 @@ using System.Collections.Generic;
 using Avalonia.Rendering.Composition.Expressions;
 using Avalonia.Rendering.Composition.Server;
 
-namespace Avalonia.Rendering.Composition.Animations;
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
 
+namespace Avalonia.Rendering.Composition.Animations;
 
 /// <summary>
 /// The base class for both key-frame and expression animation instances
@@ -79,4 +80,4 @@ internal abstract class AnimationInstanceBase : IAnimationInstance
         _invalidated = true;
         TargetObject.NotifyAnimatedValueChanged(Property);
     }
-}
+}

+ 5 - 6
src/Avalonia.Base/Rendering/Composition/Animations/CompositionAnimation.cs

@@ -1,12 +1,11 @@
 // ReSharper disable InconsistentNaming
 // ReSharper disable CheckNamespace
 
-using System;
-using System.Collections.Generic;
 using System.Numerics;
 using Avalonia.Rendering.Composition.Expressions;
 using Avalonia.Rendering.Composition.Server;
-using Avalonia.Rendering.Composition.Transport;
+
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
 
 namespace Avalonia.Rendering.Composition.Animations
 {
@@ -14,10 +13,10 @@ namespace Avalonia.Rendering.Composition.Animations
     /// This is the base class for ExpressionAnimation and KeyFrameAnimation.
     /// </summary>
     /// <remarks>
-    /// Use the <see cref="CompositionObject.StartAnimation"/> method to start the animation.
+    /// Use the <see cref="CompositionObject.StartAnimation(string , CompositionAnimation)"/> method to start the animation.
     /// Value parameters (as opposed to reference parameters which are set using <see cref="SetReferenceParameter"/>)
     /// are copied and "embedded" into an expression at the time CompositionObject.StartAnimation is called.
-    /// Changing the value of the variable after <see cref="CompositionObject.StartAnimation"/> is called will not affect
+    /// Changing the value of the variable after <see cref="CompositionObject.StartAnimation(string , CompositionAnimation)"/> is called will not affect
     /// the value of the ExpressionAnimation.
     /// See the remarks section of ExpressionAnimation for additional information.
     /// </remarks>
@@ -72,4 +71,4 @@ namespace Avalonia.Rendering.Composition.Animations
             
         }
     }
-}
+}

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/Animations/CompositionAnimationGroup.cs

@@ -3,6 +3,8 @@ using System.Collections.Generic;
 using Avalonia.Rendering.Composition.Transport;
 
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition.Animations
 {
     public class CompositionAnimationGroup : CompositionObject, ICompositionAnimationBase
@@ -21,4 +23,4 @@ namespace Avalonia.Rendering.Composition.Animations
         {
         }
     }
-}
+}

+ 4 - 2
src/Avalonia.Base/Rendering/Composition/Animations/ExpressionAnimation.cs

@@ -3,6 +3,8 @@ using System;
 using Avalonia.Rendering.Composition.Expressions;
 using Avalonia.Rendering.Composition.Server;
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition.Animations
 {
     /// <summary>
@@ -14,7 +16,7 @@ namespace Avalonia.Rendering.Composition.Animations
     /// This contrasts <see cref="KeyFrameAnimation"/>s, which use an interpolator to define how the animating
     /// property changes over time. The mathematical equation can be defined using references to properties
     /// of Composition objects, mathematical functions and operators and Input.
-    /// Use the <see cref="CompositionObject.StartAnimation"/> method to start the animation.
+    /// Use the <see cref="CompositionObject.StartAnimation(string , CompositionAnimation)"/> method to start the animation.
     /// </remarks>
     public class ExpressionAnimation : CompositionAnimation
     {
@@ -50,4 +52,4 @@ namespace Avalonia.Rendering.Composition.Animations
             => new ExpressionAnimationInstance(ParsedExpression,
                 targetObject, finalValue, CreateSnapshot());
     }
-}
+}

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/Animations/ExpressionAnimationInstance.cs

@@ -3,6 +3,8 @@ using System.Collections.Generic;
 using Avalonia.Rendering.Composition.Expressions;
 using Avalonia.Rendering.Composition.Server;
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition.Animations
 {
     
@@ -46,4 +48,4 @@ namespace Avalonia.Rendering.Composition.Animations
             _finalValue = finalValue;
         }
     }
-}
+}

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/Animations/IAnimationInstance.cs

@@ -2,6 +2,8 @@ using System;
 using Avalonia.Rendering.Composition.Expressions;
 using Avalonia.Rendering.Composition.Server;
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition.Animations
 {
     internal interface IAnimationInstance
@@ -13,4 +15,4 @@ namespace Avalonia.Rendering.Composition.Animations
         void Deactivate();
         void Invalidate();
     }
-}
+}

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/Animations/ICompositionAnimationBase.cs

@@ -2,6 +2,8 @@
 
 using Avalonia.Rendering.Composition.Server;
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition.Animations
 {
     /// <summary>
@@ -12,4 +14,4 @@ namespace Avalonia.Rendering.Composition.Animations
         internal void InternalOnly();
     }
 
-}
+}

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/Animations/ImplicitAnimationCollection.cs

@@ -4,6 +4,8 @@ using System.Collections.Generic;
 using System.Diagnostics.CodeAnalysis;
 using Avalonia.Rendering.Composition.Transport;
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition.Animations
 {
     /// <summary>
@@ -79,4 +81,4 @@ namespace Avalonia.Rendering.Composition.Animations
             return rv;
         }
     }
-}
+}

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/Animations/Interpolators.cs

@@ -1,6 +1,8 @@
 using System;
 using System.Numerics;
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition.Animations
 {
     /// <summary>
@@ -73,4 +75,4 @@ namespace Avalonia.Rendering.Composition.Animations
         
         public static BooleanInterpolator Instance { get; } = new BooleanInterpolator();
     }
-}
+}

+ 5 - 3
src/Avalonia.Base/Rendering/Composition/Animations/KeyFrameAnimation.cs

@@ -2,6 +2,8 @@ using System;
 using Avalonia.Animation;
 using Avalonia.Animation.Easings;
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition.Animations
 {
     
@@ -22,9 +24,9 @@ namespace Avalonia.Rendering.Composition.Animations
         /// The delay behavior of the key frame animation.
         /// </summary>
         public AnimationDelayBehavior DelayBehavior { get; set; }
-        
+
         /// <summary>
-        /// Delay before the animation starts after <see cref="CompositionObject.StartAnimation"/> is called.
+        /// Delay before the animation starts after <see cref="CompositionObject.StartAnimation(string , CompositionAnimation)"/> is called.
         /// </summary>
         public System.TimeSpan DelayTime { get; set; }
         
@@ -131,4 +133,4 @@ namespace Avalonia.Rendering.Composition.Animations
         /// </summary>
         SetToFinalValue
     }
-}
+}

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/Animations/KeyFrameAnimationInstance.cs

@@ -4,6 +4,8 @@ using Avalonia.Animation;
 using Avalonia.Rendering.Composition.Expressions;
 using Avalonia.Rendering.Composition.Server;
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition.Animations
 {
     /// <summary>
@@ -175,4 +177,4 @@ namespace Avalonia.Rendering.Composition.Animations
             base.Deactivate();
         }
     }
-}
+}

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/Animations/KeyFrames.cs

@@ -3,6 +3,8 @@ using System.Collections.Generic;
 using Avalonia.Animation.Easings;
 using Avalonia.Rendering.Composition.Expressions;
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition.Animations
 {
     
@@ -86,4 +88,4 @@ namespace Avalonia.Rendering.Composition.Animations
     {
         public void InsertExpressionKeyFrame(float normalizedProgressKey, string value, IEasing easingFunction);
     }
-}
+}

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/Animations/PropertySetSnapshot.cs

@@ -1,6 +1,8 @@
 using System.Collections.Generic;
 using Avalonia.Rendering.Composition.Expressions;
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition.Animations
 {
     /// <summary>
@@ -46,4 +48,4 @@ namespace Avalonia.Rendering.Composition.Animations
 
         public ExpressionVariant GetProperty(string name) => GetParameter(name);
     }
-}
+}

+ 2 - 0
src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs

@@ -11,6 +11,8 @@ using Avalonia.Rendering.Composition.Server;
 using Avalonia.Threading;
 using Avalonia.VisualTree;
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition;
 
 /// <summary>

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/CompositionDrawListVisual.cs

@@ -5,6 +5,8 @@ using Avalonia.Rendering.Composition.Server;
 using Avalonia.Rendering.Composition.Transport;
 using Avalonia.VisualTree;
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition;
 
 
@@ -72,4 +74,4 @@ internal class CompositionDrawListVisual : CompositionContainerVisual
                 return true;
         return false;
     }
-}
+}

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/CompositionObject.cs

@@ -5,6 +5,8 @@ using Avalonia.Rendering.Composition.Server;
 using Avalonia.Rendering.Composition.Transport;
 using Avalonia.Utilities;
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition
 {
     /// <summary>
@@ -138,4 +140,4 @@ namespace Avalonia.Rendering.Composition
                 writer.Write((byte)(IsDisposed ? 1 : 0));
         }
     }
-}
+}

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/CompositionPropertySet.cs

@@ -5,6 +5,8 @@ using Avalonia.Rendering.Composition.Animations;
 using Avalonia.Rendering.Composition.Expressions;
 using Avalonia.Rendering.Composition.Transport;
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition
 {
     /// <summary>
@@ -144,4 +146,4 @@ namespace Avalonia.Rendering.Composition
         TypeMismatch,
         NotFound
     }
-}
+}

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/CompositionTarget.cs

@@ -4,6 +4,8 @@ using System.Numerics;
 using Avalonia.Collections.Pooled;
 using Avalonia.VisualTree;
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition
 {
     /// <summary>
@@ -127,4 +129,4 @@ namespace Avalonia.Rendering.Composition
             Compositor.Server.Render();
         }
     }
-}
+}

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/Compositor.cs

@@ -12,6 +12,8 @@ using Avalonia.Rendering.Composition.Transport;
 using Avalonia.Threading;
 
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition
 {
     /// <summary>
@@ -138,4 +140,4 @@ namespace Avalonia.Rendering.Composition
             _invokeOnNextCommit.Add(action);
         }
     }
-}
+}

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/ContainerVisual.cs

@@ -1,5 +1,7 @@
 using Avalonia.Rendering.Composition.Server;
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition
 {
     /// <summary>
@@ -21,4 +23,4 @@ namespace Avalonia.Rendering.Composition
             base.OnRootChangedCore();
         }
     }
-}
+}

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/Drawing/CompositionDrawList.cs

@@ -4,6 +4,8 @@ using Avalonia.Rendering.Composition.Server;
 using Avalonia.Rendering.SceneGraph;
 using Avalonia.Utilities;
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition.Drawing;
 
 /// <summary>
@@ -99,4 +101,4 @@ internal class CompositionDrawListBuilder
         if (count < Count)
             _operations!.RemoveRange(count, _operations.Count - count);
     }
-}
+}

+ 4 - 1
src/Avalonia.Base/Rendering/Composition/Drawing/CompositionDrawingContext.cs

@@ -8,6 +8,9 @@ using Avalonia.Rendering.Composition.Drawing;
 using Avalonia.Rendering.SceneGraph;
 using Avalonia.Utilities;
 using Avalonia.VisualTree;
+
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition;
 
 /// <summary>
@@ -388,4 +391,4 @@ internal class CompositionDrawingContext : IDrawingContextImpl, IDrawingContextW
         }
         return null;
     }
-}
+}

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/ElementCompositionPreview.cs

@@ -1,3 +1,5 @@
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition;
 
 /// <summary>
@@ -11,4 +13,4 @@ public static class ElementComposition
     /// <param name="visual"></param>
     /// <returns></returns>
     public static CompositionVisual? GetElementVisual(Visual visual) => visual.CompositionVisual;
-}
+}

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/Enums.cs

@@ -1,5 +1,7 @@
 using System;
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition
 {
     public enum CompositionBlendMode
@@ -117,4 +119,4 @@ namespace Avalonia.Rendering.Composition
         Fill = 1,
         //TODO: Uniform, UniformToFill
     }
-}
+}

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/Expressions/BuiltInExpressionFfi.cs

@@ -4,6 +4,8 @@ using System.Numerics;
 using Avalonia.Rendering.Composition.Animations;
 using Avalonia.Utilities;
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition.Expressions
 {
     /// <summary>
@@ -234,4 +236,4 @@ namespace Avalonia.Rendering.Composition.Expressions
 
         public static BuiltInExpressionFfi Instance { get; } = new BuiltInExpressionFfi();
     }
-}
+}

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/Expressions/DelegateExpressionFfi.cs

@@ -5,6 +5,8 @@ using System.Linq;
 using System.Numerics;
 using Avalonia.Media;
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition.Expressions
 {
     /// <summary>
@@ -181,4 +183,4 @@ namespace Avalonia.Rendering.Composition.Expressions
             );
         }
     }
-}
+}

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/Expressions/Expression.cs

@@ -4,6 +4,8 @@ using System.Globalization;
 using System.Reflection;
 using Avalonia.Rendering.Composition.Server;
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition.Expressions
 {
     /// <summary>
@@ -374,4 +376,4 @@ namespace Avalonia.Rendering.Composition.Expressions
 
 
 
-}
+}

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionEvaluationContext.cs

@@ -1,6 +1,8 @@
 using System.Collections.Generic;
 using Avalonia.Rendering.Composition.Server;
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition.Expressions
 {
     internal struct ExpressionEvaluationContext
@@ -29,4 +31,4 @@ namespace Avalonia.Rendering.Composition.Expressions
     {
         bool Call(string name, IReadOnlyList<ExpressionVariant> arguments, out ExpressionVariant result);
     }
-}
+}

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionParseException.cs

@@ -1,5 +1,7 @@
 using System;
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition.Expressions
 {
     internal class ExpressionParseException : Exception
@@ -11,4 +13,4 @@ namespace Avalonia.Rendering.Composition.Expressions
             Position = position;
         }
     }
-}
+}

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionParser.cs

@@ -5,6 +5,8 @@ using System.Linq;
 
 // ReSharper disable StringLiteralTypo
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition.Expressions
 {
     internal class ExpressionParser
@@ -295,4 +297,4 @@ namespace Avalonia.Rendering.Composition.Expressions
             
         }
     }
-}
+}

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionTrackedValues.cs

@@ -2,6 +2,8 @@ using System.Collections;
 using System.Collections.Generic;
 using Avalonia.Rendering.Composition.Server;
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition.Expressions;
 
 internal class ExpressionTrackedObjects : IEnumerable<IExpressionObject>
@@ -54,4 +56,4 @@ internal class ExpressionTrackedObjects : IEnumerable<IExpressionObject>
             _stack.Push(obj);
         }
     }
-}
+}

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionVariant.cs

@@ -4,6 +4,8 @@ using System.Numerics;
 using System.Runtime.InteropServices;
 using Avalonia.Media;
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition.Expressions
 {
     internal enum VariantType
@@ -727,4 +729,4 @@ namespace Avalonia.Rendering.Composition.Expressions
         }
     }
 
-}
+}

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/Expressions/TokenParser.cs

@@ -1,6 +1,8 @@
 using System;
 using System.Globalization;
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition.Expressions
 {
     /// <summary>
@@ -256,4 +258,4 @@ namespace Avalonia.Rendering.Composition.Expressions
         public override string ToString() => _s.ToString();
 
     }
-}
+}

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/ICompositionTargetDebugEvents.cs

@@ -1,6 +1,8 @@
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition;
 
 internal interface ICompositionTargetDebugEvents
 {
     void RectInvalidated(Rect rc);
-}
+}

+ 7 - 0
src/Avalonia.Base/Rendering/Composition/License.md

@@ -0,0 +1,7 @@
+Please note: Any code in this directory is excluded from the normal MIT license.
+
+This code is owned and copyright to Avalonia OU.
+
+This code may be used free of charge by any application that consumes Avalonia binary packages as a direct or indirect dependency.
+
+Explicit permission is required for any other use outside of Avalonia applications.

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/MatrixUtils.cs

@@ -1,5 +1,7 @@
 using System.Numerics;
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition
 {
     static class MatrixUtils
@@ -63,4 +65,4 @@ namespace Avalonia.Rendering.Composition
                 matrix44.M42,
                 matrix44.M44);
     }
-}
+}

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/Server/CompositionProperty.cs

@@ -1,6 +1,8 @@
 using System.Collections.Generic;
 using System.Threading;
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition.Server;
 
 internal class CompositionProperty
@@ -12,4 +14,4 @@ internal class CompositionProperty
     {
         Id = Interlocked.Increment(ref s_NextId)
     };
-}
+}

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs

@@ -6,6 +6,8 @@ using Avalonia.Rendering.Composition.Drawing;
 using Avalonia.Rendering.SceneGraph;
 using Avalonia.Utilities;
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition.Server;
 
 /// <summary>
@@ -176,4 +178,4 @@ internal class CompositorDrawingContextProxy : IDrawingContextImpl, IDrawingCont
         if (_impl is IDrawingContextWithAcrylicLikeSupport acrylic) 
             acrylic.DrawRectangle(material, rect);
     }
-}
+}

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/Server/FpsCounter.cs

@@ -6,6 +6,8 @@ using Avalonia.Media.TextFormatting;
 using Avalonia.Platform;
 using Avalonia.Utilities;
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition.Server;
 
 /// <summary>
@@ -73,4 +75,4 @@ internal class FpsCounter
             offset += run.Size.Width;
         }
     }
-}
+}

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/Server/ReadbackIndices.cs

@@ -1,3 +1,5 @@
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition.Server
 {
     /// <summary>
@@ -43,4 +45,4 @@ namespace Avalonia.Rendering.Composition.Server
             }
         }
     }
-}
+}

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionContainerVisual.cs

@@ -1,6 +1,8 @@
 using System.Numerics;
 using Avalonia.Platform;
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition.Server
 {
     /// <summary>
@@ -41,4 +43,4 @@ namespace Avalonia.Rendering.Composition.Server
             Children = new ServerCompositionVisualCollection(Compositor);
         }
     }
-}
+}

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionDrawListVisual.cs

@@ -7,6 +7,8 @@ using Avalonia.Rendering.Composition.Transport;
 using Avalonia.Rendering.SceneGraph;
 using Avalonia.Utilities;
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition.Server;
 
 /// <summary>
@@ -72,4 +74,4 @@ internal class ServerCompositionDrawListVisual : ServerCompositionContainerVisua
         return UiVisual.GetType().ToString();
     }
 #endif
-}
+}

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionSurface.cs

@@ -1,3 +1,5 @@
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition.Server
 {
     internal abstract class ServerCompositionSurface : ServerObject
@@ -6,4 +8,4 @@ namespace Avalonia.Rendering.Composition.Server
         {
         }
     }
-}
+}

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs

@@ -9,6 +9,8 @@ using Avalonia.Platform;
 using Avalonia.Rendering.Composition.Transport;
 using Avalonia.Utilities;
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition.Server
 {
     /// <summary>
@@ -218,4 +220,4 @@ namespace Avalonia.Rendering.Composition.Server
 
         public void EnqueueAdornerUpdate(ServerCompositionVisual visual) => _adornerUpdateQueue.Enqueue(visual);
     }
-}
+}

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.DirtyProperties.cs

@@ -1,3 +1,5 @@
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition.Server;
 
 partial class ServerCompositionVisual
@@ -73,4 +75,4 @@ partial class ServerCompositionVisual
             || offset == s_IdOfSizeProperty)
             _clipSizeDirty = true;
     }
-}
+}

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs

@@ -6,6 +6,8 @@ using Avalonia.Rendering.Composition.Animations;
 using Avalonia.Rendering.Composition.Transport;
 using Avalonia.Utilities;
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition.Server
 {
     /// <summary>
@@ -234,4 +236,4 @@ namespace Avalonia.Rendering.Composition.Server
     }
 
 
-}
+}

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/Server/ServerCompositor.cs

@@ -6,6 +6,8 @@ using Avalonia.Rendering.Composition.Animations;
 using Avalonia.Rendering.Composition.Expressions;
 using Avalonia.Rendering.Composition.Transport;
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition.Server
 {
     /// <summary>
@@ -137,4 +139,4 @@ namespace Avalonia.Rendering.Composition.Server
         public void RemoveFromClock(IAnimationInstance animationInstance) =>
             _activeAnimations.Remove(animationInstance);
     }
-}
+}

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/Server/ServerList.cs

@@ -2,6 +2,8 @@ using System;
 using System.Collections.Generic;
 using Avalonia.Rendering.Composition.Transport;
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition.Server
 {
     /// <summary>
@@ -41,4 +43,4 @@ namespace Avalonia.Rendering.Composition.Server
         {
         }
     }
-}
+}

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/Server/ServerObject.cs

@@ -7,6 +7,8 @@ using Avalonia.Rendering.Composition.Expressions;
 using Avalonia.Rendering.Composition.Transport;
 using Avalonia.Utilities;
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition.Server
 {
     /// <summary>
@@ -177,4 +179,4 @@ namespace Avalonia.Rendering.Composition.Server
             ItselfLastChangedBy = batch.SequenceId;
         }
     }
-}
+}

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/Transport/Batch.cs

@@ -4,6 +4,8 @@ using System.Collections.Generic;
 using System.Threading;
 using System.Threading.Tasks;
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition.Transport
 {
     /// <summary>
@@ -36,4 +38,4 @@ namespace Avalonia.Rendering.Composition.Transport
 
         public Task Completed => _tcs.Task;
     }
-}
+}

+ 2 - 0
src/Avalonia.Base/Rendering/Composition/Transport/BatchStream.cs

@@ -5,6 +5,8 @@ using System.Runtime.CompilerServices;
 using Avalonia.Rendering.Composition.Animations;
 using Avalonia.Rendering.Composition.Server;
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition.Transport;
 
 /// <summary>

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/Transport/BatchStreamArrayPool.cs

@@ -5,6 +5,8 @@ using System.Runtime.InteropServices;
 using Avalonia.Platform;
 using Avalonia.Threading;
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition.Transport;
 
 /// <summary>
@@ -153,4 +155,4 @@ internal sealed class BatchStreamMemoryPool : BatchStreamPoolBase<IntPtr>
     protected override IntPtr CreateItem() => Marshal.AllocHGlobal(BufferSize);
 
     protected override void DestroyItem(IntPtr item) => Marshal.FreeHGlobal(item);
-}
+}

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/Transport/BatchStreamDebugMarker.cs

@@ -1,9 +1,11 @@
 using System;
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition.Transport;
 
 internal class BatchStreamDebugMarkers
 {
     public static object ObjectEndMarker = new object();
     public static Guid ObjectEndMagic = Guid.NewGuid();
-}
+}

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/Transport/ServerListProxyHelper.cs

@@ -2,6 +2,8 @@ using System.Collections;
 using System.Collections.Generic;
 using Avalonia.Rendering.Composition.Server;
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition.Transport
 {
     /// <summary>
@@ -95,4 +97,4 @@ namespace Avalonia.Rendering.Composition.Transport
             _changed = false;
         }
     }
-}
+}

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/Visual.cs

@@ -3,6 +3,8 @@ using System.Numerics;
 using Avalonia.Media;
 using Avalonia.VisualTree;
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition
 {
     /// <summary>
@@ -53,4 +55,4 @@ namespace Avalonia.Rendering.Composition
 
         internal virtual bool HitTest(Point point, Func<IVisual, bool>? filter) => true;
     }
-}
+}

+ 3 - 1
src/Avalonia.Base/Rendering/Composition/VisualCollection.cs

@@ -1,6 +1,8 @@
 using System;
 using Avalonia.Rendering.Composition.Server;
 
+// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
+
 namespace Avalonia.Rendering.Composition
 {
     /// <summary>
@@ -70,4 +72,4 @@ namespace Avalonia.Rendering.Composition
             item.Parent = item;
         }
     }
-}
+}

+ 3 - 0
src/Avalonia.Base/Rendering/Composition/readme.md

@@ -0,0 +1,3 @@
+Please note the composition renderer is not subject to the usual MIT license.
+
+Please contact us for more details and see [License.md](https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md)

+ 1 - 1
src/Avalonia.Base/Rendering/SceneGraph/ExperimentalAcrylicNode.cs

@@ -83,7 +83,7 @@ namespace Avalonia.Rendering.SceneGraph
                 if (Material != null)
                 {
                     var rect = Rect.Rect;
-                    return rect.Contains(p);
+                    return rect.ContainsExclusive(p);
                 }
             }
 

+ 1 - 3
src/Avalonia.Base/Rendering/SceneGraph/GeometryNode.cs

@@ -1,9 +1,7 @@
 using System;
-using System.Collections.Generic;
 using Avalonia.Media;
 using Avalonia.Media.Immutable;
 using Avalonia.Platform;
-using Avalonia.VisualTree;
 
 namespace Avalonia.Rendering.SceneGraph
 {
@@ -19,7 +17,7 @@ namespace Avalonia.Rendering.SceneGraph
         /// <param name="brush">The fill brush.</param>
         /// <param name="pen">The stroke pen.</param>
         /// <param name="geometry">The geometry.</param>
-        /// <param name="childScenes">Child scenes for drawing visual brushes.</param>
+        /// <param name="aux">Auxiliary data required to draw the brush.</param>
         public GeometryNode(Matrix transform,
             IBrush? brush,
             IPen? pen,

+ 2 - 6
src/Avalonia.Base/Rendering/SceneGraph/GlyphRunNode.cs

@@ -1,10 +1,6 @@
 using System;
-using System.Collections.Generic;
-
 using Avalonia.Media;
-using Avalonia.Media.Immutable;
 using Avalonia.Platform;
-using Avalonia.VisualTree;
 
 namespace Avalonia.Rendering.SceneGraph
 {
@@ -19,7 +15,7 @@ namespace Avalonia.Rendering.SceneGraph
         /// <param name="transform">The transform.</param>
         /// <param name="foreground">The foreground brush.</param>
         /// <param name="glyphRun">The glyph run to draw.</param>
-        /// <param name="childScenes">Child scenes for drawing visual brushes.</param>
+        /// <param name="aux">Auxiliary data required to draw the brush.</param>
         public GlyphRunNode(
             Matrix transform,
             IBrush foreground,
@@ -73,6 +69,6 @@ namespace Avalonia.Rendering.SceneGraph
         }
 
         /// <inheritdoc/>
-        public override bool HitTest(Point p) => Bounds.Contains(p);
+        public override bool HitTest(Point p) => Bounds.ContainsExclusive(p);
     }
 }

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