Browse Source

Merge branch 'fix-button-flyout' of https://github.com/pr8x/Avalonia into fix-button-flyout

pull/8448/head
Luis von der Eltz 4 years ago
parent
commit
c436bed856
  1. 4
      samples/BindingDemo/App.xaml
  2. 2
      samples/BindingDemo/BindingDemo.csproj
  3. 1
      samples/ControlCatalog.Android/ControlCatalog.Android.csproj
  4. 8
      samples/ControlCatalog/App.xaml
  5. 1
      samples/ControlCatalog/Pages/ButtonsPage.xaml
  6. 9
      samples/ControlCatalog/Pages/DataGridPage.xaml
  7. 15
      samples/ControlCatalog/Pages/ExpanderPage.xaml
  8. 2
      samples/ControlCatalog/Pages/ExpanderPage.xaml.cs
  9. 44
      samples/ControlCatalog/Pages/OpenGlPage.xaml.cs
  10. 2
      samples/ControlCatalog/Pages/TextBlockPage.xaml
  11. 27
      samples/ControlCatalog/ViewModels/ExpanderPageViewModel.cs
  12. 8
      samples/RenderDemo/App.xaml
  13. 286
      samples/SampleControls/HamburgerMenu/HamburgerMenu.xaml
  14. 5
      src/Avalonia.Base/Input/Cursor.cs
  15. 18
      src/Avalonia.Base/Input/DragEventArgs.cs
  16. 11
      src/Avalonia.Base/Input/GotFocusEventArgs.cs
  17. 5
      src/Avalonia.Base/Input/IInputRoot.cs
  18. 13
      src/Avalonia.Base/Input/IKeyboardDevice.cs
  19. 12
      src/Avalonia.Base/Input/IMouseDevice.cs
  20. 14
      src/Avalonia.Base/Input/IPointerDevice.cs
  21. 2
      src/Avalonia.Base/Input/KeyEventArgs.cs
  22. 12
      src/Avalonia.Base/Input/KeyGesture.cs
  23. 54
      src/Avalonia.Base/Input/MouseDevice.cs
  24. 19
      src/Avalonia.Base/Input/PenDevice.cs
  25. 62
      src/Avalonia.Base/Input/PointerEventArgs.cs
  26. 2
      src/Avalonia.Base/Input/PointerOverPreProcessor.cs
  27. 5
      src/Avalonia.Base/Input/Raw/RawDragEvent.cs
  28. 3
      src/Avalonia.Base/Input/Raw/RawTouchEventArgs.cs
  29. 11
      src/Avalonia.Base/Input/TouchDevice.cs
  30. 109
      src/Avalonia.Base/Media/GlyphRun.cs
  31. 2
      src/Avalonia.Base/Media/TextDecoration.cs
  32. 51
      src/Avalonia.Base/Media/TextFormatting/TextLayout.cs
  33. 433
      src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs
  34. 4
      src/Avalonia.Base/Metadata/IAddChild.cs
  35. 4
      src/Avalonia.Base/Platform/IGeometryImpl.cs
  36. 5
      src/Avalonia.Base/Rendering/Composition/Animations/AnimationInstanceBase.cs
  37. 4
      src/Avalonia.Base/Rendering/Composition/Animations/CompositionAnimation.cs
  38. 4
      src/Avalonia.Base/Rendering/Composition/Animations/CompositionAnimationGroup.cs
  39. 4
      src/Avalonia.Base/Rendering/Composition/Animations/ExpressionAnimation.cs
  40. 4
      src/Avalonia.Base/Rendering/Composition/Animations/ExpressionAnimationInstance.cs
  41. 4
      src/Avalonia.Base/Rendering/Composition/Animations/IAnimationInstance.cs
  42. 4
      src/Avalonia.Base/Rendering/Composition/Animations/ICompositionAnimationBase.cs
  43. 4
      src/Avalonia.Base/Rendering/Composition/Animations/ImplicitAnimationCollection.cs
  44. 4
      src/Avalonia.Base/Rendering/Composition/Animations/Interpolators.cs
  45. 4
      src/Avalonia.Base/Rendering/Composition/Animations/KeyFrameAnimation.cs
  46. 4
      src/Avalonia.Base/Rendering/Composition/Animations/KeyFrameAnimationInstance.cs
  47. 4
      src/Avalonia.Base/Rendering/Composition/Animations/KeyFrames.cs
  48. 4
      src/Avalonia.Base/Rendering/Composition/Animations/PropertySetSnapshot.cs
  49. 2
      src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs
  50. 4
      src/Avalonia.Base/Rendering/Composition/CompositionDrawListVisual.cs
  51. 4
      src/Avalonia.Base/Rendering/Composition/CompositionObject.cs
  52. 4
      src/Avalonia.Base/Rendering/Composition/CompositionPropertySet.cs
  53. 4
      src/Avalonia.Base/Rendering/Composition/CompositionTarget.cs
  54. 4
      src/Avalonia.Base/Rendering/Composition/Compositor.cs
  55. 4
      src/Avalonia.Base/Rendering/Composition/ContainerVisual.cs
  56. 4
      src/Avalonia.Base/Rendering/Composition/Drawing/CompositionDrawList.cs
  57. 5
      src/Avalonia.Base/Rendering/Composition/Drawing/CompositionDrawingContext.cs
  58. 4
      src/Avalonia.Base/Rendering/Composition/ElementCompositionPreview.cs
  59. 4
      src/Avalonia.Base/Rendering/Composition/Enums.cs
  60. 4
      src/Avalonia.Base/Rendering/Composition/Expressions/BuiltInExpressionFfi.cs
  61. 4
      src/Avalonia.Base/Rendering/Composition/Expressions/DelegateExpressionFfi.cs
  62. 4
      src/Avalonia.Base/Rendering/Composition/Expressions/Expression.cs
  63. 4
      src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionEvaluationContext.cs
  64. 4
      src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionParseException.cs
  65. 4
      src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionParser.cs
  66. 4
      src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionTrackedValues.cs
  67. 4
      src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionVariant.cs
  68. 4
      src/Avalonia.Base/Rendering/Composition/Expressions/TokenParser.cs
  69. 4
      src/Avalonia.Base/Rendering/Composition/ICompositionTargetDebugEvents.cs
  70. 7
      src/Avalonia.Base/Rendering/Composition/License.md
  71. 4
      src/Avalonia.Base/Rendering/Composition/MatrixUtils.cs
  72. 4
      src/Avalonia.Base/Rendering/Composition/Server/CompositionProperty.cs
  73. 4
      src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs
  74. 4
      src/Avalonia.Base/Rendering/Composition/Server/FpsCounter.cs
  75. 4
      src/Avalonia.Base/Rendering/Composition/Server/ReadbackIndices.cs
  76. 4
      src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionContainerVisual.cs
  77. 4
      src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionDrawListVisual.cs
  78. 4
      src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionSurface.cs
  79. 4
      src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs
  80. 4
      src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.DirtyProperties.cs
  81. 4
      src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs
  82. 4
      src/Avalonia.Base/Rendering/Composition/Server/ServerCompositor.cs
  83. 4
      src/Avalonia.Base/Rendering/Composition/Server/ServerList.cs
  84. 4
      src/Avalonia.Base/Rendering/Composition/Server/ServerObject.cs
  85. 4
      src/Avalonia.Base/Rendering/Composition/Transport/Batch.cs
  86. 2
      src/Avalonia.Base/Rendering/Composition/Transport/BatchStream.cs
  87. 4
      src/Avalonia.Base/Rendering/Composition/Transport/BatchStreamArrayPool.cs
  88. 4
      src/Avalonia.Base/Rendering/Composition/Transport/BatchStreamDebugMarker.cs
  89. 4
      src/Avalonia.Base/Rendering/Composition/Transport/ServerListProxyHelper.cs
  90. 4
      src/Avalonia.Base/Rendering/Composition/Visual.cs
  91. 4
      src/Avalonia.Base/Rendering/Composition/VisualCollection.cs
  92. 3
      src/Avalonia.Base/Rendering/Composition/readme.md
  93. 85
      src/Avalonia.Base/StyledElement.cs
  94. 2
      src/Avalonia.Base/Styling/ChildSelector.cs
  95. 67
      src/Avalonia.Base/Styling/ControlTheme.cs
  96. 2
      src/Avalonia.Base/Styling/DescendentSelector.cs
  97. 2
      src/Avalonia.Base/Styling/IStyle.cs
  98. 7
      src/Avalonia.Base/Styling/IStyleable.cs
  99. 14
      src/Avalonia.Base/Styling/NestingSelector.cs
  100. 2
      src/Avalonia.Base/Styling/NotSelector.cs

4
samples/BindingDemo/App.xaml

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

2
samples/BindingDemo/BindingDemo.csproj

@ -5,7 +5,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" /> <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="..\..\src\Linux\Avalonia.LinuxFramebuffer\Avalonia.LinuxFramebuffer.csproj" />
<ProjectReference Include="..\MiniMvvm\MiniMvvm.csproj" /> <ProjectReference Include="..\MiniMvvm\MiniMvvm.csproj" />
</ItemGroup> </ItemGroup>

1
samples/ControlCatalog.Android/ControlCatalog.Android.csproj

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

8
samples/ControlCatalog/App.xaml

@ -5,6 +5,13 @@
x:CompileBindings="True" x:CompileBindings="True"
Name="Avalonia ControlCatalog" Name="Avalonia ControlCatalog"
x:Class="ControlCatalog.App"> x:Class="ControlCatalog.App">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceInclude Source="avares://ControlSamples/HamburgerMenu/HamburgerMenu.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
<Application.Styles> <Application.Styles>
<Style Selector="TextBlock.h1, TextBlock.h2, TextBlock.h3"> <Style Selector="TextBlock.h1, TextBlock.h2, TextBlock.h3">
<Setter Property="TextWrapping" Value="Wrap" /> <Setter Property="TextWrapping" Value="Wrap" />
@ -29,7 +36,6 @@
<Style Selector="Label.h3"> <Style Selector="Label.h3">
<Setter Property="FontSize" Value="12" /> <Setter Property="FontSize" Value="12" />
</Style> </Style>
<StyleInclude Source="avares://ControlSamples/HamburgerMenu/HamburgerMenu.xaml" />
</Application.Styles> </Application.Styles>
<TrayIcon.Icons> <TrayIcon.Icons>
<TrayIcons> <TrayIcons>

1
samples/ControlCatalog/Pages/ButtonsPage.xaml

@ -90,6 +90,7 @@
</Style> </Style>
</Button.Styles> </Button.Styles>
</Button> </Button>
<Button Classes="accent">Accent</Button>
</StackPanel> </StackPanel>
<StackPanel Orientation="Vertical" <StackPanel Orientation="Vertical"

9
samples/ControlCatalog/Pages/DataGridPage.xaml

@ -10,11 +10,11 @@
<TextBlock Text="{Binding}"/> <TextBlock Text="{Binding}"/>
</StackPanel> </StackPanel>
</DataTemplate> </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.Resources>
<UserControl.Styles> <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)"> <Style Selector="DataGridColumnHeader:nth-last-child(1)">
<Setter Property="FontWeight" Value="Bold" /> <Setter Property="FontWeight" Value="Bold" />
</Style> </Style>
@ -54,7 +54,8 @@
<DataGridTextColumn Header="Region" Binding="{CompiledBinding Region}" Width="4*" x:DataType="local:Country" /> <DataGridTextColumn Header="Region" Binding="{CompiledBinding Region}" Width="4*" x:DataType="local:Country" />
<DataGridTextColumn Header="Population" Binding="{Binding Population}" Width="3*" /> <DataGridTextColumn Header="Population" Binding="{Binding Population}" Width="3*" />
<DataGridTextColumn Header="Area" Binding="{Binding Area}" 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" MinWidth="200"
IsVisible="{Binding #ShowGDP.IsChecked}"/> IsVisible="{Binding #ShowGDP.IsChecked}"/>
</DataGrid.Columns> </DataGrid.Columns>

15
samples/ControlCatalog/Pages/ExpanderPage.xaml

@ -8,26 +8,31 @@
Margin="0,16,0,0" Margin="0,16,0,0"
HorizontalAlignment="Center" HorizontalAlignment="Center"
Spacing="16"> Spacing="16">
<Expander Header="Expand Up" ExpandDirection="Up"> <Expander Header="Expand Up" ExpandDirection="Up"
CornerRadius="{Binding CornerRadius}">
<StackPanel> <StackPanel>
<TextBlock>Expanded content</TextBlock> <TextBlock>Expanded content</TextBlock>
</StackPanel> </StackPanel>
</Expander> </Expander>
<Expander Header="Expand Down" ExpandDirection="Down"> <Expander Header="Expand Down" ExpandDirection="Down"
CornerRadius="{Binding CornerRadius}">
<StackPanel> <StackPanel>
<TextBlock>Expanded content</TextBlock> <TextBlock>Expanded content</TextBlock>
</StackPanel> </StackPanel>
</Expander> </Expander>
<Expander Header="Expand Left" ExpandDirection="Left"> <Expander Header="Expand Left" ExpandDirection="Left"
CornerRadius="{Binding CornerRadius}">
<StackPanel> <StackPanel>
<TextBlock>Expanded content</TextBlock> <TextBlock>Expanded content</TextBlock>
</StackPanel> </StackPanel>
</Expander> </Expander>
<Expander Header="Expand Right" ExpandDirection="Right"> <Expander Header="Expand Right" ExpandDirection="Right"
CornerRadius="{Binding CornerRadius}">
<StackPanel> <StackPanel>
<TextBlock>Expanded content</TextBlock> <TextBlock>Expanded content</TextBlock>
</StackPanel> </StackPanel>
</Expander> </Expander>
</StackPanel> <CheckBox IsChecked="{Binding Rounded}">Rounded</CheckBox>
</StackPanel>
</StackPanel> </StackPanel>
</UserControl> </UserControl>

2
samples/ControlCatalog/Pages/ExpanderPage.xaml.cs

@ -1,5 +1,6 @@
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using ControlCatalog.ViewModels;
namespace ControlCatalog.Pages namespace ControlCatalog.Pages
{ {
@ -8,6 +9,7 @@ namespace ControlCatalog.Pages
public ExpanderPage() public ExpanderPage()
{ {
this.InitializeComponent(); this.InitializeComponent();
DataContext = new ExpanderPageViewModel();
} }
private void InitializeComponent() private void InitializeComponent()

44
samples/ControlCatalog/Pages/OpenGlPage.xaml.cs

@ -90,7 +90,6 @@ namespace ControlCatalog.Pages
private int _vertexBufferObject; private int _vertexBufferObject;
private int _indexBufferObject; private int _indexBufferObject;
private int _vertexArrayObject; private int _vertexArrayObject;
private GlExtrasInterface _glExt;
private string GetShader(bool fragment, string shader) private string GetShader(bool fragment, string shader)
{ {
@ -258,7 +257,6 @@ namespace ControlCatalog.Pages
protected unsafe override void OnOpenGlInit(GlInterface GL, int fb) protected unsafe override void OnOpenGlInit(GlInterface GL, int fb)
{ {
CheckError(GL); CheckError(GL);
_glExt = new GlExtrasInterface(GL);
Info = $"Renderer: {GL.GetString(GL_RENDERER)} Version: {GL.GetString(GL_VERSION)}"; 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.BufferData(GL_ELEMENT_ARRAY_BUFFER, new IntPtr(_indices.Length * sizeof(ushort)), new IntPtr(pdata),
GL_STATIC_DRAW); GL_STATIC_DRAW);
CheckError(GL); CheckError(GL);
_vertexArrayObject = _glExt.GenVertexArray(); _vertexArrayObject = GL.GenVertexArray();
_glExt.BindVertexArray(_vertexArrayObject); GL.BindVertexArray(_vertexArrayObject);
CheckError(GL); CheckError(GL);
GL.VertexAttribPointer(positionLocation, 3, GL_FLOAT, GL.VertexAttribPointer(positionLocation, 3, GL_FLOAT,
0, vertexSize, IntPtr.Zero); 0, vertexSize, IntPtr.Zero);
@ -316,12 +314,13 @@ namespace ControlCatalog.Pages
// Unbind everything // Unbind everything
GL.BindBuffer(GL_ARRAY_BUFFER, 0); GL.BindBuffer(GL_ARRAY_BUFFER, 0);
GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
_glExt.BindVertexArray(0); GL.BindVertexArray(0);
GL.UseProgram(0); GL.UseProgram(0);
// Delete all resources. // Delete all resources.
GL.DeleteBuffers(2, new[] { _vertexBufferObject, _indexBufferObject }); GL.DeleteBuffer(_vertexBufferObject);
_glExt.DeleteVertexArrays(1, new[] { _vertexArrayObject }); GL.DeleteBuffer(_indexBufferObject);
GL.DeleteVertexArray(_vertexArrayObject);
GL.DeleteProgram(_shaderProgram); GL.DeleteProgram(_shaderProgram);
GL.DeleteShader(_fragmentShader); GL.DeleteShader(_fragmentShader);
GL.DeleteShader(_vertexShader); GL.DeleteShader(_vertexShader);
@ -338,7 +337,7 @@ namespace ControlCatalog.Pages
GL.BindBuffer(GL_ARRAY_BUFFER, _vertexBufferObject); GL.BindBuffer(GL_ARRAY_BUFFER, _vertexBufferObject);
GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBufferObject); GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBufferObject);
_glExt.BindVertexArray(_vertexArrayObject); GL.BindVertexArray(_vertexArrayObject);
GL.UseProgram(_shaderProgram); GL.UseProgram(_shaderProgram);
CheckError(GL); CheckError(GL);
var projection = var projection =
@ -369,34 +368,5 @@ namespace ControlCatalog.Pages
if (_disco > 0.01) if (_disco > 0.01)
Dispatcher.UIThread.Post(InvalidateVisual, DispatcherPriority.Background); 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];
}
}
} }
} }

2
samples/ControlCatalog/Pages/TextBlockPage.xaml

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

27
samples/ControlCatalog/ViewModels/ExpanderPageViewModel.cs

@ -0,0 +1,27 @@
using Avalonia;
using MiniMvvm;
namespace ControlCatalog.ViewModels
{
public class ExpanderPageViewModel : ViewModelBase
{
private object _cornerRadius = AvaloniaProperty.UnsetValue;
private bool _rounded;
public object CornerRadius
{
get => _cornerRadius;
private set => RaiseAndSetIfChanged(ref _cornerRadius, value);
}
public bool Rounded
{
get => _rounded;
set
{
if (RaiseAndSetIfChanged(ref _rounded, value))
CornerRadius = _rounded ? new CornerRadius(25) : AvaloniaProperty.UnsetValue;
}
}
}
}

8
samples/RenderDemo/App.xaml

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

286
samples/SampleControls/HamburgerMenu/HamburgerMenu.xaml

@ -1,6 +1,6 @@
<Styles xmlns="https://github.com/avaloniaui" <ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:catalog="using:ControlSamples"> xmlns:catalog="using:ControlSamples">
<Design.PreviewWith> <Design.PreviewWith>
<Border Width="400" <Border Width="400"
Height="150"> Height="150">
@ -20,25 +20,136 @@
</Border> </Border>
</Design.PreviewWith> </Design.PreviewWith>
<Styles.Resources> <x:Double x:Key="PaneCompactWidth">40</x:Double>
<x:Double x:Key="PaneCompactWidth">40</x:Double> <x:Double x:Key="PaneExpandWidth">220</x:Double>
<x:Double x:Key="PaneExpandWidth">220</x:Double> <x:Double x:Key="HeaderHeight">36</x:Double>
<x:Double x:Key="HeaderHeight">36</x:Double> <x:Double x:Key="NavigationItemHeight">36</x:Double>
<x:Double x:Key="NavigationItemHeight">36</x:Double> <x:Double x:Key="HamburgerMenuButtonHeight">32</x:Double>
<x:Double x:Key="HamburgerMenuButtonHeight">32</x:Double> <Thickness x:Key="HeaderMarginCollapsedPane">12,0,0,0</Thickness>
<Thickness x:Key="HeaderMarginCollapsedPane">12,0,0,0</Thickness> <Thickness x:Key="HeaderMarginExpandedPane">52,0,0,0</Thickness>
<Thickness x:Key="HeaderMarginExpandedPane">52,0,0,0</Thickness> <Thickness x:Key="HeaderMarginExpandedOverlayPane">212,0,0,0</Thickness>
<Thickness x:Key="HeaderMarginExpandedOverlayPane">212,0,0,0</Thickness> <BoxShadows x:Key="NavigationItemShadow">1 1 1 1 #2000, 0 0 1 1 #2fff</BoxShadows>
<BoxShadows x:Key="NavigationItemShadow">1 1 1 1 #2000, 0 0 1 1 #2fff</BoxShadows> <BoxShadows x:Key="NavigationContentShadow">0 0 1 1 #2000</BoxShadows>
<BoxShadows x:Key="NavigationContentShadow">0 0 1 1 #2000</BoxShadows>
</Styles.Resources> <ControlTheme x:Key="NavigationButton" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="VerticalAlignment" Value="Stretch" />
<Setter Property="FontSize" Value="{DynamicResource ControlContentThemeFontSize}" />
<Setter Property="FontWeight" Value="Normal" />
<Setter Property="MinHeight" Value="0" />
<Setter Property="Height" Value="{StaticResource NavigationItemHeight}" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Padding" Value="12,0,4,0" />
<Setter Property="Margin" Value="4,0,8,0" />
<Setter Property="CornerRadius" Value="8" />
<Setter Property="ClipToBounds" Value="False" />
<Setter Property="Template">
<ControlTemplate>
<ContentPresenter Name="PART_ContentPresenter"
Padding="{TemplateBinding Padding}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
CornerRadius="{TemplateBinding CornerRadius}"
TextElement.FontFamily="{TemplateBinding FontFamily}"
TextElement.FontSize="{TemplateBinding FontSize}"
TextElement.FontWeight="{TemplateBinding FontWeight}" />
</ControlTemplate>
</Setter>
<Style Selector="^:pointerover /template/ ContentPresenter">
<Setter Property="Border.Background" Value="{DynamicResource SystemChromeLowColor}" />
<Setter Property="Border.BoxShadow" Value="{StaticResource NavigationItemShadow}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource TabItemHeaderForegroundUnselectedPointerOver}" />
</Style>
</ControlTheme>
<ControlTheme x:Key="HamburgerMenuTabItem" TargetType="TabItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="VerticalAlignment" Value="Stretch" />
<Setter Property="FontSize" Value="{DynamicResource ControlContentThemeFontSize}" />
<Setter Property="FontWeight" Value="Normal" />
<Setter Property="MinHeight" Value="0" />
<Setter Property="Height" Value="{StaticResource NavigationItemHeight}" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Padding" Value="12,0,4,0" />
<Setter Property="Margin" Value="4,0,8,0" />
<Setter Property="CornerRadius" Value="8" />
<Setter Property="ClipToBounds" Value="False" />
<Setter Property="Template">
<ControlTemplate>
<Border Name="PART_LayoutRoot"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}">
<Panel>
<Border Name="PART_SelectedPipe"
Width="{DynamicResource TabItemPipeThickness}"
Height="{DynamicResource TabItemVerticalPipeHeight}"
Margin="6,0,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Background="{DynamicResource TabItemHeaderSelectedPipeFill}"
IsVisible="False"
CornerRadius="{DynamicResource ControlCornerRadius}"/>
<ContentPresenter Name="PART_ContentPresenter"
Padding="{TemplateBinding Padding}"
Margin="0"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
TextElement.FontFamily="{TemplateBinding FontFamily}"
TextElement.FontSize="{TemplateBinding FontSize}"
TextElement.FontWeight="{TemplateBinding FontWeight}" />
</Panel>
</Border>
</ControlTemplate>
</Setter>
<Style Selector="^:pointerover">
<Setter Property="Background" Value="{DynamicResource ThemeControlHighlightMidBrush}"/>
<Style Selector="^ /template/ Border#PART_LayoutRoot">
<Setter Property="Background" Value="{DynamicResource SystemChromeLowColor}" />
<Setter Property="BoxShadow" Value="{StaticResource NavigationItemShadow}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource TabItemHeaderForegroundUnselectedPointerOver}" />
</Style>
</Style>
<Style Selector="^:selected">
<Setter Property="Background" Value="{DynamicResource ThemeAccentBrush4}"/>
<Style Selector="^ /template/ Border#PART_SelectedPipe">
<Setter Property="IsVisible" Value="True" />
</Style>
</Style>
<Style Selector="^:pressed /template/ Border#PART_LayoutRoot">
<Setter Property="Border.Background" Value="{DynamicResource SystemChromeLowColor}" />
<Setter Property="Border.BoxShadow" Value="{StaticResource NavigationItemShadow}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource TabItemHeaderForegroundUnselectedPressed}" />
</Style>
</ControlTheme>
<!-- HamburgerMenu --> <!-- HamburgerMenu -->
<Style Selector="catalog|HamburgerMenu"> <ControlTheme x:Key="{x:Type catalog:HamburgerMenu}" TargetType="catalog:HamburgerMenu">
<Setter Property="Padding" Value="12 8 4 0" /> <Setter Property="Padding" Value="12 8 4 0" />
<Setter Property="PaneBackground" Value="{DynamicResource SystemChromeMediumColor}" /> <Setter Property="PaneBackground" Value="{DynamicResource SystemChromeMediumColor}" />
<Setter Property="Background" Value="{DynamicResource SystemChromeMediumColor}" /> <Setter Property="Background" Value="{DynamicResource SystemChromeMediumColor}" />
<Setter Property="ContentBackground" Value="{DynamicResource SystemAltHighColor}" /> <Setter Property="ContentBackground" Value="{DynamicResource SystemAltHighColor}" />
<Setter Property="ItemContainerTheme" Value="{StaticResource HamburgerMenuTabItem}"/>
<Setter Property="TabStripPlacement" Value="Left" />
<Setter Property="Template"> <Setter Property="Template">
<ControlTemplate> <ControlTemplate>
<Panel Background="{TemplateBinding PaneBackground}"> <Panel Background="{TemplateBinding PaneBackground}">
@ -46,7 +157,8 @@
CompactPaneLength="{StaticResource PaneCompactWidth}" CompactPaneLength="{StaticResource PaneCompactWidth}"
DisplayMode="Inline" DisplayMode="Inline"
IsPaneOpen="True" IsPaneOpen="True"
OpenPaneLength="{StaticResource PaneExpandWidth}"> OpenPaneLength="{StaticResource PaneExpandWidth}"
PaneBackground="Transparent">
<SplitView.Pane> <SplitView.Pane>
<Grid Margin="0,0,1,5" RowDefinitions="Auto, *, Auto"> <Grid Margin="0,0,1,5" RowDefinitions="Auto, *, Auto">
<Panel Height="{StaticResource HeaderHeight}" /> <Panel Height="{StaticResource HeaderHeight}" />
@ -58,8 +170,7 @@
<ItemsPresenter Name="PART_ItemsPresenter" <ItemsPresenter Name="PART_ItemsPresenter"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
ItemTemplate="{TemplateBinding ItemTemplate}" ItemTemplate="{TemplateBinding ItemTemplate}"
Items="{TemplateBinding Items}" Items="{TemplateBinding Items}">
ItemsPanel="{TemplateBinding ItemsPanel}">
<ItemsPresenter.ItemsPanel> <ItemsPresenter.ItemsPanel>
<ItemsPanelTemplate> <ItemsPanelTemplate>
<StackPanel x:Name="HamburgerItemsPanel" <StackPanel x:Name="HamburgerItemsPanel"
@ -70,7 +181,7 @@
</ScrollViewer> </ScrollViewer>
<Button x:Name="SettingsButton" <Button x:Name="SettingsButton"
Grid.Row="2" Grid.Row="2"
Classes="NavigationButton" Theme="{StaticResource NavigationButton}"
Content="Settings" Content="Settings"
Flyout="{TemplateBinding (FlyoutBase.AttachedFlyout)}" Flyout="{TemplateBinding (FlyoutBase.AttachedFlyout)}"
IsVisible="{Binding $parent[TabControl].(FlyoutBase.AttachedFlyout), Converter={x:Static ObjectConverters.IsNotNull}}" /> IsVisible="{Binding $parent[TabControl].(FlyoutBase.AttachedFlyout), Converter={x:Static ObjectConverters.IsNotNull}}" />
@ -82,6 +193,7 @@
<TextBlock x:Name="HeaderHolder" <TextBlock x:Name="HeaderHolder"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="h1" Classes="h1"
Margin="{StaticResource HeaderMarginExpandedPane}"
Text="{Binding $parent[TabControl].SelectedItem.Header, FallbackValue=''}"> Text="{Binding $parent[TabControl].SelectedItem.Header, FallbackValue=''}">
<TextBlock.Transitions> <TextBlock.Transitions>
<Transitions> <Transitions>
@ -119,7 +231,7 @@
HorizontalAlignment="Left" HorizontalAlignment="Left"
VerticalAlignment="Top" VerticalAlignment="Top"
HorizontalContentAlignment="Center" HorizontalContentAlignment="Center"
Classes="NavigationButton" Theme="{StaticResource NavigationButton}"
CornerRadius="4" CornerRadius="4"
IsChecked="{Binding #PART_NavigationPane.IsPaneOpen, Mode=TwoWay}"> IsChecked="{Binding #PART_NavigationPane.IsPaneOpen, Mode=TwoWay}">
<PathIcon Data="M3 17h18a1 1 0 0 1 .117 1.993L21 19H3a1 1 0 0 1-.117-1.993L3 17h18H3Zm0-6 18-.002a1 1 0 0 1 .117 1.993l-.117.007L3 13a1 1 0 0 1-.117-1.993L3 11l18-.002L3 11Zm0-6h18a1 1 0 0 1 .117 1.993L21 7H3a1 1 0 0 1-.117-1.993L3 5h18H3Z" Foreground="{TemplateBinding Foreground}" /> <PathIcon Data="M3 17h18a1 1 0 0 1 .117 1.993L21 19H3a1 1 0 0 1-.117-1.993L3 17h18H3Zm0-6 18-.002a1 1 0 0 1 .117 1.993l-.117.007L3 13a1 1 0 0 1-.117-1.993L3 11l18-.002L3 11Zm0-6h18a1 1 0 0 1 .117 1.993L21 7H3a1 1 0 0 1-.117-1.993L3 5h18H3Z" Foreground="{TemplateBinding Foreground}" />
@ -127,116 +239,26 @@
</Panel> </Panel>
</ControlTemplate> </ControlTemplate>
</Setter> </Setter>
</Style>
<Style Selector="catalog|HamburgerMenu /template/ SplitView TextBlock#HeaderHolder"> <Style Selector="^ /template/ SplitView[IsPaneOpen=True] TextBlock#HeaderHolder">
<Setter Property="Margin" Value="{StaticResource HeaderMarginExpandedPane}" /> <Setter Property="Margin" Value="{StaticResource HeaderMarginCollapsedPane}" />
</Style> </Style>
<Style Selector="catalog|HamburgerMenu /template/ SplitView[IsPaneOpen=True] TextBlock#HeaderHolder"> <Style Selector="^ /template/ SplitView[DisplayMode=Overlay][IsPaneOpen=True] TextBlock#HeaderHolder">
<Setter Property="Margin" Value="{StaticResource HeaderMarginCollapsedPane}" /> <Setter Property="Margin" Value="{StaticResource HeaderMarginExpandedOverlayPane}" />
</Style> </Style>
<Style Selector="catalog|HamburgerMenu /template/ SplitView[DisplayMode=Overlay][IsPaneOpen=True] TextBlock#HeaderHolder"> <Style Selector="^ /template/ SplitView[DisplayMode=Overlay]">
<Setter Property="Margin" Value="{StaticResource HeaderMarginExpandedOverlayPane}" /> <Setter Property="PaneBackground" Value="{TemplateBinding PaneBackground}" />
</Style> </Style>
<Style Selector="catalog|HamburgerMenu /template/ SplitView"> <Style Selector="^ /template/ SplitView[DisplayMode=Overlay]">
<Setter Property="PaneBackground" Value="Transparent" /> <Setter Property="Background" Value="{Binding $parent[TabControl].ContentBackground}" />
</Style> </Style>
<Style Selector="catalog|HamburgerMenu /template/ SplitView[DisplayMode=Overlay]"> <Style Selector="^ /template/ SplitView[DisplayMode=Inline] Border#BackgroundBorder">
<Setter Property="PaneBackground" Value="{TemplateBinding PaneBackground}" /> <Setter Property="Background" Value="{Binding $parent[TabControl].ContentBackground}" />
</Style> <Setter Property="BoxShadow" Value="{StaticResource NavigationContentShadow}" />
<Style Selector="catalog|HamburgerMenu /template/ SplitView[DisplayMode=Overlay]"> </Style>
<Setter Property="Background" Value="{Binding $parent[TabControl].ContentBackground}" /> <Style Selector="^ /template/ SplitView[DisplayMode=Inline][IsPaneOpen=True] Border#BackgroundBorder">
</Style> <Setter Property="CornerRadius" Value="8 0 0 0" />
<Style Selector="catalog|HamburgerMenu /template/ SplitView[DisplayMode=Inline] Border#BackgroundBorder"> </Style>
<Setter Property="Background" Value="{Binding $parent[TabControl].ContentBackground}" /> </ControlTheme>
<Setter Property="BoxShadow" Value="{StaticResource NavigationContentShadow}" />
</Style>
<Style Selector="catalog|HamburgerMenu /template/ SplitView[DisplayMode=Inline][IsPaneOpen=True] Border#BackgroundBorder">
<Setter Property="CornerRadius" Value="8 0 0 0" />
</Style>
</ResourceDictionary>
<!-- HamburgerMenu TabItem -->
<Style Selector="catalog|HamburgerMenu > TabItem, :is(Button).NavigationButton">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="VerticalAlignment" Value="Stretch" />
<Setter Property="FontSize" Value="{DynamicResource ControlContentThemeFontSize}" />
<Setter Property="FontWeight" Value="Normal" />
<Setter Property="MinHeight" Value="0" />
<Setter Property="Height" Value="{StaticResource NavigationItemHeight}" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Padding" Value="12,0,4,0" />
<Setter Property="Margin" Value="4,0,8,0" />
<Setter Property="CornerRadius" Value="8" />
<Setter Property="ClipToBounds" Value="False" />
</Style>
<Style Selector="catalog|HamburgerMenu > TabItem">
<Setter Property="Template">
<ControlTemplate>
<Border Name="PART_LayoutRoot"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}">
<Panel>
<Border Name="PART_SelectedPipe"
Width="{DynamicResource TabItemPipeThickness}"
Height="{DynamicResource TabItemVerticalPipeHeight}"
Margin="6,0,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Background="{DynamicResource TabItemHeaderSelectedPipeFill}" />
<ContentPresenter Name="PART_ContentPresenter"
Padding="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
TextElement.FontFamily="{TemplateBinding FontFamily}"
TextElement.FontSize="{TemplateBinding FontSize}"
TextElement.FontWeight="{TemplateBinding FontWeight}" />
</Panel>
</Border>
</ControlTemplate>
</Setter>
</Style>
<Style Selector=":is(Button).NavigationButton">
<Setter Property="Template">
<ControlTemplate>
<ContentPresenter Name="PART_ContentPresenter"
Padding="{TemplateBinding Padding}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
CornerRadius="{TemplateBinding CornerRadius}"
TextElement.FontFamily="{TemplateBinding FontFamily}"
TextElement.FontSize="{TemplateBinding FontSize}"
TextElement.FontWeight="{TemplateBinding FontWeight}" />
</ControlTemplate>
</Setter>
</Style>
<Style Selector="catalog|HamburgerMenu > TabItem /template/ Border#PART_LayoutRoot, :is(Button).NavigationButton /template/ ContentPresenter">
<Setter Property="Border.Background" Value="Transparent" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SystemBaseHighColor}" />
</Style>
<Style Selector="catalog|HamburgerMenu > TabItem:pointerover /template/ Border#PART_LayoutRoot, :is(Button).NavigationButton:pointerover /template/ ContentPresenter">
<Setter Property="Border.Background" Value="{DynamicResource SystemChromeLowColor}" />
<Setter Property="Border.BoxShadow" Value="{StaticResource NavigationItemShadow}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource TabItemHeaderForegroundUnselectedPointerOver}" />
</Style>
<Style Selector="catalog|HamburgerMenu > TabItem:pressed /template/ Border#PART_LayoutRoot, :is(Button).NavigationButton:pressed /template/ ContentPresenter">
<Setter Property="Border.Background" Value="{DynamicResource SystemChromeLowColor}" />
<Setter Property="Border.BoxShadow" Value="{StaticResource NavigationItemShadow}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource TabItemHeaderForegroundUnselectedPressed}" />
</Style>
<Style Selector=":is(Button).NavigationButton:pressed">
<Setter Property="RenderTransform" Value="none" />
</Style>
</Styles>

5
src/Avalonia.Base/Input/Cursor.cs

@ -32,10 +32,7 @@ namespace Avalonia.Input
DragCopy, DragCopy,
DragLink, DragLink,
None, None,
[Obsolete("Use BottomSide")]
BottomSize = BottomSide
// Not available in GTK directly, see http://www.pixelbeat.org/programming/x_cursors/ // Not available in GTK directly, see http://www.pixelbeat.org/programming/x_cursors/
// We might enable them later, preferably, by loading pixmax directly from theme with fallback image // We might enable them later, preferably, by loading pixmax directly from theme with fallback image
// SizeNorthWestSouthEast, // SizeNorthWestSouthEast,

18
src/Avalonia.Base/Input/DragEventArgs.cs

@ -13,9 +13,6 @@ namespace Avalonia.Input
public IDataObject Data { get; private set; } public IDataObject Data { get; private set; }
[Obsolete("Use KeyModifiers")]
public InputModifiers Modifiers { get; private set; }
public KeyModifiers KeyModifiers { get; private set; } public KeyModifiers KeyModifiers { get; private set; }
public Point GetPosition(IVisual relativeTo) public Point GetPosition(IVisual relativeTo)
@ -35,17 +32,6 @@ namespace Avalonia.Input
return point; return point;
} }
[Obsolete("Use constructor taking KeyModifiers")]
public DragEventArgs(RoutedEvent<DragEventArgs> routedEvent, IDataObject data, Interactive target, Point targetLocation, InputModifiers modifiers)
: base(routedEvent)
{
Data = data;
_target = target;
_targetLocation = targetLocation;
Modifiers = modifiers;
KeyModifiers = (KeyModifiers)(((int)modifiers) & 0xF);
}
public DragEventArgs(RoutedEvent<DragEventArgs> routedEvent, IDataObject data, Interactive target, Point targetLocation, KeyModifiers keyModifiers) public DragEventArgs(RoutedEvent<DragEventArgs> routedEvent, IDataObject data, Interactive target, Point targetLocation, KeyModifiers keyModifiers)
: base(routedEvent) : base(routedEvent)
{ {
@ -53,10 +39,6 @@ namespace Avalonia.Input
_target = target; _target = target;
_targetLocation = targetLocation; _targetLocation = targetLocation;
KeyModifiers = keyModifiers; KeyModifiers = keyModifiers;
#pragma warning disable CS0618 // Type or member is obsolete
Modifiers = (InputModifiers)keyModifiers;
#pragma warning restore CS0618 // Type or member is obsolete
} }
} }
} }

11
src/Avalonia.Base/Input/GotFocusEventArgs.cs

@ -1,4 +1,3 @@
using System;
using Avalonia.Interactivity; using Avalonia.Interactivity;
namespace Avalonia.Input namespace Avalonia.Input
@ -13,16 +12,6 @@ namespace Avalonia.Input
/// </summary> /// </summary>
public NavigationMethod NavigationMethod { get; set; } public NavigationMethod NavigationMethod { get; set; }
/// <summary>
/// Gets or sets any input modifiers active at the time of focus.
/// </summary>
[Obsolete("Use KeyModifiers")]
public InputModifiers InputModifiers
{
get => (InputModifiers)KeyModifiers;
set => KeyModifiers = (KeyModifiers)((int)value & 0xF);
}
/// <summary> /// <summary>
/// Gets or sets any key modifiers active at the time of focus. /// Gets or sets any key modifiers active at the time of focus.
/// </summary> /// </summary>

5
src/Avalonia.Base/Input/IInputRoot.cs

@ -27,10 +27,5 @@ namespace Avalonia.Input
/// Gets or sets a value indicating whether access keys are shown in the window. /// Gets or sets a value indicating whether access keys are shown in the window.
/// </summary> /// </summary>
bool ShowAccessKeys { get; set; } bool ShowAccessKeys { get; set; }
/// <summary>
/// Gets associated mouse device
/// </summary>
IMouseDevice? MouseDevice { get; }
} }
} }

13
src/Avalonia.Base/Input/IKeyboardDevice.cs

@ -4,19 +4,6 @@ using Avalonia.Metadata;
namespace Avalonia.Input namespace Avalonia.Input
{ {
[Flags, Obsolete("Use KeyModifiers and PointerPointProperties")]
public enum InputModifiers
{
None = 0,
Alt = 1,
Control = 2,
Shift = 4,
Windows = 8,
LeftMouseButton = 16,
RightMouseButton = 32,
MiddleMouseButton = 64
}
[Flags] [Flags]
public enum KeyModifiers public enum KeyModifiers
{ {

12
src/Avalonia.Base/Input/IMouseDevice.cs

@ -1,4 +1,3 @@
using System;
using Avalonia.Metadata; using Avalonia.Metadata;
namespace Avalonia.Input namespace Avalonia.Input
@ -9,16 +8,5 @@ namespace Avalonia.Input
[NotClientImplementable] [NotClientImplementable]
public interface IMouseDevice : IPointerDevice public interface IMouseDevice : IPointerDevice
{ {
/// <summary>
/// Gets the mouse position, in screen coordinates.
/// </summary>
[Obsolete("Use PointerEventArgs.GetPosition")]
PixelPoint Position { get; }
[Obsolete]
void TopLevelClosed(IInputRoot root);
[Obsolete]
void SceneInvalidated(IInputRoot root, Rect rect);
} }
} }

14
src/Avalonia.Base/Input/IPointerDevice.cs

@ -1,5 +1,3 @@
using System;
using Avalonia.VisualTree;
using Avalonia.Input.Raw; using Avalonia.Input.Raw;
using Avalonia.Metadata; using Avalonia.Metadata;
@ -8,18 +6,6 @@ namespace Avalonia.Input
[NotClientImplementable] [NotClientImplementable]
public interface IPointerDevice : IInputDevice public interface IPointerDevice : IInputDevice
{ {
/// <inheritdoc cref="IPointer.Captured" />
[Obsolete("Use IPointer")]
IInputElement? Captured { get; }
/// <inheritdoc cref="IPointer.Capture(IInputElement?)" />
[Obsolete("Use IPointer")]
void Capture(IInputElement? control);
/// <inheritdoc cref="PointerEventArgs.GetPosition(IVisual?)" />
[Obsolete("Use PointerEventArgs.GetPosition")]
Point GetPosition(IVisual relativeTo);
/// <summary> /// <summary>
/// Gets a pointer for specific event args. /// Gets a pointer for specific event args.
/// </summary> /// </summary>

2
src/Avalonia.Base/Input/KeyEventArgs.cs

@ -9,8 +9,6 @@ namespace Avalonia.Input
public Key Key { get; set; } public Key Key { get; set; }
[Obsolete("Use KeyModifiers")]
public InputModifiers Modifiers => (InputModifiers)KeyModifiers;
public KeyModifiers KeyModifiers { get; set; } public KeyModifiers KeyModifiers { get; set; }
} }
} }

12
src/Avalonia.Base/Input/KeyGesture.cs

@ -15,13 +15,6 @@ namespace Avalonia.Input
{ "+", Key.OemPlus }, { "-", Key.OemMinus }, { ".", Key.OemPeriod }, { ",", Key.OemComma } { "+", Key.OemPlus }, { "-", Key.OemMinus }, { ".", Key.OemPeriod }, { ",", Key.OemComma }
}; };
[Obsolete("Use constructor taking KeyModifiers")]
public KeyGesture(Key key, InputModifiers modifiers)
{
Key = key;
KeyModifiers = (KeyModifiers)(((int)modifiers) & 0xf);
}
public KeyGesture(Key key, KeyModifiers modifiers = KeyModifiers.None) public KeyGesture(Key key, KeyModifiers modifiers = KeyModifiers.None)
{ {
Key = key; Key = key;
@ -63,10 +56,7 @@ namespace Avalonia.Input
} }
public Key Key { get; } public Key Key { get; }
[Obsolete("Use KeyModifiers")]
public InputModifiers Modifiers => (InputModifiers)KeyModifiers;
public KeyModifiers KeyModifiers { get; } public KeyModifiers KeyModifiers { get; }
public static KeyGesture Parse(string gesture) public static KeyGesture Parse(string gesture)

54
src/Avalonia.Base/Input/MouseDevice.cs

@ -21,7 +21,6 @@ namespace Avalonia.Input
private readonly Pointer _pointer; private readonly Pointer _pointer;
private bool _disposed; private bool _disposed;
private PixelPoint? _position;
private MouseButton _lastMouseDownButton; private MouseButton _lastMouseDownButton;
public MouseDevice(Pointer? pointer = null) public MouseDevice(Pointer? pointer = null)
@ -29,43 +28,6 @@ namespace Avalonia.Input
_pointer = pointer ?? new Pointer(Pointer.GetNextFreeId(), PointerType.Mouse, true); _pointer = pointer ?? new Pointer(Pointer.GetNextFreeId(), PointerType.Mouse, true);
} }
[Obsolete("Use IPointer instead")]
public IInputElement? Captured => _pointer.Captured;
[Obsolete("Use events instead")]
public PixelPoint Position
{
get => _position ?? new PixelPoint(-1, -1);
protected set => _position = value;
}
[Obsolete("Use IPointer instead")]
public void Capture(IInputElement? control)
{
_pointer.Capture(control);
}
/// <summary>
/// Gets the mouse position relative to a control.
/// </summary>
/// <param name="relativeTo">The control.</param>
/// <returns>The mouse position in the control's coordinates.</returns>
public Point GetPosition(IVisual relativeTo)
{
relativeTo = relativeTo ?? throw new ArgumentNullException(nameof(relativeTo));
if (relativeTo.VisualRoot == null)
{
throw new InvalidOperationException("Control is not attached to visual tree.");
}
#pragma warning disable CS0618 // Type or member is obsolete
var rootPoint = relativeTo.VisualRoot.PointToClient(Position);
#pragma warning restore CS0618 // Type or member is obsolete
var transform = relativeTo.VisualRoot.TransformToVisual(relativeTo);
return rootPoint * transform!.Value;
}
public void ProcessRawEvent(RawInputEventArgs e) public void ProcessRawEvent(RawInputEventArgs e)
{ {
if (!e.Handled && e is RawPointerEventArgs margs) if (!e.Handled && e is RawPointerEventArgs margs)
@ -96,7 +58,6 @@ namespace Avalonia.Input
if(mouse._disposed) if(mouse._disposed)
return; return;
_position = e.Root.PointToScreen(e.Position);
var props = CreateProperties(e); var props = CreateProperties(e);
var keyModifiers = e.InputModifiers.ToKeyModifiers(); var keyModifiers = e.InputModifiers.ToKeyModifiers();
switch (e.Type) switch (e.Type)
@ -145,7 +106,6 @@ namespace Avalonia.Input
private void LeaveWindow() private void LeaveWindow()
{ {
_position = null;
} }
PointerPointProperties CreateProperties(RawPointerEventArgs args) PointerPointProperties CreateProperties(RawPointerEventArgs args)
@ -324,19 +284,7 @@ namespace Avalonia.Input
_disposed = true; _disposed = true;
_pointer?.Dispose(); _pointer?.Dispose();
} }
[Obsolete]
public void TopLevelClosed(IInputRoot root)
{
// no-op
}
[Obsolete]
public void SceneInvalidated(IInputRoot root, Rect rect)
{
// no-op
}
public IPointer? TryGetPointer(RawPointerEventArgs ev) public IPointer? TryGetPointer(RawPointerEventArgs ev)
{ {
return _pointer; return _pointer;

19
src/Avalonia.Base/Input/PenDevice.cs

@ -1,10 +1,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reactive.Linq;
using Avalonia.Input.Raw; using Avalonia.Input.Raw;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.VisualTree;
namespace Avalonia.Input namespace Avalonia.Input
{ {
@ -14,7 +12,6 @@ namespace Avalonia.Input
public class PenDevice : IPenDevice, IDisposable public class PenDevice : IPenDevice, IDisposable
{ {
private readonly Dictionary<long, Pointer> _pointers = new(); private readonly Dictionary<long, Pointer> _pointers = new();
private readonly Dictionary<long, PixelPoint> _lastPositions = new();
private int _clickCount; private int _clickCount;
private Rect _lastClickRect; private Rect _lastClickRect;
private ulong _lastClickTime; private ulong _lastClickTime;
@ -41,9 +38,7 @@ namespace Avalonia.Input
_pointers[e.RawPointerId] = pointer = new Pointer(Pointer.GetNextFreeId(), _pointers[e.RawPointerId] = pointer = new Pointer(Pointer.GetNextFreeId(),
PointerType.Pen, _pointers.Count == 0); PointerType.Pen, _pointers.Count == 0);
} }
_lastPositions[e.RawPointerId] = e.Root.PointToScreen(e.Position);
var props = new PointerPointProperties(e.InputModifiers, e.Type.ToUpdateKind(), var props = new PointerPointProperties(e.InputModifiers, e.Type.ToUpdateKind(),
e.Point.Twist, e.Point.Pressure, e.Point.XTilt, e.Point.YTilt); e.Point.Twist, e.Point.Pressure, e.Point.XTilt, e.Point.YTilt);
var keyModifiers = e.InputModifiers.ToKeyModifiers(); var keyModifiers = e.InputModifiers.ToKeyModifiers();
@ -69,7 +64,6 @@ namespace Avalonia.Input
{ {
pointer.Dispose(); pointer.Dispose();
_pointers.Remove(e.RawPointerId); _pointers.Remove(e.RawPointerId);
_lastPositions.Remove(e.RawPointerId);
} }
} }
@ -153,17 +147,6 @@ namespace Avalonia.Input
p.Dispose(); p.Dispose();
} }
[Obsolete]
IInputElement? IPointerDevice.Captured => _pointers.Values
.FirstOrDefault(p => p.IsPrimary)?.Captured;
[Obsolete]
void IPointerDevice.Capture(IInputElement? control) => _pointers.Values
.FirstOrDefault(p => p.IsPrimary)?.Capture(control);
[Obsolete]
Point IPointerDevice.GetPosition(IVisual relativeTo) => new Point(-1, -1);
public IPointer? TryGetPointer(RawPointerEventArgs ev) public IPointer? TryGetPointer(RawPointerEventArgs ev)
{ {
return _pointers.TryGetValue(ev.RawPointerId, out var pointer) return _pointers.TryGetValue(ev.RawPointerId, out var pointer)

62
src/Avalonia.Base/Input/PointerEventArgs.cs

@ -11,7 +11,7 @@ namespace Avalonia.Input
private readonly IVisual? _rootVisual; private readonly IVisual? _rootVisual;
private readonly Point _rootVisualPosition; private readonly Point _rootVisualPosition;
private readonly PointerPointProperties _properties; private readonly PointerPointProperties _properties;
private Lazy<IReadOnlyList<RawPointerPoint>?>? _previousPoints; private readonly Lazy<IReadOnlyList<RawPointerPoint>?>? _previousPoints;
public PointerEventArgs(RoutedEvent routedEvent, public PointerEventArgs(RoutedEvent routedEvent,
IInteractive? source, IInteractive? source,
@ -43,29 +43,6 @@ namespace Avalonia.Input
{ {
_previousPoints = previousPoints; _previousPoints = previousPoints;
} }
class EmulatedDevice : IPointerDevice
{
private readonly PointerEventArgs _ev;
public EmulatedDevice(PointerEventArgs ev)
{
_ev = ev;
}
public void ProcessRawEvent(RawInputEventArgs ev) => throw new NotSupportedException();
public IInputElement? Captured => _ev.Pointer.Captured;
public void Capture(IInputElement? control)
{
_ev.Pointer.Capture(control);
}
public Point GetPosition(IVisual relativeTo) => _ev.GetPosition(relativeTo);
public IPointer? TryGetPointer(RawPointerEventArgs ev) => _ev.Pointer;
}
/// <summary> /// <summary>
/// Gets specific pointer generated by input device. /// Gets specific pointer generated by input device.
@ -77,28 +54,6 @@ namespace Avalonia.Input
/// </summary> /// </summary>
public ulong Timestamp { get; } public ulong Timestamp { get; }
private IPointerDevice? _device;
[Obsolete("Use Pointer to get pointer-specific information")]
public IPointerDevice Device => _device ?? (_device = new EmulatedDevice(this));
[Obsolete("Use KeyModifiers and PointerPointProperties")]
public InputModifiers InputModifiers
{
get
{
var mods = (InputModifiers)KeyModifiers;
if (_properties.IsLeftButtonPressed)
mods |= InputModifiers.LeftMouseButton;
if (_properties.IsMiddleButtonPressed)
mods |= InputModifiers.MiddleMouseButton;
if (_properties.IsRightButtonPressed)
mods |= InputModifiers.RightMouseButton;
return mods;
}
}
/// <summary> /// <summary>
/// Gets a value that indicates which key modifiers were active at the time that the pointer event was initiated. /// Gets a value that indicates which key modifiers were active at the time that the pointer event was initiated.
/// </summary> /// </summary>
@ -120,9 +75,6 @@ namespace Avalonia.Input
/// <returns>The pointer position in the control's coordinates.</returns> /// <returns>The pointer position in the control's coordinates.</returns>
public Point GetPosition(IVisual? relativeTo) => GetPosition(_rootVisualPosition, relativeTo); public Point GetPosition(IVisual? relativeTo) => GetPosition(_rootVisualPosition, relativeTo);
[Obsolete("Use GetCurrentPoint")]
public PointerPoint GetPointerPoint(IVisual? relativeTo) => GetCurrentPoint(relativeTo);
/// <summary> /// <summary>
/// Returns the PointerPoint associated with the current event /// Returns the PointerPoint associated with the current event
/// </summary> /// </summary>
@ -171,8 +123,6 @@ namespace Avalonia.Input
public class PointerPressedEventArgs : PointerEventArgs public class PointerPressedEventArgs : PointerEventArgs
{ {
private readonly int _clickCount;
public PointerPressedEventArgs( public PointerPressedEventArgs(
IInteractive source, IInteractive source,
IPointer pointer, IPointer pointer,
@ -184,13 +134,10 @@ namespace Avalonia.Input
: base(InputElement.PointerPressedEvent, source, pointer, rootVisual, rootVisualPosition, : base(InputElement.PointerPressedEvent, source, pointer, rootVisual, rootVisualPosition,
timestamp, properties, modifiers) timestamp, properties, modifiers)
{ {
_clickCount = clickCount; ClickCount = clickCount;
} }
public int ClickCount => _clickCount; public int ClickCount { get; }
[Obsolete("Use PointerPressedEventArgs.GetCurrentPoint(this).Properties")]
public MouseButton MouseButton => Properties.PointerUpdateKind.GetMouseButton();
} }
public class PointerReleasedEventArgs : PointerEventArgs public class PointerReleasedEventArgs : PointerEventArgs
@ -210,9 +157,6 @@ namespace Avalonia.Input
/// Gets the mouse button that triggered the corresponding PointerPressed event /// Gets the mouse button that triggered the corresponding PointerPressed event
/// </summary> /// </summary>
public MouseButton InitialPressMouseButton { get; } public MouseButton InitialPressMouseButton { get; }
[Obsolete("Use InitialPressMouseButton")]
public MouseButton MouseButton => InitialPressMouseButton;
} }
public class PointerCaptureLostEventArgs : RoutedEventArgs public class PointerCaptureLostEventArgs : RoutedEventArgs

2
src/Avalonia.Base/Input/PointerOverPreProcessor.cs

@ -15,6 +15,8 @@ namespace Avalonia.Input
_inputRoot = inputRoot ?? throw new ArgumentNullException(nameof(inputRoot)); _inputRoot = inputRoot ?? throw new ArgumentNullException(nameof(inputRoot));
} }
public PixelPoint? LastPosition => _lastPointer?.position;
public void OnCompleted() public void OnCompleted()
{ {
ClearPointerOver(); ClearPointerOver();

5
src/Avalonia.Base/Input/Raw/RawDragEvent.cs

@ -8,8 +8,6 @@ namespace Avalonia.Input.Raw
public IDataObject Data { get; } public IDataObject Data { get; }
public DragDropEffects Effects { get; set; } public DragDropEffects Effects { get; set; }
public RawDragEventType Type { get; } public RawDragEventType Type { get; }
[Obsolete("Use KeyModifiers")]
public InputModifiers Modifiers { get; }
public KeyModifiers KeyModifiers { get; } public KeyModifiers KeyModifiers { get; }
public RawDragEvent(IDragDropDevice inputDevice, RawDragEventType type, public RawDragEvent(IDragDropDevice inputDevice, RawDragEventType type,
@ -21,9 +19,6 @@ namespace Avalonia.Input.Raw
Data = data; Data = data;
Effects = effects; Effects = effects;
KeyModifiers = modifiers.ToKeyModifiers(); KeyModifiers = modifiers.ToKeyModifiers();
#pragma warning disable CS0618 // Type or member is obsolete
Modifiers = (InputModifiers)modifiers;
#pragma warning restore CS0618 // Type or member is obsolete
} }
} }
} }

3
src/Avalonia.Base/Input/Raw/RawTouchEventArgs.cs

@ -19,8 +19,5 @@ namespace Avalonia.Input.Raw
{ {
RawPointerId = rawPointerId; RawPointerId = rawPointerId;
} }
[Obsolete("Use RawPointerId")]
public long TouchPointId { get => RawPointerId; set => RawPointerId = value; }
} }
} }

11
src/Avalonia.Base/Input/TouchDevice.cs

@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using Avalonia.Input.Raw; using Avalonia.Input.Raw;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.VisualTree;
namespace Avalonia.Input namespace Avalonia.Input
{ {
@ -20,9 +19,6 @@ namespace Avalonia.Input
private int _clickCount; private int _clickCount;
private Rect _lastClickRect; private Rect _lastClickRect;
private ulong _lastClickTime; private ulong _lastClickTime;
private Pointer? _lastPointer;
IInputElement? IPointerDevice.Captured => _lastPointer?.Captured;
RawInputModifiers GetModifiers(RawInputModifiers modifiers, bool isLeftButtonDown) RawInputModifiers GetModifiers(RawInputModifiers modifiers, bool isLeftButtonDown)
{ {
@ -32,10 +28,6 @@ namespace Avalonia.Input
return rv; return rv;
} }
void IPointerDevice.Capture(IInputElement? control) => _lastPointer?.Capture(control);
Point IPointerDevice.GetPosition(IVisual relativeTo) => default;
public void ProcessRawEvent(RawInputEventArgs ev) public void ProcessRawEvent(RawInputEventArgs ev)
{ {
if (ev.Handled || _disposed) if (ev.Handled || _disposed)
@ -51,7 +43,6 @@ namespace Avalonia.Input
PointerType.Touch, _pointers.Count == 0); PointerType.Touch, _pointers.Count == 0);
pointer.Capture(hit); pointer.Capture(hit);
} }
_lastPointer = pointer;
var target = pointer.Captured ?? args.Root; var target = pointer.Captured ?? args.Root;
var updateKind = args.Type.ToUpdateKind(); var updateKind = args.Type.ToUpdateKind();
@ -96,7 +87,6 @@ namespace Avalonia.Input
new PointerPointProperties(GetModifiers(args.InputModifiers, false), updateKind), new PointerPointProperties(GetModifiers(args.InputModifiers, false), updateKind),
keyModifier, MouseButton.Left)); keyModifier, MouseButton.Left));
} }
_lastPointer = null;
} }
if (args.Type == RawPointerEventType.TouchCancel) if (args.Type == RawPointerEventType.TouchCancel)
@ -104,7 +94,6 @@ namespace Avalonia.Input
_pointers.Remove(args.RawPointerId); _pointers.Remove(args.RawPointerId);
using (pointer) using (pointer)
pointer.Capture(null); pointer.Capture(null);
_lastPointer = null;
} }
if (args.Type == RawPointerEventType.TouchUpdate) if (args.Type == RawPointerEventType.TouchUpdate)

109
src/Avalonia.Base/Media/GlyphRun.cs

@ -445,7 +445,7 @@ namespace Avalonia.Media
/// </returns> /// </returns>
public int FindGlyphIndex(int characterIndex) public int FindGlyphIndex(int characterIndex)
{ {
if (GlyphClusters == null) if (GlyphClusters == null || GlyphClusters.Count == 0)
{ {
return characterIndex; return characterIndex;
} }
@ -614,17 +614,29 @@ namespace Avalonia.Media
private GlyphRunMetrics CreateGlyphRunMetrics() 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) 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); _offsetToFirstCharacter = Math.Max(0, Characters.Start - firstCluster);
} }
var isReversed = firstCluster > lastCluster;
var height = (GlyphTypeface.Descent - GlyphTypeface.Ascent + GlyphTypeface.LineGap) * Scale; var height = (GlyphTypeface.Descent - GlyphTypeface.Ascent + GlyphTypeface.LineGap) * Scale;
var widthIncludingTrailingWhitespace = 0d; 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++) for (var index = 0; index < GlyphIndices.Count; index++)
{ {
@ -635,16 +647,16 @@ namespace Avalonia.Media
var width = widthIncludingTrailingWhitespace; 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 _); width -= GetGlyphAdvance(index, out _);
} }
} }
else else
{ {
for (var index = 0; index < glyphCount; index++) for (var index = GlyphIndices.Count - glyphCount; index < GlyphIndices.Count; index++)
{ {
width -= GetGlyphAdvance(index, out _); width -= GetGlyphAdvance(index, out _);
} }
@ -654,16 +666,15 @@ namespace Avalonia.Media
height); height);
} }
private int GetTrailingWhitespaceLength(out int newLineLength, out int glyphCount) private int GetTrailingWhitespaceLength(bool isReversed, out int newLineLength, out int glyphCount)
{ {
glyphCount = 0; if (isReversed)
newLineLength = 0;
if (Characters.IsEmpty)
{ {
return 0; return GetTralingWhitespaceLengthRightToLeft(out newLineLength, out glyphCount);
} }
glyphCount = 0;
newLineLength = 0;
var trailingWhitespaceLength = 0; var trailingWhitespaceLength = 0;
if (GlyphClusters == null) if (GlyphClusters == null)
@ -732,6 +743,78 @@ namespace Avalonia.Media
return trailingWhitespaceLength; 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) private void Set<T>(ref T field, T value)
{ {
_glyphRunImpl?.Dispose(); _glyphRunImpl?.Dispose();

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

@ -209,7 +209,7 @@ namespace Avalonia.Media
var pen = new Pen(Stroke ?? defaultBrush, thickness, var pen = new Pen(Stroke ?? defaultBrush, thickness,
new DashStyle(StrokeDashArray, StrokeDashOffset), StrokeLineCap); 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));
} }
} }
} }

51
src/Avalonia.Base/Media/TextFormatting/TextLayout.cs

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

433
src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs

@ -166,58 +166,74 @@ namespace Avalonia.Media.TextFormatting
if (distance <= 0) if (distance <= 0)
{ {
// hit happens before the line, return the first position
var firstRun = _textRuns[0]; var firstRun = _textRuns[0];
if (firstRun is ShapedTextCharacters shapedTextCharacters) return GetRunCharacterHit(firstRun, FirstTextSourceIndex, 0);
{ }
return shapedTextCharacters.GlyphRun.GetCharacterHitFromDistance(distance, out _);
}
return _resolvedFlowDirection == FlowDirection.LeftToRight ? if (distance > WidthIncludingTrailingWhitespace)
new CharacterHit(FirstTextSourceIndex) : {
new CharacterHit(FirstTextSourceIndex + Length); var lastRun = _textRuns[_textRuns.Count - 1];
return GetRunCharacterHit(lastRun, FirstTextSourceIndex + Length - lastRun.TextSourceLength, lastRun.Size.Width);
} }
// process hit that happens within the line // process hit that happens within the line
var characterHit = new CharacterHit(); var characterHit = new CharacterHit();
var currentPosition = FirstTextSourceIndex; var currentPosition = FirstTextSourceIndex;
var currentDistance = 0.0;
foreach (var currentRun in _textRuns) foreach (var currentRun in _textRuns)
{ {
switch (currentRun) if (currentDistance + currentRun.Size.Width < distance)
{ {
case ShapedTextCharacters shapedRun: currentDistance += currentRun.Size.Width;
{ currentPosition += currentRun.TextSourceLength;
characterHit = shapedRun.GlyphRun.GetCharacterHitFromDistance(distance, out _);
var offset = Math.Max(0, currentPosition - shapedRun.Text.Start); continue;
}
characterHit = new CharacterHit(characterHit.FirstCharacterIndex + offset, characterHit.TrailingLength); characterHit = GetRunCharacterHit(currentRun, currentPosition, distance - currentDistance);
break; break;
} }
default:
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 = Math.Max(0, currentPosition - shapedRun.Text.Start);
if (!shapedRun.GlyphRun.IsLeftToRight)
{ {
if (distance < currentRun.Size.Width / 2) offset = Math.Max(0, offset - shapedRun.Text.End);
{
characterHit = new CharacterHit(currentPosition);
}
else
{
characterHit = new CharacterHit(currentPosition, currentRun.TextSourceLength);
}
break;
} }
}
if (distance <= currentRun.Size.Width) characterHit = new CharacterHit(characterHit.FirstCharacterIndex + offset, characterHit.TrailingLength);
{
break;
}
distance -= currentRun.Size.Width; break;
currentPosition += currentRun.TextSourceLength; }
default:
{
if (distance < run.Size.Width / 2)
{
characterHit = new CharacterHit(currentPosition);
}
else
{
characterHit = new CharacterHit(currentPosition, run.TextSourceLength);
}
break;
}
} }
return characterHit; return characterHit;
@ -226,136 +242,122 @@ namespace Avalonia.Media.TextFormatting
/// <inheritdoc/> /// <inheritdoc/>
public override double GetDistanceFromCharacterHit(CharacterHit characterHit) public override double GetDistanceFromCharacterHit(CharacterHit characterHit)
{ {
var isTrailingHit = characterHit.TrailingLength > 0; var flowDirection = _paragraphProperties.FlowDirection;
var characterIndex = characterHit.FirstCharacterIndex + characterHit.TrailingLength; var characterIndex = characterHit.FirstCharacterIndex + characterHit.TrailingLength;
var currentDistance = Start;
var currentPosition = FirstTextSourceIndex; var currentPosition = FirstTextSourceIndex;
var remainingLength = characterIndex - FirstTextSourceIndex; var remainingLength = characterIndex - FirstTextSourceIndex;
GlyphRun? lastRun = null; var currentDistance = Start;
for (var index = 0; index < _textRuns.Count; index++) if (flowDirection == FlowDirection.LeftToRight)
{
for (var index = 0; index < _textRuns.Count; index++)
{
var currentRun = _textRuns[index];
if (TryGetDistanceFromCharacterHit(currentRun, characterHit, currentPosition, remainingLength,
flowDirection, out var distance, out _))
{
return currentDistance + distance;
}
//No hit hit found so we add the full width
currentDistance += currentRun.Size.Width;
currentPosition += currentRun.TextSourceLength;
remainingLength -= currentRun.TextSourceLength;
}
}
else
{ {
var textRun = _textRuns[index]; currentDistance += WidthIncludingTrailingWhitespace;
switch (textRun) for (var index = _textRuns.Count - 1; index >= 0; index--)
{ {
case ShapedTextCharacters shapedTextCharacters: var currentRun = _textRuns[index];
{
var currentRun = shapedTextCharacters.GlyphRun;
if (lastRun != null) if (TryGetDistanceFromCharacterHit(currentRun, characterHit, currentPosition, remainingLength,
{ flowDirection, out var distance, out var currentGlyphRun))
if (!lastRun.IsLeftToRight && currentRun.IsLeftToRight && {
currentRun.Characters.Start == characterHit.FirstCharacterIndex && if (currentGlyphRun != null)
characterHit.TrailingLength == 0) {
{ distance = currentGlyphRun.Size.Width - distance;
return currentDistance; }
}
}
//Look for a hit in within the current run return currentDistance - distance;
if (currentPosition + remainingLength <= currentPosition + textRun.Text.Length) }
{
characterHit = new CharacterHit(textRun.Text.Start + remainingLength);
var distance = currentRun.GetDistanceFromCharacterHit(characterHit); //No hit hit found so we add the full width
currentDistance -= currentRun.Size.Width;
currentPosition += currentRun.TextSourceLength;
remainingLength -= currentRun.TextSourceLength;
}
}
return currentDistance + distance; return currentDistance;
} }
//Look at the left and right edge of the current run private static bool TryGetDistanceFromCharacterHit(
if (currentRun.IsLeftToRight) DrawableTextRun currentRun,
{ CharacterHit characterHit,
if (_resolvedFlowDirection == FlowDirection.LeftToRight && (lastRun == null || lastRun.IsLeftToRight)) int currentPosition,
{ int remainingLength,
if (characterIndex <= currentPosition) FlowDirection flowDirection,
{ out double distance,
return currentDistance; out GlyphRun? currentGlyphRun)
} {
} var characterIndex = characterHit.FirstCharacterIndex + characterHit.TrailingLength;
else var isTrailingHit = characterHit.TrailingLength > 0;
{
if (characterIndex == currentPosition)
{
return currentDistance;
}
}
if (characterIndex == currentPosition + textRun.Text.Length && isTrailingHit) distance = 0;
{ currentGlyphRun = null;
return currentDistance + currentRun.Size.Width;
}
}
else
{
if (characterIndex == currentPosition)
{
return currentDistance + currentRun.Size.Width;
}
var nextRun = index + 1 < _textRuns.Count ? switch (currentRun)
_textRuns[index + 1] as ShapedTextCharacters : {
null; case ShapedTextCharacters shapedTextCharacters:
{
currentGlyphRun = shapedTextCharacters.GlyphRun;
if (nextRun != null) if (currentPosition + remainingLength <= currentPosition + currentRun.Text.Length)
{ {
if (nextRun.ShapedBuffer.IsLeftToRight) characterHit = new CharacterHit(currentRun.Text.Start + remainingLength);
{
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;
}
}
}
lastRun = currentRun; distance = currentGlyphRun.GetDistanceFromCharacterHit(characterHit);
break; return true;
} }
default:
if (currentPosition + remainingLength == currentPosition + currentRun.Text.Length && isTrailingHit)
{ {
if (characterIndex == currentPosition) if (currentGlyphRun.IsLeftToRight || flowDirection == FlowDirection.RightToLeft)
{ {
return currentDistance; distance = currentGlyphRun.Size.Width;
} }
if (characterIndex == currentPosition + textRun.TextSourceLength) return true;
{ }
return currentDistance + textRun.Size.Width;
}
break; break;
}
default:
{
if (characterIndex == currentPosition)
{
return true;
} }
}
//No hit hit found so we add the full width if (characterIndex == currentPosition + currentRun.TextSourceLength)
currentDistance += textRun.Size.Width; {
currentPosition += textRun.TextSourceLength; distance = currentRun.Size.Width;
remainingLength -= textRun.TextSourceLength;
if (remainingLength <= 0) return true;
{
break; }
}
break;
}
} }
return currentDistance; return false;
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -460,20 +462,33 @@ namespace Avalonia.Media.TextFormatting
var startIndex = currentRun.Text.Start + offset; var startIndex = currentRun.Text.Start + offset;
var endOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit( double startOffset;
currentShapedRun.ShapedBuffer.IsLeftToRight ? double endOffset;
new CharacterHit(startIndex + remainingLength) :
new CharacterHit(startIndex));
endX += 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));
var startOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit( if (currentPosition < startIndex)
currentShapedRun.ShapedBuffer.IsLeftToRight ? {
new CharacterHit(startIndex) : startOffset = endOffset;
new CharacterHit(startIndex + remainingLength)); }
else
{
startOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit(new CharacterHit(startIndex + remainingLength));
}
}
startX += startOffset; startX += startOffset;
endX += endOffset;
var endHit = currentShapedRun.GlyphRun.GetCharacterHitFromDistance(endOffset, out _); var endHit = currentShapedRun.GlyphRun.GetCharacterHitFromDistance(endOffset, out _);
var startHit = currentShapedRun.GlyphRun.GetCharacterHitFromDistance(startOffset, out _); var startHit = currentShapedRun.GlyphRun.GetCharacterHitFromDistance(startOffset, out _);
@ -504,47 +519,40 @@ namespace Avalonia.Media.TextFormatting
} }
//Lines that only contain a linebreak need to be covered here //Lines that only contain a linebreak need to be covered here
if(characterLength == 0) if (characterLength == 0)
{ {
characterLength = NewLineLength; characterLength = NewLineLength;
} }
var runwidth = endX - startX; var runWidth = endX - startX;
var currentRunBounds = new TextRunBounds(new Rect(startX, 0, runwidth, Height), currentPosition, characterLength, currentRun); var currentRunBounds = new TextRunBounds(new Rect(startX, 0, runWidth, Height), currentPosition, characterLength, currentRun);
if (lastDirection == currentDirection && result.Count > 0 && MathUtilities.AreClose(currentRect.Right, startX)) if (!MathUtilities.IsZero(runWidth) || NewLineLength > 0)
{ {
currentRect = currentRect.WithWidth(currentWidth + runwidth); if (lastDirection == currentDirection && result.Count > 0 && MathUtilities.AreClose(currentRect.Right, 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); textBounds.TextRunBounds.Add(currentRunBounds);
} }
else else
{ {
currentRect = currentRunBounds.Rectangle; 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; currentWidth += runWidth;
currentPosition += characterLength; currentPosition += characterLength;
if (currentDirection == FlowDirection.LeftToRight) if (currentPosition > characterIndex)
{
if (currentPosition > characterIndex)
{
break;
}
}
else
{ {
if (currentPosition <= firstTextSourceIndex) break;
{
break;
}
} }
startX = endX; startX = endX;
@ -571,7 +579,7 @@ namespace Avalonia.Media.TextFormatting
var currentPosition = FirstTextSourceIndex; var currentPosition = FirstTextSourceIndex;
var remainingLength = textLength; var remainingLength = textLength;
var startX = Start + WidthIncludingTrailingWhitespace; var startX = WidthIncludingTrailingWhitespace;
double currentWidth = 0; double currentWidth = 0;
var currentRect = Rect.Empty; var currentRect = Rect.Empty;
@ -582,7 +590,7 @@ namespace Avalonia.Media.TextFormatting
continue; continue;
} }
if (currentPosition + currentRun.TextSourceLength <= firstTextSourceIndex) if (currentPosition + currentRun.TextSourceLength < firstTextSourceIndex)
{ {
startX -= currentRun.Size.Width; startX -= currentRun.Size.Width;
@ -601,20 +609,31 @@ namespace Avalonia.Media.TextFormatting
currentPosition += offset; currentPosition += offset;
var startIndex = currentRun.Text.Start + offset; var startIndex = currentRun.Text.Start + offset;
double startOffset;
double endOffset;
var endOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit( if (currentShapedRun.ShapedBuffer.IsLeftToRight)
currentShapedRun.ShapedBuffer.IsLeftToRight ? {
new CharacterHit(startIndex + remainingLength) : if (currentPosition < startIndex)
new CharacterHit(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( startOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit(new CharacterHit(startIndex + remainingLength));
currentShapedRun.ShapedBuffer.IsLeftToRight ? }
new CharacterHit(startIndex) :
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 endHit = currentShapedRun.GlyphRun.GetCharacterHitFromDistance(endOffset, out _);
var startHit = currentShapedRun.GlyphRun.GetCharacterHitFromDistance(startOffset, out _); var startHit = currentShapedRun.GlyphRun.GetCharacterHitFromDistance(startOffset, out _);
@ -652,41 +671,35 @@ namespace Avalonia.Media.TextFormatting
} }
var runWidth = endX - startX; 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); textBounds.TextRunBounds.Add(currentRunBounds);
} }
else else
{ {
currentRect = currentRunBounds.Rectangle; 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; currentWidth += runWidth;
currentPosition += characterLength; currentPosition += characterLength;
if (currentDirection == FlowDirection.LeftToRight) if (currentPosition > characterIndex)
{
if (currentPosition > characterIndex)
{
break;
}
}
else
{ {
if (currentPosition <= firstTextSourceIndex) break;
{
break;
}
} }
lastDirection = currentDirection; lastDirection = currentDirection;
@ -698,6 +711,8 @@ namespace Avalonia.Media.TextFormatting
} }
} }
result.Reverse();
return result; return result;
} }
@ -1302,8 +1317,14 @@ namespace Avalonia.Media.TextFormatting
switch (textAlignment) switch (textAlignment)
{ {
case TextAlignment.Center: 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: case TextAlignment.Right:
return Math.Max(0, _paragraphWidth - widthIncludingTrailingWhitespace); return Math.Max(0, _paragraphWidth - widthIncludingTrailingWhitespace);

4
src/Markup/Avalonia.Markup.Xaml/IAddChild.cs → src/Avalonia.Base/Metadata/IAddChild.cs

@ -1,11 +1,11 @@
namespace Avalonia.Markup.Xaml namespace Avalonia.Metadata
{ {
public interface IAddChild public interface IAddChild
{ {
void AddChild(object child); void AddChild(object child);
} }
public interface IAddChild<T> : IAddChild public interface IAddChild<T>
{ {
void AddChild(T child); void AddChild(T child);
} }

4
src/Avalonia.Base/Platform/IGeometryImpl.cs

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

5
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.Expressions;
using Avalonia.Rendering.Composition.Server; 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> /// <summary>
/// The base class for both key-frame and expression animation instances /// The base class for both key-frame and expression animation instances
@ -79,4 +80,4 @@ internal abstract class AnimationInstanceBase : IAnimationInstance
_invalidated = true; _invalidated = true;
TargetObject.NotifyAnimatedValueChanged(Property); TargetObject.NotifyAnimatedValueChanged(Property);
} }
} }

4
src/Avalonia.Base/Rendering/Composition/Animations/CompositionAnimation.cs

@ -8,6 +8,8 @@ using Avalonia.Rendering.Composition.Expressions;
using Avalonia.Rendering.Composition.Server; using Avalonia.Rendering.Composition.Server;
using Avalonia.Rendering.Composition.Transport; 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 namespace Avalonia.Rendering.Composition.Animations
{ {
/// <summary> /// <summary>
@ -72,4 +74,4 @@ namespace Avalonia.Rendering.Composition.Animations
} }
} }
} }

4
src/Avalonia.Base/Rendering/Composition/Animations/CompositionAnimationGroup.cs

@ -3,6 +3,8 @@ using System.Collections.Generic;
using Avalonia.Rendering.Composition.Transport; 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 namespace Avalonia.Rendering.Composition.Animations
{ {
public class CompositionAnimationGroup : CompositionObject, ICompositionAnimationBase public class CompositionAnimationGroup : CompositionObject, ICompositionAnimationBase
@ -21,4 +23,4 @@ namespace Avalonia.Rendering.Composition.Animations
{ {
} }
} }
} }

4
src/Avalonia.Base/Rendering/Composition/Animations/ExpressionAnimation.cs

@ -3,6 +3,8 @@ using System;
using Avalonia.Rendering.Composition.Expressions; using Avalonia.Rendering.Composition.Expressions;
using Avalonia.Rendering.Composition.Server; 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 namespace Avalonia.Rendering.Composition.Animations
{ {
/// <summary> /// <summary>
@ -50,4 +52,4 @@ namespace Avalonia.Rendering.Composition.Animations
=> new ExpressionAnimationInstance(ParsedExpression, => new ExpressionAnimationInstance(ParsedExpression,
targetObject, finalValue, CreateSnapshot()); targetObject, finalValue, CreateSnapshot());
} }
} }

4
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.Expressions;
using Avalonia.Rendering.Composition.Server; 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 namespace Avalonia.Rendering.Composition.Animations
{ {
@ -46,4 +48,4 @@ namespace Avalonia.Rendering.Composition.Animations
_finalValue = finalValue; _finalValue = finalValue;
} }
} }
} }

4
src/Avalonia.Base/Rendering/Composition/Animations/IAnimationInstance.cs

@ -2,6 +2,8 @@ using System;
using Avalonia.Rendering.Composition.Expressions; using Avalonia.Rendering.Composition.Expressions;
using Avalonia.Rendering.Composition.Server; 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 namespace Avalonia.Rendering.Composition.Animations
{ {
internal interface IAnimationInstance internal interface IAnimationInstance
@ -13,4 +15,4 @@ namespace Avalonia.Rendering.Composition.Animations
void Deactivate(); void Deactivate();
void Invalidate(); void Invalidate();
} }
} }

4
src/Avalonia.Base/Rendering/Composition/Animations/ICompositionAnimationBase.cs

@ -2,6 +2,8 @@
using Avalonia.Rendering.Composition.Server; 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 namespace Avalonia.Rendering.Composition.Animations
{ {
/// <summary> /// <summary>
@ -12,4 +14,4 @@ namespace Avalonia.Rendering.Composition.Animations
internal void InternalOnly(); internal void InternalOnly();
} }
} }

4
src/Avalonia.Base/Rendering/Composition/Animations/ImplicitAnimationCollection.cs

@ -4,6 +4,8 @@ using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using Avalonia.Rendering.Composition.Transport; 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 namespace Avalonia.Rendering.Composition.Animations
{ {
/// <summary> /// <summary>
@ -79,4 +81,4 @@ namespace Avalonia.Rendering.Composition.Animations
return rv; return rv;
} }
} }
} }

4
src/Avalonia.Base/Rendering/Composition/Animations/Interpolators.cs

@ -1,6 +1,8 @@
using System; using System;
using System.Numerics; 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 namespace Avalonia.Rendering.Composition.Animations
{ {
/// <summary> /// <summary>
@ -73,4 +75,4 @@ namespace Avalonia.Rendering.Composition.Animations
public static BooleanInterpolator Instance { get; } = new BooleanInterpolator(); public static BooleanInterpolator Instance { get; } = new BooleanInterpolator();
} }
} }

4
src/Avalonia.Base/Rendering/Composition/Animations/KeyFrameAnimation.cs

@ -2,6 +2,8 @@ using System;
using Avalonia.Animation; using Avalonia.Animation;
using Avalonia.Animation.Easings; 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 namespace Avalonia.Rendering.Composition.Animations
{ {
@ -131,4 +133,4 @@ namespace Avalonia.Rendering.Composition.Animations
/// </summary> /// </summary>
SetToFinalValue SetToFinalValue
} }
} }

4
src/Avalonia.Base/Rendering/Composition/Animations/KeyFrameAnimationInstance.cs

@ -4,6 +4,8 @@ using Avalonia.Animation;
using Avalonia.Rendering.Composition.Expressions; using Avalonia.Rendering.Composition.Expressions;
using Avalonia.Rendering.Composition.Server; 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 namespace Avalonia.Rendering.Composition.Animations
{ {
/// <summary> /// <summary>
@ -175,4 +177,4 @@ namespace Avalonia.Rendering.Composition.Animations
base.Deactivate(); base.Deactivate();
} }
} }
} }

4
src/Avalonia.Base/Rendering/Composition/Animations/KeyFrames.cs

@ -3,6 +3,8 @@ using System.Collections.Generic;
using Avalonia.Animation.Easings; using Avalonia.Animation.Easings;
using Avalonia.Rendering.Composition.Expressions; 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 namespace Avalonia.Rendering.Composition.Animations
{ {
@ -86,4 +88,4 @@ namespace Avalonia.Rendering.Composition.Animations
{ {
public void InsertExpressionKeyFrame(float normalizedProgressKey, string value, IEasing easingFunction); public void InsertExpressionKeyFrame(float normalizedProgressKey, string value, IEasing easingFunction);
} }
} }

4
src/Avalonia.Base/Rendering/Composition/Animations/PropertySetSnapshot.cs

@ -1,6 +1,8 @@
using System.Collections.Generic; using System.Collections.Generic;
using Avalonia.Rendering.Composition.Expressions; 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 namespace Avalonia.Rendering.Composition.Animations
{ {
/// <summary> /// <summary>
@ -46,4 +48,4 @@ namespace Avalonia.Rendering.Composition.Animations
public ExpressionVariant GetProperty(string name) => GetParameter(name); public ExpressionVariant GetProperty(string name) => GetParameter(name);
} }
} }

2
src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs

@ -11,6 +11,8 @@ using Avalonia.Rendering.Composition.Server;
using Avalonia.Threading; using Avalonia.Threading;
using Avalonia.VisualTree; 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; namespace Avalonia.Rendering.Composition;
/// <summary> /// <summary>

4
src/Avalonia.Base/Rendering/Composition/CompositionDrawListVisual.cs

@ -5,6 +5,8 @@ using Avalonia.Rendering.Composition.Server;
using Avalonia.Rendering.Composition.Transport; using Avalonia.Rendering.Composition.Transport;
using Avalonia.VisualTree; 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; namespace Avalonia.Rendering.Composition;
@ -72,4 +74,4 @@ internal class CompositionDrawListVisual : CompositionContainerVisual
return true; return true;
return false; return false;
} }
} }

4
src/Avalonia.Base/Rendering/Composition/CompositionObject.cs

@ -5,6 +5,8 @@ using Avalonia.Rendering.Composition.Server;
using Avalonia.Rendering.Composition.Transport; using Avalonia.Rendering.Composition.Transport;
using Avalonia.Utilities; 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 namespace Avalonia.Rendering.Composition
{ {
/// <summary> /// <summary>
@ -138,4 +140,4 @@ namespace Avalonia.Rendering.Composition
writer.Write((byte)(IsDisposed ? 1 : 0)); writer.Write((byte)(IsDisposed ? 1 : 0));
} }
} }
} }

4
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.Expressions;
using Avalonia.Rendering.Composition.Transport; 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 namespace Avalonia.Rendering.Composition
{ {
/// <summary> /// <summary>
@ -144,4 +146,4 @@ namespace Avalonia.Rendering.Composition
TypeMismatch, TypeMismatch,
NotFound NotFound
} }
} }

4
src/Avalonia.Base/Rendering/Composition/CompositionTarget.cs

@ -4,6 +4,8 @@ using System.Numerics;
using Avalonia.Collections.Pooled; using Avalonia.Collections.Pooled;
using Avalonia.VisualTree; 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 namespace Avalonia.Rendering.Composition
{ {
/// <summary> /// <summary>
@ -127,4 +129,4 @@ namespace Avalonia.Rendering.Composition
Compositor.Server.Render(); Compositor.Server.Render();
} }
} }
} }

4
src/Avalonia.Base/Rendering/Composition/Compositor.cs

@ -12,6 +12,8 @@ using Avalonia.Rendering.Composition.Transport;
using Avalonia.Threading; 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 namespace Avalonia.Rendering.Composition
{ {
/// <summary> /// <summary>
@ -138,4 +140,4 @@ namespace Avalonia.Rendering.Composition
_invokeOnNextCommit.Add(action); _invokeOnNextCommit.Add(action);
} }
} }
} }

4
src/Avalonia.Base/Rendering/Composition/ContainerVisual.cs

@ -1,5 +1,7 @@
using Avalonia.Rendering.Composition.Server; 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 namespace Avalonia.Rendering.Composition
{ {
/// <summary> /// <summary>
@ -21,4 +23,4 @@ namespace Avalonia.Rendering.Composition
base.OnRootChangedCore(); base.OnRootChangedCore();
} }
} }
} }

4
src/Avalonia.Base/Rendering/Composition/Drawing/CompositionDrawList.cs

@ -4,6 +4,8 @@ using Avalonia.Rendering.Composition.Server;
using Avalonia.Rendering.SceneGraph; using Avalonia.Rendering.SceneGraph;
using Avalonia.Utilities; 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; namespace Avalonia.Rendering.Composition.Drawing;
/// <summary> /// <summary>
@ -99,4 +101,4 @@ internal class CompositionDrawListBuilder
if (count < Count) if (count < Count)
_operations!.RemoveRange(count, _operations.Count - count); _operations!.RemoveRange(count, _operations.Count - count);
} }
} }

5
src/Avalonia.Base/Rendering/Composition/Drawing/CompositionDrawingContext.cs

@ -8,6 +8,9 @@ using Avalonia.Rendering.Composition.Drawing;
using Avalonia.Rendering.SceneGraph; using Avalonia.Rendering.SceneGraph;
using Avalonia.Utilities; using Avalonia.Utilities;
using Avalonia.VisualTree; 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; namespace Avalonia.Rendering.Composition;
/// <summary> /// <summary>
@ -388,4 +391,4 @@ internal class CompositionDrawingContext : IDrawingContextImpl, IDrawingContextW
} }
return null; return null;
} }
} }

4
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; namespace Avalonia.Rendering.Composition;
/// <summary> /// <summary>
@ -11,4 +13,4 @@ public static class ElementComposition
/// <param name="visual"></param> /// <param name="visual"></param>
/// <returns></returns> /// <returns></returns>
public static CompositionVisual? GetElementVisual(Visual visual) => visual.CompositionVisual; public static CompositionVisual? GetElementVisual(Visual visual) => visual.CompositionVisual;
} }

4
src/Avalonia.Base/Rendering/Composition/Enums.cs

@ -1,5 +1,7 @@
using System; 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 namespace Avalonia.Rendering.Composition
{ {
public enum CompositionBlendMode public enum CompositionBlendMode
@ -117,4 +119,4 @@ namespace Avalonia.Rendering.Composition
Fill = 1, Fill = 1,
//TODO: Uniform, UniformToFill //TODO: Uniform, UniformToFill
} }
} }

4
src/Avalonia.Base/Rendering/Composition/Expressions/BuiltInExpressionFfi.cs

@ -4,6 +4,8 @@ using System.Numerics;
using Avalonia.Rendering.Composition.Animations; using Avalonia.Rendering.Composition.Animations;
using Avalonia.Utilities; 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 namespace Avalonia.Rendering.Composition.Expressions
{ {
/// <summary> /// <summary>
@ -234,4 +236,4 @@ namespace Avalonia.Rendering.Composition.Expressions
public static BuiltInExpressionFfi Instance { get; } = new BuiltInExpressionFfi(); public static BuiltInExpressionFfi Instance { get; } = new BuiltInExpressionFfi();
} }
} }

4
src/Avalonia.Base/Rendering/Composition/Expressions/DelegateExpressionFfi.cs

@ -5,6 +5,8 @@ using System.Linq;
using System.Numerics; using System.Numerics;
using Avalonia.Media; 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 namespace Avalonia.Rendering.Composition.Expressions
{ {
/// <summary> /// <summary>
@ -181,4 +183,4 @@ namespace Avalonia.Rendering.Composition.Expressions
); );
} }
} }
} }

4
src/Avalonia.Base/Rendering/Composition/Expressions/Expression.cs

@ -4,6 +4,8 @@ using System.Globalization;
using System.Reflection; using System.Reflection;
using Avalonia.Rendering.Composition.Server; 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 namespace Avalonia.Rendering.Composition.Expressions
{ {
/// <summary> /// <summary>
@ -374,4 +376,4 @@ namespace Avalonia.Rendering.Composition.Expressions
} }

4
src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionEvaluationContext.cs

@ -1,6 +1,8 @@
using System.Collections.Generic; using System.Collections.Generic;
using Avalonia.Rendering.Composition.Server; 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 namespace Avalonia.Rendering.Composition.Expressions
{ {
internal struct ExpressionEvaluationContext internal struct ExpressionEvaluationContext
@ -29,4 +31,4 @@ namespace Avalonia.Rendering.Composition.Expressions
{ {
bool Call(string name, IReadOnlyList<ExpressionVariant> arguments, out ExpressionVariant result); bool Call(string name, IReadOnlyList<ExpressionVariant> arguments, out ExpressionVariant result);
} }
} }

4
src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionParseException.cs

@ -1,5 +1,7 @@
using System; 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 namespace Avalonia.Rendering.Composition.Expressions
{ {
internal class ExpressionParseException : Exception internal class ExpressionParseException : Exception
@ -11,4 +13,4 @@ namespace Avalonia.Rendering.Composition.Expressions
Position = position; Position = position;
} }
} }
} }

4
src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionParser.cs

@ -5,6 +5,8 @@ using System.Linq;
// ReSharper disable StringLiteralTypo // 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 namespace Avalonia.Rendering.Composition.Expressions
{ {
internal class ExpressionParser internal class ExpressionParser
@ -295,4 +297,4 @@ namespace Avalonia.Rendering.Composition.Expressions
} }
} }
} }

4
src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionTrackedValues.cs

@ -2,6 +2,8 @@ using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using Avalonia.Rendering.Composition.Server; 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; namespace Avalonia.Rendering.Composition.Expressions;
internal class ExpressionTrackedObjects : IEnumerable<IExpressionObject> internal class ExpressionTrackedObjects : IEnumerable<IExpressionObject>
@ -54,4 +56,4 @@ internal class ExpressionTrackedObjects : IEnumerable<IExpressionObject>
_stack.Push(obj); _stack.Push(obj);
} }
} }
} }

4
src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionVariant.cs

@ -4,6 +4,8 @@ using System.Numerics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Avalonia.Media; 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 namespace Avalonia.Rendering.Composition.Expressions
{ {
internal enum VariantType internal enum VariantType
@ -727,4 +729,4 @@ namespace Avalonia.Rendering.Composition.Expressions
} }
} }
} }

4
src/Avalonia.Base/Rendering/Composition/Expressions/TokenParser.cs

@ -1,6 +1,8 @@
using System; using System;
using System.Globalization; 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 namespace Avalonia.Rendering.Composition.Expressions
{ {
/// <summary> /// <summary>
@ -256,4 +258,4 @@ namespace Avalonia.Rendering.Composition.Expressions
public override string ToString() => _s.ToString(); public override string ToString() => _s.ToString();
} }
} }

4
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; namespace Avalonia.Rendering.Composition;
internal interface ICompositionTargetDebugEvents internal interface ICompositionTargetDebugEvents
{ {
void RectInvalidated(Rect rc); void RectInvalidated(Rect rc);
} }

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

4
src/Avalonia.Base/Rendering/Composition/MatrixUtils.cs

@ -1,5 +1,7 @@
using System.Numerics; 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 namespace Avalonia.Rendering.Composition
{ {
static class MatrixUtils static class MatrixUtils
@ -63,4 +65,4 @@ namespace Avalonia.Rendering.Composition
matrix44.M42, matrix44.M42,
matrix44.M44); matrix44.M44);
} }
} }

4
src/Avalonia.Base/Rendering/Composition/Server/CompositionProperty.cs

@ -1,6 +1,8 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; 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; namespace Avalonia.Rendering.Composition.Server;
internal class CompositionProperty internal class CompositionProperty
@ -12,4 +14,4 @@ internal class CompositionProperty
{ {
Id = Interlocked.Increment(ref s_NextId) Id = Interlocked.Increment(ref s_NextId)
}; };
} }

4
src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs

@ -6,6 +6,8 @@ using Avalonia.Rendering.Composition.Drawing;
using Avalonia.Rendering.SceneGraph; using Avalonia.Rendering.SceneGraph;
using Avalonia.Utilities; 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; namespace Avalonia.Rendering.Composition.Server;
/// <summary> /// <summary>
@ -176,4 +178,4 @@ internal class CompositorDrawingContextProxy : IDrawingContextImpl, IDrawingCont
if (_impl is IDrawingContextWithAcrylicLikeSupport acrylic) if (_impl is IDrawingContextWithAcrylicLikeSupport acrylic)
acrylic.DrawRectangle(material, rect); acrylic.DrawRectangle(material, rect);
} }
} }

4
src/Avalonia.Base/Rendering/Composition/Server/FpsCounter.cs

@ -6,6 +6,8 @@ using Avalonia.Media.TextFormatting;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Utilities; 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; namespace Avalonia.Rendering.Composition.Server;
/// <summary> /// <summary>
@ -73,4 +75,4 @@ internal class FpsCounter
offset += run.Size.Width; offset += run.Size.Width;
} }
} }
} }

4
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 namespace Avalonia.Rendering.Composition.Server
{ {
/// <summary> /// <summary>
@ -43,4 +45,4 @@ namespace Avalonia.Rendering.Composition.Server
} }
} }
} }
} }

4
src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionContainerVisual.cs

@ -1,6 +1,8 @@
using System.Numerics; using System.Numerics;
using Avalonia.Platform; 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 namespace Avalonia.Rendering.Composition.Server
{ {
/// <summary> /// <summary>
@ -41,4 +43,4 @@ namespace Avalonia.Rendering.Composition.Server
Children = new ServerCompositionVisualCollection(Compositor); Children = new ServerCompositionVisualCollection(Compositor);
} }
} }
} }

4
src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionDrawListVisual.cs

@ -7,6 +7,8 @@ using Avalonia.Rendering.Composition.Transport;
using Avalonia.Rendering.SceneGraph; using Avalonia.Rendering.SceneGraph;
using Avalonia.Utilities; 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; namespace Avalonia.Rendering.Composition.Server;
/// <summary> /// <summary>
@ -72,4 +74,4 @@ internal class ServerCompositionDrawListVisual : ServerCompositionContainerVisua
return UiVisual.GetType().ToString(); return UiVisual.GetType().ToString();
} }
#endif #endif
} }

4
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 namespace Avalonia.Rendering.Composition.Server
{ {
internal abstract class ServerCompositionSurface : ServerObject internal abstract class ServerCompositionSurface : ServerObject
@ -6,4 +8,4 @@ namespace Avalonia.Rendering.Composition.Server
{ {
} }
} }
} }

4
src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs

@ -9,6 +9,8 @@ using Avalonia.Platform;
using Avalonia.Rendering.Composition.Transport; using Avalonia.Rendering.Composition.Transport;
using Avalonia.Utilities; 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 namespace Avalonia.Rendering.Composition.Server
{ {
/// <summary> /// <summary>
@ -218,4 +220,4 @@ namespace Avalonia.Rendering.Composition.Server
public void EnqueueAdornerUpdate(ServerCompositionVisual visual) => _adornerUpdateQueue.Enqueue(visual); public void EnqueueAdornerUpdate(ServerCompositionVisual visual) => _adornerUpdateQueue.Enqueue(visual);
} }
} }

4
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; namespace Avalonia.Rendering.Composition.Server;
partial class ServerCompositionVisual partial class ServerCompositionVisual
@ -73,4 +75,4 @@ partial class ServerCompositionVisual
|| offset == s_IdOfSizeProperty) || offset == s_IdOfSizeProperty)
_clipSizeDirty = true; _clipSizeDirty = true;
} }
} }

4
src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs

@ -6,6 +6,8 @@ using Avalonia.Rendering.Composition.Animations;
using Avalonia.Rendering.Composition.Transport; using Avalonia.Rendering.Composition.Transport;
using Avalonia.Utilities; 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 namespace Avalonia.Rendering.Composition.Server
{ {
/// <summary> /// <summary>
@ -234,4 +236,4 @@ namespace Avalonia.Rendering.Composition.Server
} }
} }

4
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.Expressions;
using Avalonia.Rendering.Composition.Transport; 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 namespace Avalonia.Rendering.Composition.Server
{ {
/// <summary> /// <summary>
@ -137,4 +139,4 @@ namespace Avalonia.Rendering.Composition.Server
public void RemoveFromClock(IAnimationInstance animationInstance) => public void RemoveFromClock(IAnimationInstance animationInstance) =>
_activeAnimations.Remove(animationInstance); _activeAnimations.Remove(animationInstance);
} }
} }

4
src/Avalonia.Base/Rendering/Composition/Server/ServerList.cs

@ -2,6 +2,8 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using Avalonia.Rendering.Composition.Transport; 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 namespace Avalonia.Rendering.Composition.Server
{ {
/// <summary> /// <summary>
@ -41,4 +43,4 @@ namespace Avalonia.Rendering.Composition.Server
{ {
} }
} }
} }

4
src/Avalonia.Base/Rendering/Composition/Server/ServerObject.cs

@ -7,6 +7,8 @@ using Avalonia.Rendering.Composition.Expressions;
using Avalonia.Rendering.Composition.Transport; using Avalonia.Rendering.Composition.Transport;
using Avalonia.Utilities; 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 namespace Avalonia.Rendering.Composition.Server
{ {
/// <summary> /// <summary>
@ -177,4 +179,4 @@ namespace Avalonia.Rendering.Composition.Server
ItselfLastChangedBy = batch.SequenceId; ItselfLastChangedBy = batch.SequenceId;
} }
} }
} }

4
src/Avalonia.Base/Rendering/Composition/Transport/Batch.cs

@ -4,6 +4,8 @@ using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; 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 namespace Avalonia.Rendering.Composition.Transport
{ {
/// <summary> /// <summary>
@ -36,4 +38,4 @@ namespace Avalonia.Rendering.Composition.Transport
public Task Completed => _tcs.Task; public Task Completed => _tcs.Task;
} }
} }

2
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.Animations;
using Avalonia.Rendering.Composition.Server; 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; namespace Avalonia.Rendering.Composition.Transport;
/// <summary> /// <summary>

4
src/Avalonia.Base/Rendering/Composition/Transport/BatchStreamArrayPool.cs

@ -5,6 +5,8 @@ using System.Runtime.InteropServices;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Threading; 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; namespace Avalonia.Rendering.Composition.Transport;
/// <summary> /// <summary>
@ -153,4 +155,4 @@ internal sealed class BatchStreamMemoryPool : BatchStreamPoolBase<IntPtr>
protected override IntPtr CreateItem() => Marshal.AllocHGlobal(BufferSize); protected override IntPtr CreateItem() => Marshal.AllocHGlobal(BufferSize);
protected override void DestroyItem(IntPtr item) => Marshal.FreeHGlobal(item); protected override void DestroyItem(IntPtr item) => Marshal.FreeHGlobal(item);
} }

4
src/Avalonia.Base/Rendering/Composition/Transport/BatchStreamDebugMarker.cs

@ -1,9 +1,11 @@
using System; 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; namespace Avalonia.Rendering.Composition.Transport;
internal class BatchStreamDebugMarkers internal class BatchStreamDebugMarkers
{ {
public static object ObjectEndMarker = new object(); public static object ObjectEndMarker = new object();
public static Guid ObjectEndMagic = Guid.NewGuid(); public static Guid ObjectEndMagic = Guid.NewGuid();
} }

4
src/Avalonia.Base/Rendering/Composition/Transport/ServerListProxyHelper.cs

@ -2,6 +2,8 @@ using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using Avalonia.Rendering.Composition.Server; 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 namespace Avalonia.Rendering.Composition.Transport
{ {
/// <summary> /// <summary>
@ -95,4 +97,4 @@ namespace Avalonia.Rendering.Composition.Transport
_changed = false; _changed = false;
} }
} }
} }

4
src/Avalonia.Base/Rendering/Composition/Visual.cs

@ -3,6 +3,8 @@ using System.Numerics;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.VisualTree; 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 namespace Avalonia.Rendering.Composition
{ {
/// <summary> /// <summary>
@ -53,4 +55,4 @@ namespace Avalonia.Rendering.Composition
internal virtual bool HitTest(Point point, Func<IVisual, bool>? filter) => true; internal virtual bool HitTest(Point point, Func<IVisual, bool>? filter) => true;
} }
} }

4
src/Avalonia.Base/Rendering/Composition/VisualCollection.cs

@ -1,6 +1,8 @@
using System; using System;
using Avalonia.Rendering.Composition.Server; 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 namespace Avalonia.Rendering.Composition
{ {
/// <summary> /// <summary>
@ -70,4 +72,4 @@ namespace Avalonia.Rendering.Composition
item.Parent = item; item.Parent = item;
} }
} }
} }

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

85
src/Avalonia.Base/StyledElement.cs

@ -12,8 +12,6 @@ using Avalonia.Logging;
using Avalonia.LogicalTree; using Avalonia.LogicalTree;
using Avalonia.Styling; using Avalonia.Styling;
#nullable enable
namespace Avalonia namespace Avalonia
{ {
/// <summary> /// <summary>
@ -55,7 +53,14 @@ namespace Avalonia
nameof(TemplatedParent), nameof(TemplatedParent),
o => o.TemplatedParent, o => o.TemplatedParent,
(o ,v) => o.TemplatedParent = v); (o ,v) => o.TemplatedParent = v);
/// <summary>
/// Defines the <see cref="Theme"/> property.
/// </summary>
public static readonly StyledProperty<ControlTheme?> ThemeProperty =
AvaloniaProperty.Register<StyledElement, ControlTheme?>(nameof(Theme));
private static readonly ControlTheme s_invalidTheme = new ControlTheme();
private int _initCount; private int _initCount;
private string? _name; private string? _name;
private readonly Classes _classes = new Classes(); private readonly Classes _classes = new Classes();
@ -67,6 +72,8 @@ namespace Avalonia
private List<IStyleInstance>? _appliedStyles; private List<IStyleInstance>? _appliedStyles;
private ITemplatedControl? _templatedParent; private ITemplatedControl? _templatedParent;
private bool _dataContextUpdating; private bool _dataContextUpdating;
private bool _hasPromotedTheme;
private ControlTheme? _implicitTheme;
/// <summary> /// <summary>
/// Initializes static members of the <see cref="StyledElement"/> class. /// Initializes static members of the <see cref="StyledElement"/> class.
@ -230,6 +237,15 @@ namespace Avalonia
internal set => SetAndRaise(TemplatedParentProperty, ref _templatedParent, value); internal set => SetAndRaise(TemplatedParentProperty, ref _templatedParent, value);
} }
/// <summary>
/// Gets or sets the theme to be applied to the element.
/// </summary>
public ControlTheme? Theme
{
get => GetValue(ThemeProperty);
set => SetValue(ThemeProperty, value);
}
/// <summary> /// <summary>
/// Gets the styled element's logical children. /// Gets the styled element's logical children.
/// </summary> /// </summary>
@ -302,6 +318,7 @@ namespace Avalonia
/// <inheritdoc/> /// <inheritdoc/>
IStyleHost? IStyleHost.StylingParent => (IStyleHost?)InheritanceParent; IStyleHost? IStyleHost.StylingParent => (IStyleHost?)InheritanceParent;
/// <inheritdoc/> /// <inheritdoc/>
public virtual void BeginInit() public virtual void BeginInit()
{ {
@ -341,10 +358,15 @@ namespace Avalonia
} }
finally finally
{ {
_styled = true;
EndBatchUpdate(); EndBatchUpdate();
} }
_styled = true; if (_hasPromotedTheme)
{
_hasPromotedTheme = false;
ClearValue(ThemeProperty);
}
} }
return _styled; return _styled;
@ -475,6 +497,31 @@ namespace Avalonia
}; };
} }
ControlTheme? IStyleable.GetEffectiveTheme()
{
var theme = Theme;
// Explitly set Theme property takes precedence.
if (theme is not null)
return theme;
// If the Theme property is not set, try to find a ControlTheme resource with our StyleKey.
if (_implicitTheme is null)
{
var key = ((IStyleable)this).StyleKey;
if (this.TryFindResource(key, out var value) && value is ControlTheme t)
_implicitTheme = t;
else
_implicitTheme = s_invalidTheme;
}
if (_implicitTheme != s_invalidTheme)
return _implicitTheme;
return null;
}
void IStyleable.StyleApplied(IStyleInstance instance) void IStyleable.StyleApplied(IStyleInstance instance)
{ {
instance = instance ?? throw new ArgumentNullException(nameof(instance)); instance = instance ?? throw new ArgumentNullException(nameof(instance));
@ -590,6 +637,30 @@ namespace Avalonia
{ {
} }
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
if (change.Property == ThemeProperty)
{
// Changing the theme detaches all styles, meaning that if the theme property was
// set via a style, it will get cleared! To work around this, if the value was
// applied at less than local value priority then promote the value to local value
// priority until styling is re-applied.
if (change.Priority > BindingPriority.LocalValue)
{
Theme = change.GetNewValue<ControlTheme?>();
_hasPromotedTheme = true;
}
else if (_hasPromotedTheme && change.Priority == BindingPriority.LocalValue)
{
_hasPromotedTheme = false;
}
InvalidateStyles();
}
}
private static void DataContextNotifying(IAvaloniaObject o, bool updateStarted) private static void DataContextNotifying(IAvaloniaObject o, bool updateStarted)
{ {
if (o is StyledElement element) if (o is StyledElement element)
@ -696,6 +767,7 @@ namespace Avalonia
if (_logicalRoot != null) if (_logicalRoot != null)
{ {
_logicalRoot = null; _logicalRoot = null;
_implicitTheme = null;
DetachStyles(); DetachStyles();
OnDetachedFromLogicalTree(e); OnDetachedFromLogicalTree(e);
DetachedFromLogicalTree?.Invoke(this, e); DetachedFromLogicalTree?.Invoke(this, e);
@ -760,7 +832,7 @@ namespace Avalonia
private void DetachStyles() private void DetachStyles()
{ {
if (_appliedStyles is object) if (_appliedStyles?.Count > 0)
{ {
BeginBatchUpdate(); BeginBatchUpdate();
@ -801,9 +873,12 @@ namespace Avalonia
if (applied.Source == styles[i]) if (applied.Source == styles[i])
{ {
applied.Dispose();
_appliedStyles.RemoveAt(j); _appliedStyles.RemoveAt(j);
applied.Dispose();
} }
if (j > _appliedStyles.Count)
j = _appliedStyles.Count;
} }
} }
} }

2
src/Avalonia.Base/Styling/ChildSelector.cs

@ -65,6 +65,6 @@ namespace Avalonia.Styling
} }
protected override Selector? MovePrevious() => null; protected override Selector? MovePrevious() => null;
internal override bool HasValidNestingSelector() => _parent.HasValidNestingSelector(); protected override Selector? MovePreviousOrParent() => _parent;
} }
} }

67
src/Avalonia.Base/Styling/ControlTheme.cs

@ -0,0 +1,67 @@
using System;
namespace Avalonia.Styling
{
/// <summary>
/// Defines a switchable theme for a control.
/// </summary>
public class ControlTheme : StyleBase
{
/// <summary>
/// Initializes a new instance of the <see cref="ControlTheme"/> class.
/// </summary>
public ControlTheme() { }
/// <summary>
/// Initializes a new instance of the <see cref="ControlTheme"/> class.
/// </summary>
/// <param name="targetType">The value for <see cref="TargetType"/>.</param>
public ControlTheme(Type targetType) => TargetType = targetType;
/// <summary>
/// Gets or sets the type for which this control theme is intended.
/// </summary>
public Type? TargetType { get; set; }
/// <summary>
/// Gets or sets a control theme that is the basis of the current theme.
/// </summary>
public ControlTheme? BasedOn { get; set; }
public override SelectorMatchResult TryAttach(IStyleable target, object? host)
{
_ = target ?? throw new ArgumentNullException(nameof(target));
if (TargetType is null)
throw new InvalidOperationException("ControlTheme has no TargetType.");
var result = BasedOn?.TryAttach(target, host) ?? SelectorMatchResult.NeverThisType;
if (HasSettersOrAnimations && TargetType.IsAssignableFrom(target.StyleKey))
{
Attach(target, null);
result = SelectorMatchResult.AlwaysThisType;
}
var childResult = TryAttachChildren(target, host);
if (childResult > result)
result = childResult;
return result;
}
public override string ToString()
{
if (TargetType is not null)
return "ControlTheme: " + TargetType.Name;
else
return "ControlTheme";
}
internal override void SetParent(StyleBase? parent)
{
throw new InvalidOperationException("ControlThemes cannot be added as a nested style.");
}
}
}

2
src/Avalonia.Base/Styling/DescendentSelector.cs

@ -70,6 +70,6 @@ namespace Avalonia.Styling
} }
protected override Selector? MovePrevious() => null; protected override Selector? MovePrevious() => null;
internal override bool HasValidNestingSelector() => _parent.HasValidNestingSelector(); protected override Selector? MovePreviousOrParent() => _parent;
} }
} }

2
src/Avalonia.Base/Styling/IStyle.cs

@ -23,6 +23,6 @@ namespace Avalonia.Styling
/// <returns> /// <returns>
/// A <see cref="SelectorMatchResult"/> describing how the style matches the control. /// A <see cref="SelectorMatchResult"/> describing how the style matches the control.
/// </returns> /// </returns>
SelectorMatchResult TryAttach(IStyleable target, IStyleHost? host); SelectorMatchResult TryAttach(IStyleable target, object? host);
} }
} }

7
src/Avalonia.Base/Styling/IStyleable.cs

@ -3,8 +3,6 @@ using System.Collections.Generic;
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Metadata; using Avalonia.Metadata;
#nullable enable
namespace Avalonia.Styling namespace Avalonia.Styling
{ {
/// <summary> /// <summary>
@ -28,6 +26,11 @@ namespace Avalonia.Styling
/// </summary> /// </summary>
ITemplatedControl? TemplatedParent { get; } ITemplatedControl? TemplatedParent { get; }
/// <summary>
/// Gets the effective theme for the control as used by the syling system.
/// </summary>
ControlTheme? GetEffectiveTheme();
/// <summary> /// <summary>
/// Notifies the element that a style has been applied. /// Notifies the element that a style has been applied.
/// </summary> /// </summary>

14
src/Avalonia.Base/Styling/NestingSelector.cs

@ -15,9 +15,17 @@ namespace Avalonia.Styling
protected override SelectorMatch Evaluate(IStyleable control, IStyle? parent, bool subscribe) protected override SelectorMatch Evaluate(IStyleable control, IStyle? parent, bool subscribe)
{ {
if (parent is Style s && s.Selector is Selector selector) if (parent is Style s && s.Selector is not null)
{ {
return selector.Match(control, (parent as Style)?.Parent, subscribe); return s.Selector.Match(control, s.Parent, subscribe);
}
else if (parent is ControlTheme theme)
{
if (theme.TargetType is null)
throw new InvalidOperationException("ControlTheme has no TargetType.");
return theme.TargetType.IsAssignableFrom(control.StyleKey) ?
SelectorMatch.AlwaysThisType :
SelectorMatch.NeverThisType;
} }
throw new InvalidOperationException( throw new InvalidOperationException(
@ -25,6 +33,6 @@ namespace Avalonia.Styling
} }
protected override Selector? MovePrevious() => null; protected override Selector? MovePrevious() => null;
internal override bool HasValidNestingSelector() => true; protected override Selector? MovePreviousOrParent() => null;
} }
} }

2
src/Avalonia.Base/Styling/NotSelector.cs

@ -67,6 +67,6 @@ namespace Avalonia.Styling
} }
protected override Selector? MovePrevious() => _previous; protected override Selector? MovePrevious() => _previous;
internal override bool HasValidNestingSelector() => _argument.HasValidNestingSelector(); protected override Selector? MovePreviousOrParent() => _previous;
} }
} }

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

Loading…
Cancel
Save