Browse Source

Implement embedded runs

Improve text edit navigation
Implement IncrementalTabWidth
pull/7746/head
Benedikt Stebner 4 years ago
parent
commit
9250d93407
  1. 3
      samples/ControlCatalog/Pages/PointersPage.cs
  2. 3
      samples/RenderDemo/MainWindow.xaml
  3. 7
      samples/RenderDemo/Pages/TextFormatterPage.axaml
  4. 118
      samples/RenderDemo/Pages/TextFormatterPage.axaml.cs
  5. 4
      samples/RenderDemo/RenderDemo.csproj
  6. 18
      samples/SampleControls/HamburgerMenu/HamburgerMenu.xaml
  7. 3
      src/Avalonia.Controls.DataGrid/DataGridTextColumn.cs
  8. 2
      src/Avalonia.Controls.DataGrid/Themes/Default.xaml
  9. 2
      src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml
  10. 22
      src/Avalonia.Controls/ApiCompatBaseline.txt
  11. 114
      src/Avalonia.Controls/Control.cs
  12. 167
      src/Avalonia.Controls/Documents/TextElement.cs
  13. 89
      src/Avalonia.Controls/Presenters/TextPresenter.cs
  14. 75
      src/Avalonia.Controls/Primitives/TemplatedControl.cs
  15. 240
      src/Avalonia.Controls/TextBlock.cs
  16. 109
      src/Avalonia.Controls/TextBox.cs
  17. 1
      src/Avalonia.Controls/Window.cs
  18. 4
      src/Avalonia.Diagnostics/Diagnostics/Views/ConsoleView.xaml
  19. 2
      src/Avalonia.Diagnostics/Diagnostics/Views/LayoutExplorerView.axaml
  20. 7
      src/Avalonia.Headless/HeadlessPlatformStubs.cs
  21. 4
      src/Avalonia.Themes.Default/Controls/Button.xaml
  22. 4
      src/Avalonia.Themes.Default/Controls/CaptionButtons.xaml
  23. 4
      src/Avalonia.Themes.Default/Controls/CheckBox.xaml
  24. 22
      src/Avalonia.Themes.Default/Controls/DatePicker.xaml
  25. 4
      src/Avalonia.Themes.Default/Controls/RepeatButton.xaml
  26. 46
      src/Avalonia.Themes.Default/Controls/SplitButton.xaml
  27. 16
      src/Avalonia.Themes.Default/Controls/TimePicker.xaml
  28. 2
      src/Avalonia.Themes.Default/Controls/ToggleButton.xaml
  29. 16
      src/Avalonia.Themes.Fluent/Controls/Button.xaml
  30. 6
      src/Avalonia.Themes.Fluent/Controls/CalendarItem.xaml
  31. 2
      src/Avalonia.Themes.Fluent/Controls/CaptionButtons.xaml
  32. 24
      src/Avalonia.Themes.Fluent/Controls/CheckBox.xaml
  33. 28
      src/Avalonia.Themes.Fluent/Controls/ComboBox.xaml
  34. 18
      src/Avalonia.Themes.Fluent/Controls/ComboBoxItem.xaml
  35. 2
      src/Avalonia.Themes.Fluent/Controls/DataValidationErrors.xaml
  36. 22
      src/Avalonia.Themes.Fluent/Controls/DatePicker.xaml
  37. 2
      src/Avalonia.Themes.Fluent/Controls/Expander.xaml
  38. 2
      src/Avalonia.Themes.Fluent/Controls/ListBox.xaml
  39. 14
      src/Avalonia.Themes.Fluent/Controls/ListBoxItem.xaml
  40. 16
      src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml
  41. 10
      src/Avalonia.Themes.Fluent/Controls/RadioButton.xaml
  42. 8
      src/Avalonia.Themes.Fluent/Controls/RepeatButton.xaml
  43. 12
      src/Avalonia.Themes.Fluent/Controls/Slider.xaml
  44. 46
      src/Avalonia.Themes.Fluent/Controls/SplitButton.xaml
  45. 20
      src/Avalonia.Themes.Fluent/Controls/TabItem.xaml
  46. 20
      src/Avalonia.Themes.Fluent/Controls/TabStripItem.xaml
  47. 16
      src/Avalonia.Themes.Fluent/Controls/TimePicker.xaml
  48. 24
      src/Avalonia.Themes.Fluent/Controls/ToggleButton.xaml
  49. 16
      src/Avalonia.Themes.Fluent/Controls/TreeViewItem.xaml
  50. 2
      src/Avalonia.Themes.Fluent/Controls/WindowNotificationManager.xaml
  51. 12
      src/Avalonia.Visuals/ApiCompatBaseline.txt
  52. 4
      src/Avalonia.Visuals/Media/FormattedText.cs
  53. 10
      src/Avalonia.Visuals/Media/TextDecorationCollection.cs
  54. 5
      src/Avalonia.Visuals/Media/TextFormatting/DrawableTextRun.cs
  55. 2
      src/Avalonia.Visuals/Media/TextFormatting/FormattedTextSource.cs
  56. 2
      src/Avalonia.Visuals/Media/TextFormatting/ShapedTextCharacters.cs
  57. 29
      src/Avalonia.Visuals/Media/TextFormatting/TextBounds.cs
  58. 25
      src/Avalonia.Visuals/Media/TextFormatting/TextCharacters.cs
  59. 2
      src/Avalonia.Visuals/Media/TextFormatting/TextCollapsingProperties.cs
  60. 112
      src/Avalonia.Visuals/Media/TextFormatting/TextEllipsisHelper.cs
  61. 243
      src/Avalonia.Visuals/Media/TextFormatting/TextFormatterImpl.cs
  62. 175
      src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs
  63. 127
      src/Avalonia.Visuals/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs
  64. 8
      src/Avalonia.Visuals/Media/TextFormatting/TextLine.cs
  65. 8
      src/Avalonia.Visuals/Media/TextFormatting/TextLineBreak.cs
  66. 896
      src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs
  67. 8
      src/Avalonia.Visuals/Media/TextFormatting/TextParagraphProperties.cs
  68. 2
      src/Avalonia.Visuals/Media/TextFormatting/TextRunProperties.cs
  69. 5
      src/Avalonia.Visuals/Media/TextFormatting/TextShaper.cs
  70. 49
      src/Avalonia.Visuals/Media/TextFormatting/TextShaperOptions.cs
  71. 8
      src/Avalonia.Visuals/Media/TextFormatting/TextTrailingCharacterEllipsis.cs
  72. 8
      src/Avalonia.Visuals/Media/TextFormatting/TextTrailingWordEllipsis.cs
  73. 13
      src/Avalonia.Visuals/Platform/ITextShaperImpl.cs
  74. 19
      src/Skia/Avalonia.Skia/TextShaperImpl.cs
  75. 17
      src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs
  76. 29
      tests/Avalonia.Controls.UnitTests/TextBoxTests.cs
  77. 9
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs
  78. 9
      tests/Avalonia.Skia.UnitTests/Media/GlyphRunTests.cs
  79. 7
      tests/Avalonia.Skia.UnitTests/Media/TextFormatting/MultiBufferTextSource.cs
  80. 7
      tests/Avalonia.Skia.UnitTests/Media/TextFormatting/SingleBufferTextSource.cs
  81. 94
      tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs
  82. 197
      tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs
  83. 24
      tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextShaperTests.cs
  84. 9
      tests/Avalonia.UnitTests/HarfBuzzTextShaperImpl.cs
  85. 11
      tests/Avalonia.UnitTests/MockTextShaperImpl.cs

3
samples/ControlCatalog/Pages/PointersPage.cs

@ -7,6 +7,7 @@ using System.Runtime.InteropServices;
using System.Threading;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Documents;
using Avalonia.Input;
using Avalonia.Layout;
using Avalonia.Media;
@ -131,7 +132,7 @@ public class PointersPage : Decorator
{
public PointerIntermediatePointsTab()
{
this[TextBlock.ForegroundProperty] = Brushes.Black;
this[TextElement.ForegroundProperty] = Brushes.Black;
var slider = new Slider
{
Margin = new Thickness(5),

3
samples/RenderDemo/MainWindow.xaml

@ -60,6 +60,9 @@
<TabItem Header="FormattedText">
<pages:FormattedTextPage />
</TabItem>
<TabItem Header="TextFormatter">
<pages:TextFormatterPage />
</TabItem>
<TabItem Header="LineBounds">
<pages:LineBoundsPage />
</TabItem>

7
samples/RenderDemo/Pages/TextFormatterPage.axaml

@ -0,0 +1,7 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="RenderDemo.Pages.TextFormatterPage">
</UserControl>

118
samples/RenderDemo/Pages/TextFormatterPage.axaml.cs

@ -0,0 +1,118 @@
using System;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
using Avalonia.Media.TextFormatting;
namespace RenderDemo.Pages
{
public class TextFormatterPage : UserControl
{
private TextLine _textLine;
public TextFormatterPage()
{
this.InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
public override void Render(DrawingContext context)
{
_textLine?.Draw(context, new Point());
}
protected override Size MeasureOverride(Size availableSize)
{
var defaultRunProperties = new GenericTextRunProperties(Typeface.Default, foregroundBrush: Brushes.Black,
baselineAlignment: BaselineAlignment.Center);
var paragraphProperties = new GenericTextParagraphProperties(defaultRunProperties);
var control = new Button { Content = new TextBlock { Text = "ClickMe" } };
Content = control;
var textSource = new CustomTextSource(control, defaultRunProperties);
control.Measure(Size.Infinity);
_textLine =
TextFormatter.Current.FormatLine(textSource, 0, double.PositiveInfinity, paragraphProperties);
return base.MeasureOverride(availableSize);
}
protected override Size ArrangeOverride(Size finalSize)
{
var currentX = 0d;
foreach (var textRun in _textLine.TextRuns)
{
if (textRun is ControlRun controlRun)
{
controlRun.Control.Arrange(new Rect(new Point(currentX, 0), controlRun.Size));
}
if (textRun is DrawableTextRun drawableTextRun)
{
currentX += drawableTextRun.Size.Width;
}
}
return finalSize;
}
private class CustomTextSource : ITextSource
{
private readonly Control _control;
private readonly TextRunProperties _defaultProperties;
private readonly string _text = "<-Hello World->";
public CustomTextSource(Control control, TextRunProperties defaultProperties)
{
_control = control;
_defaultProperties = defaultProperties;
}
public TextRun? GetTextRun(int textSourceIndex)
{
if (textSourceIndex >= _text.Length * 2 + TextRun.DefaultTextSourceLength)
{
return null;
}
if (textSourceIndex == _text.Length)
{
return new ControlRun(_control, _defaultProperties);
}
return new TextCharacters(_text.AsMemory(), _defaultProperties);
}
}
private class ControlRun : DrawableTextRun
{
private readonly Control _control;
public ControlRun(Control control, TextRunProperties properties)
{
_control = control;
Properties = properties;
}
public Control Control => _control;
public override Size Size => _control.DesiredSize;
public override double Baseline => 0;
public override TextRunProperties? Properties { get; }
public override void Draw(DrawingContext drawingContext, Point origin)
{
// noop
}
}
}
}

4
samples/RenderDemo/RenderDemo.csproj

@ -5,6 +5,10 @@
</PropertyGroup>
<ItemGroup>
<Compile Include="..\..\src\Avalonia.Visuals\Rendering\SceneGraph\LineBoundsHelper.cs" Link="LineBoundsHelper.cs" />
<Compile Update="Pages\TextFormatterPage.axaml.cs">
<DependentUpon>TextFormatterPage.axaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />

18
samples/SampleControls/HamburgerMenu/HamburgerMenu.xaml

@ -195,9 +195,9 @@
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
TextBlock.FontFamily="{TemplateBinding FontFamily}"
TextBlock.FontSize="{TemplateBinding FontSize}"
TextBlock.FontWeight="{TemplateBinding FontWeight}" />
TextElement.FontFamily="{TemplateBinding FontFamily}"
TextElement.FontSize="{TemplateBinding FontSize}"
TextElement.FontWeight="{TemplateBinding FontWeight}" />
</Panel>
</Border>
</ControlTemplate>
@ -216,25 +216,25 @@
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
CornerRadius="{TemplateBinding CornerRadius}"
TextBlock.FontFamily="{TemplateBinding FontFamily}"
TextBlock.FontSize="{TemplateBinding FontSize}"
TextBlock.FontWeight="{TemplateBinding FontWeight}" />
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="TextBlock.Foreground" Value="{DynamicResource SystemBaseHighColor}" />
<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="TextBlock.Foreground" Value="{DynamicResource TabItemHeaderForegroundUnselectedPointerOver}" />
<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="TextBlock.Foreground" Value="{DynamicResource TabItemHeaderForegroundUnselectedPressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource TabItemHeaderForegroundUnselectedPressed}" />
</Style>
<Style Selector=":is(Button).NavigationButton:pressed">
<Setter Property="RenderTransform" Value="none" />

3
src/Avalonia.Controls.DataGrid/DataGridTextColumn.cs

@ -10,6 +10,7 @@ using System;
using System.ComponentModel;
using Avalonia.Layout;
using Avalonia.Markup.Xaml.MarkupExtensions;
using Avalonia.Controls.Documents;
namespace Avalonia.Controls
{
@ -94,7 +95,7 @@ namespace Avalonia.Controls
/// Identifies the Foreground dependency property.
/// </summary>
public static readonly AttachedProperty<IBrush> ForegroundProperty =
TextBlock.ForegroundProperty.AddOwner<DataGridTextColumn>();
TextElement.ForegroundProperty.AddOwner<DataGridTextColumn>();
/// <summary>
/// Gets or sets a brush that describes the foreground of the column cells.

2
src/Avalonia.Controls.DataGrid/Themes/Default.xaml

@ -17,7 +17,7 @@
<ContentPresenter ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
Margin="{TemplateBinding Padding}"
TextBlock.Foreground="{TemplateBinding Foreground}"
TextElement.Foreground="{TemplateBinding Foreground}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>

2
src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml

@ -124,7 +124,7 @@
<ContentPresenter ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
Margin="{TemplateBinding Padding}"
TextBlock.Foreground="{TemplateBinding Foreground}"
TextElement.Foreground="{TemplateBinding Foreground}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />

22
src/Avalonia.Controls/ApiCompatBaseline.txt

@ -29,6 +29,21 @@ MembersMustExist : Member 'public void Avalonia.Controls.NumericUpDownValueChang
MembersMustExist : Member 'public System.Double Avalonia.Controls.NumericUpDownValueChangedEventArgs.NewValue.get()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public System.Double Avalonia.Controls.NumericUpDownValueChangedEventArgs.OldValue.get()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.StyledProperty<System.Boolean> Avalonia.StyledProperty<System.Boolean> Avalonia.Controls.ScrollViewer.AllowAutoHideProperty' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.AttachedProperty<Avalonia.Media.FontFamily> Avalonia.AttachedProperty<Avalonia.Media.FontFamily> Avalonia.Controls.TextBlock.FontFamilyProperty' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.AttachedProperty<Avalonia.Media.FontStyle> Avalonia.AttachedProperty<Avalonia.Media.FontStyle> Avalonia.Controls.TextBlock.FontStyleProperty' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.AttachedProperty<Avalonia.Media.FontWeight> Avalonia.AttachedProperty<Avalonia.Media.FontWeight> Avalonia.Controls.TextBlock.FontWeightProperty' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.AttachedProperty<Avalonia.Media.IBrush> Avalonia.AttachedProperty<Avalonia.Media.IBrush> Avalonia.Controls.TextBlock.ForegroundProperty' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.AttachedProperty<System.Double> Avalonia.AttachedProperty<System.Double> Avalonia.Controls.TextBlock.FontSizeProperty' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.Media.FontFamily Avalonia.Controls.TextBlock.GetFontFamily(Avalonia.Controls.Control)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public System.Double Avalonia.Controls.TextBlock.GetFontSize(Avalonia.Controls.Control)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.Media.FontStyle Avalonia.Controls.TextBlock.GetFontStyle(Avalonia.Controls.Control)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.Media.FontWeight Avalonia.Controls.TextBlock.GetFontWeight(Avalonia.Controls.Control)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.Media.IBrush Avalonia.Controls.TextBlock.GetForeground(Avalonia.Controls.Control)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Controls.TextBlock.SetFontFamily(Avalonia.Controls.Control, Avalonia.Media.FontFamily)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Controls.TextBlock.SetFontSize(Avalonia.Controls.Control, System.Double)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Controls.TextBlock.SetFontStyle(Avalonia.Controls.Control, Avalonia.Media.FontStyle)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Controls.TextBlock.SetFontWeight(Avalonia.Controls.Control, Avalonia.Media.FontWeight)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Controls.TextBlock.SetForeground(Avalonia.Controls.Control, Avalonia.Media.IBrush)' does not exist in the implementation but it does exist in the contract.
CannotRemoveBaseTypeOrInterface : Type 'Avalonia.Controls.TopLevel' does not implement interface 'Avalonia.Utilities.IWeakSubscriber<Avalonia.Controls.ResourcesChangedEventArgs>' in the implementation but it does in the contract.
CannotRemoveBaseTypeOrInterface : Type 'Avalonia.Controls.Viewbox' does not inherit from base type 'Avalonia.Controls.Decorator' in the implementation but it does in the contract.
MembersMustExist : Member 'public Avalonia.AvaloniaProperty<Avalonia.Media.Stretch> Avalonia.AvaloniaProperty<Avalonia.Media.Stretch> Avalonia.Controls.Viewbox.StretchProperty' does not exist in the implementation but it does exist in the contract.
@ -49,6 +64,11 @@ MembersMustExist : Member 'public Avalonia.Media.FormattedText Avalonia.Controls
MembersMustExist : Member 'public System.Int32 Avalonia.Controls.Presenters.TextPresenter.GetCaretIndex(Avalonia.Point)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'protected void Avalonia.Controls.Presenters.TextPresenter.InvalidateFormattedText()' does not exist in the implementation but it does exist in the contract.
CannotRemoveBaseTypeOrInterface : Type 'Avalonia.Controls.Primitives.PopupRoot' does not implement interface 'Avalonia.Utilities.IWeakSubscriber<Avalonia.Controls.ResourcesChangedEventArgs>' in the implementation but it does in the contract.
MembersMustExist : Member 'public Avalonia.StyledProperty<Avalonia.Media.FontFamily> Avalonia.StyledProperty<Avalonia.Media.FontFamily> Avalonia.Controls.Primitives.TemplatedControl.FontFamilyProperty' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.StyledProperty<Avalonia.Media.FontStyle> Avalonia.StyledProperty<Avalonia.Media.FontStyle> Avalonia.Controls.Primitives.TemplatedControl.FontStyleProperty' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.StyledProperty<Avalonia.Media.FontWeight> Avalonia.StyledProperty<Avalonia.Media.FontWeight> Avalonia.Controls.Primitives.TemplatedControl.FontWeightProperty' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.StyledProperty<Avalonia.Media.IBrush> Avalonia.StyledProperty<Avalonia.Media.IBrush> Avalonia.Controls.Primitives.TemplatedControl.ForegroundProperty' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.StyledProperty<System.Double> Avalonia.StyledProperty<System.Double> Avalonia.Controls.Primitives.TemplatedControl.FontSizeProperty' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'Avalonia.Platform.ExportWindowingSubsystemAttribute' does not exist in the implementation but it does exist in the contract.
EnumValuesMustMatch : Enum value 'Avalonia.Platform.ExtendClientAreaChromeHints Avalonia.Platform.ExtendClientAreaChromeHints.Default' is (System.Int32)2 in the implementation but (System.Int32)1 in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.Screen Avalonia.Platform.IScreenImpl.ScreenFromPoint(Avalonia.PixelPoint)' is present in the implementation but not in the contract.
@ -72,4 +92,4 @@ InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platfor
MembersMustExist : Member 'public void Avalonia.Platform.IWindowImpl.Resize(Avalonia.Size)' does not exist in the implementation but it does exist in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IWindowImpl.Resize(Avalonia.Size, Avalonia.Platform.PlatformResizeReason)' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.ITrayIconImpl Avalonia.Platform.IWindowingPlatform.CreateTrayIcon()' is present in the implementation but not in the contract.
Total Issues: 73
Total Issues: 93

114
src/Avalonia.Controls/Control.cs

@ -1,7 +1,7 @@
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Avalonia.Automation.Peers;
using Avalonia.Controls.Documents;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Input;
@ -67,7 +67,43 @@ namespace Avalonia.Controls
/// </summary>
public static readonly AttachedProperty<FlowDirection> FlowDirectionProperty =
AvaloniaProperty.RegisterAttached<Control, Control, FlowDirection>(nameof(FlowDirection), inherits: true);
/// <summary>
/// Defines the <see cref="FontFamily"/> property.
/// </summary>
public static readonly AttachedProperty<FontFamily> FontFamilyProperty =
TextElement.FontFamilyProperty.AddOwner<Control>();
/// <summary>
/// Defines the <see cref="FontSize"/> property.
/// </summary>
public static readonly AttachedProperty<double> FontSizeProperty =
TextElement.FontSizeProperty.AddOwner<Control>();
/// <summary>
/// Defines the <see cref="FontStyle"/> property.
/// </summary>
public static readonly AttachedProperty<FontStyle> FontStyleProperty =
TextElement.FontStyleProperty.AddOwner<Control>();
/// <summary>
/// Defines the <see cref="FontWeight"/> property.
/// </summary>
public static readonly AttachedProperty<FontWeight> FontWeightProperty =
TextElement.FontWeightProperty.AddOwner<Control>();
/// <summary>
/// Defines the <see cref="FontStretch"/> property.
/// </summary>
public static readonly AttachedProperty<FontStretch> FontStretchProperty =
TextElement.FontStretchProperty.AddOwner<Control>();
/// <summary>
/// Defines the <see cref="Foreground"/> property.
/// </summary>
public static readonly AttachedProperty<IBrush?> ForegroundProperty =
TextElement.ForegroundProperty.AddOwner<Control>();
private DataTemplates? _dataTemplates;
private IControl? _focusAdorner;
private AutomationPeer? _automationPeer;
@ -126,6 +162,60 @@ namespace Avalonia.Controls
set => SetValue(FlowDirectionProperty, value);
}
/// <summary>
/// Gets or sets the font family.
/// </summary>
public FontFamily FontFamily
{
get { return GetValue(FontFamilyProperty); }
set { SetValue(FontFamilyProperty, value); }
}
/// <summary>
/// Gets or sets the font size.
/// </summary>
public double FontSize
{
get { return GetValue(FontSizeProperty); }
set { SetValue(FontSizeProperty, value); }
}
/// <summary>
/// Gets or sets the font style.
/// </summary>
public FontStyle FontStyle
{
get { return GetValue(FontStyleProperty); }
set { SetValue(FontStyleProperty, value); }
}
/// <summary>
/// Gets or sets the font weight.
/// </summary>
public FontWeight FontWeight
{
get { return GetValue(FontWeightProperty); }
set { SetValue(FontWeightProperty, value); }
}
/// <summary>
/// Gets or sets the font stretch.
/// </summary>
public FontStretch FontStretch
{
get { return GetValue(FontStretchProperty); }
set { SetValue(FontStretchProperty, value); }
}
/// <summary>
/// Gets or sets a brush used to paint the text.
/// </summary>
public IBrush? Foreground
{
get { return GetValue(ForegroundProperty); }
set { SetValue(ForegroundProperty, value); }
}
/// <summary>
/// Occurs when the user has completed a context input gesture, such as a right-click.
/// </summary>
@ -137,6 +227,26 @@ namespace Avalonia.Controls
public new IControl? Parent => (IControl?)base.Parent;
/// <summary>
/// Gets the value of the attached <see cref="FlowDirectionProperty"/> on a control.
/// </summary>
/// <param name="control">The control.</param>
/// <returns>The flow direction.</returns>
public static FlowDirection GetFlowDirection(Control control)
{
return control.GetValue(FlowDirectionProperty);
}
/// <summary>
/// Sets the value of the attached <see cref="FlowDirectionProperty"/> on a control.
/// </summary>
/// <param name="control">The control.</param>
/// <param name="value">The property value to set.</param>
public static void SetFlowDirection(Control control, FlowDirection value)
{
control.SetValue(FlowDirectionProperty, value);
}
/// <inheritdoc/>
bool IDataTemplateHost.IsDataTemplatesInitialized => _dataTemplates != null;

167
src/Avalonia.Controls/Documents/TextElement.cs

@ -13,37 +13,60 @@ namespace Avalonia.Controls.Documents
/// Defines the <see cref="Background"/> property.
/// </summary>
public static readonly StyledProperty<IBrush?> BackgroundProperty =
AvaloniaProperty.Register<TextElement, IBrush?>(nameof(Background), inherits: true);
Border.BackgroundProperty.AddOwner<TextElement>();
/// <summary>
/// Defines the <see cref="FontFamily"/> property.
/// </summary>
public static readonly AttachedProperty<FontFamily> FontFamilyProperty =
TextBlock.FontFamilyProperty.AddOwner<TextElement>();
AvaloniaProperty.RegisterAttached<TextElement, Control, FontFamily>(
nameof(FontFamily),
defaultValue: FontFamily.Default,
inherits: true);
/// <summary>
/// Defines the <see cref="FontSize"/> property.
/// </summary>
public static readonly AttachedProperty<double> FontSizeProperty =
TextBlock.FontSizeProperty.AddOwner<TextElement>();
AvaloniaProperty.RegisterAttached<TextElement, Control, double>(
nameof(FontSize),
defaultValue: 12,
inherits: true);
/// <summary>
/// Defines the <see cref="FontStyle"/> property.
/// </summary>
public static readonly AttachedProperty<FontStyle> FontStyleProperty =
TextBlock.FontStyleProperty.AddOwner<TextElement>();
AvaloniaProperty.RegisterAttached<TextElement, Control, FontStyle>(
nameof(FontStyle),
inherits: true);
/// <summary>
/// Defines the <see cref="FontWeight"/> property.
/// </summary>
public static readonly AttachedProperty<FontWeight> FontWeightProperty =
TextBlock.FontWeightProperty.AddOwner<TextElement>();
AvaloniaProperty.RegisterAttached<TextElement, Control, FontWeight>(
nameof(FontWeight),
inherits: true,
defaultValue: FontWeight.Normal);
/// <summary>
/// Defines the <see cref="FontStretch"/> property.
/// </summary>
public static readonly AttachedProperty<FontStretch> FontStretchProperty =
AvaloniaProperty.RegisterAttached<TextElement, Control, FontStretch>(
nameof(FontStretch),
inherits: true,
defaultValue: FontStretch.Normal);
/// <summary>
/// Defines the <see cref="Foreground"/> property.
/// </summary>
public static readonly AttachedProperty<IBrush?> ForegroundProperty =
TextBlock.ForegroundProperty.AddOwner<TextElement>();
AvaloniaProperty.RegisterAttached<TextElement, Control, IBrush?>(
nameof(Foreground),
Brushes.Black,
inherits: true);
/// <summary>
/// Gets or sets a brush used to paint the control's background.
@ -90,6 +113,15 @@ namespace Avalonia.Controls.Documents
set { SetValue(FontWeightProperty, value); }
}
/// <summary>
/// Gets or sets the font stretch.
/// </summary>
public FontStretch FontStretch
{
get { return GetValue(FontStretchProperty); }
set { SetValue(FontStretchProperty, value); }
}
/// <summary>
/// Gets or sets a brush used to paint the text.
/// </summary>
@ -98,7 +130,127 @@ namespace Avalonia.Controls.Documents
get { return GetValue(ForegroundProperty); }
set { SetValue(ForegroundProperty, value); }
}
/// <summary>
/// Gets the value of the attached <see cref="FontFamilyProperty"/> on a control.
/// </summary>
/// <param name="control">The control.</param>
/// <returns>The font family.</returns>
public static FontFamily GetFontFamily(Control control)
{
return control.GetValue(FontFamilyProperty);
}
/// <summary>
/// Sets the value of the attached <see cref="FontFamilyProperty"/> on a control.
/// </summary>
/// <param name="control">The control.</param>
/// <param name="value">The property value to set.</param>
public static void SetFontFamily(Control control, FontFamily value)
{
control.SetValue(FontFamilyProperty, value);
}
/// <summary>
/// Gets the value of the attached <see cref="FontSizeProperty"/> on a control.
/// </summary>
/// <param name="control">The control.</param>
/// <returns>The font size.</returns>
public static double GetFontSize(Control control)
{
return control.GetValue(FontSizeProperty);
}
/// <summary>
/// Sets the value of the attached <see cref="FontSizeProperty"/> on a control.
/// </summary>
/// <param name="control">The control.</param>
/// <param name="value">The property value to set.</param>
public static void SetFontSize(Control control, double value)
{
control.SetValue(FontSizeProperty, value);
}
/// <summary>
/// Gets the value of the attached <see cref="FontStyleProperty"/> on a control.
/// </summary>
/// <param name="control">The control.</param>
/// <returns>The font style.</returns>
public static FontStyle GetFontStyle(Control control)
{
return control.GetValue(FontStyleProperty);
}
/// <summary>
/// Sets the value of the attached <see cref="FontStyleProperty"/> on a control.
/// </summary>
/// <param name="control">The control.</param>
/// <param name="value">The property value to set.</param>
public static void SetFontStyle(Control control, FontStyle value)
{
control.SetValue(FontStyleProperty, value);
}
/// <summary>
/// Gets the value of the attached <see cref="FontWeightProperty"/> on a control.
/// </summary>
/// <param name="control">The control.</param>
/// <returns>The font weight.</returns>
public static FontWeight GetFontWeight(Control control)
{
return control.GetValue(FontWeightProperty);
}
/// <summary>
/// Sets the value of the attached <see cref="FontWeightProperty"/> on a control.
/// </summary>
/// <param name="control">The control.</param>
/// <param name="value">The property value to set.</param>
public static void SetFontWeight(Control control, FontWeight value)
{
control.SetValue(FontWeightProperty, value);
}
/// <summary>
/// Gets the value of the attached <see cref="FontStretchProperty"/> on a control.
/// </summary>
/// <param name="control">The control.</param>
/// <returns>The font stretch.</returns>
public static FontStretch GetFontStretch(Control control)
{
return control.GetValue(FontStretchProperty);
}
/// <summary>
/// Sets the value of the attached <see cref="FontStretchProperty"/> on a control.
/// </summary>
/// <param name="control">The control.</param>
/// <param name="value">The property value to set.</param>
public static void SetFontStretch(Control control, FontStretch value)
{
control.SetValue(FontStretchProperty, value);
}
/// <summary>
/// Gets the value of the attached <see cref="ForegroundProperty"/> on a control.
/// </summary>
/// <param name="control">The control.</param>
/// <returns>The foreground.</returns>
public static IBrush? GetForeground(Control control)
{
return control.GetValue(ForegroundProperty);
}
/// <summary>
/// Sets the value of the attached <see cref="ForegroundProperty"/> on a control.
/// </summary>
/// <param name="control">The control.</param>
/// <param name="value">The property value to set.</param>
public static void SetForeground(Control control, IBrush? value)
{
control.SetValue(ForegroundProperty, value);
}
/// <summary>
/// Raised when the visual representation of the text element changes.
/// </summary>
@ -115,6 +267,7 @@ namespace Avalonia.Controls.Documents
case nameof(FontSize):
case nameof(FontStyle):
case nameof(FontWeight):
case nameof(FontStretch):
case nameof(Foreground):
Invalidate();
break;

89
src/Avalonia.Controls/Presenters/TextPresenter.cs

@ -116,60 +116,6 @@ namespace Avalonia.Controls.Presenters
set => SetAndRaise(TextProperty, ref _text, value);
}
/// <summary>
/// Gets or sets the font family.
/// </summary>
public FontFamily FontFamily
{
get => TextBlock.GetFontFamily(this);
set => TextBlock.SetFontFamily(this, value);
}
/// <summary>
/// Gets or sets the font size.
/// </summary>
public double FontSize
{
get => TextBlock.GetFontSize(this);
set => TextBlock.SetFontSize(this, value);
}
/// <summary>
/// Gets or sets the font style.
/// </summary>
public FontStyle FontStyle
{
get => TextBlock.GetFontStyle(this);
set => TextBlock.SetFontStyle(this, value);
}
/// <summary>
/// Gets or sets the font weight.
/// </summary>
public FontWeight FontWeight
{
get => TextBlock.GetFontWeight(this);
set => TextBlock.SetFontWeight(this, value);
}
/// <summary>
/// Gets or sets the font stretch.
/// </summary>
public FontStretch FontStretch
{
get => TextBlock.GetFontStretch(this);
set => TextBlock.SetFontStretch(this, value);
}
/// <summary>
/// Gets or sets a brush used to paint the text.
/// </summary>
public IBrush? Foreground
{
get => TextBlock.GetForeground(this);
set => TextBlock.SetForeground(this, value);
}
/// <summary>
/// Gets or sets the control's text wrapping mode.
/// </summary>
@ -353,7 +299,7 @@ namespace Avalonia.Controls.Presenters
foreach (var rect in rects)
{
context.FillRectangle(selectionBrush, rect);
context.FillRectangle(selectionBrush, PixelRect.FromRect(rect, 1).ToRect(1));
}
}
@ -528,9 +474,9 @@ namespace Avalonia.Controls.Presenters
{
return finalSize;
}
_constraint = finalSize;
_constraint = new Size(finalSize.Width, Math.Ceiling(finalSize.Height));
_textLayout = null;
return finalSize;
@ -637,14 +583,7 @@ namespace Avalonia.Controls.Presenters
if (Text is null)
{
return default;
}
if (FlowDirection == FlowDirection.RightToLeft)
{
direction = direction == LogicalDirection.Forward ?
LogicalDirection.Backward :
LogicalDirection.Forward;
}
}
var characterHit = _lastCharacterHit;
var caretIndex = characterHit.FirstCharacterIndex + characterHit.TrailingLength;
@ -719,6 +658,13 @@ namespace Avalonia.Controls.Presenters
public void MoveCaretHorizontal(LogicalDirection direction = LogicalDirection.Forward)
{
if (FlowDirection == FlowDirection.RightToLeft)
{
direction = direction == LogicalDirection.Forward ?
LogicalDirection.Backward :
LogicalDirection.Forward;
}
var characterHit = GetNextCharacterHit(direction);
UpdateCaret(characterHit);
@ -779,11 +725,11 @@ namespace Avalonia.Controls.Presenters
switch (change.Property.Name)
{
case nameof (TextBlock.Foreground):
case nameof (TextBlock.FontSize):
case nameof (TextBlock.FontStyle):
case nameof (TextBlock.FontWeight):
case nameof (TextBlock.FontFamily):
case nameof (Foreground):
case nameof (FontSize):
case nameof (FontStyle):
case nameof (FontWeight):
case nameof (FontFamily):
case nameof (Text):
case nameof (TextAlignment):
case nameof (TextWrapping):
@ -792,6 +738,7 @@ namespace Avalonia.Controls.Presenters
case nameof (SelectionForegroundBrush):
case nameof (PasswordChar):
case nameof (RevealPassword):
case nameof(FlowDirection):
{
InvalidateTextLayout();
break;

75
src/Avalonia.Controls/Primitives/TemplatedControl.cs

@ -37,36 +37,6 @@ namespace Avalonia.Controls.Primitives
public static readonly StyledProperty<CornerRadius> CornerRadiusProperty =
Border.CornerRadiusProperty.AddOwner<TemplatedControl>();
/// <summary>
/// Defines the <see cref="FontFamily"/> property.
/// </summary>
public static readonly StyledProperty<FontFamily> FontFamilyProperty =
TextBlock.FontFamilyProperty.AddOwner<TemplatedControl>();
/// <summary>
/// Defines the <see cref="FontSize"/> property.
/// </summary>
public static readonly StyledProperty<double> FontSizeProperty =
TextBlock.FontSizeProperty.AddOwner<TemplatedControl>();
/// <summary>
/// Defines the <see cref="FontStyle"/> property.
/// </summary>
public static readonly StyledProperty<FontStyle> FontStyleProperty =
TextBlock.FontStyleProperty.AddOwner<TemplatedControl>();
/// <summary>
/// Defines the <see cref="FontWeight"/> property.
/// </summary>
public static readonly StyledProperty<FontWeight> FontWeightProperty =
TextBlock.FontWeightProperty.AddOwner<TemplatedControl>();
/// <summary>
/// Defines the <see cref="Foreground"/> property.
/// </summary>
public static readonly StyledProperty<IBrush?> ForegroundProperty =
TextBlock.ForegroundProperty.AddOwner<TemplatedControl>();
/// <summary>
/// Defines the <see cref="Padding"/> property.
/// </summary>
@ -149,51 +119,6 @@ namespace Avalonia.Controls.Primitives
set { SetValue(CornerRadiusProperty, value); }
}
/// <summary>
/// Gets or sets the font family used to draw the control's text.
/// </summary>
public FontFamily FontFamily
{
get { return GetValue(FontFamilyProperty); }
set { SetValue(FontFamilyProperty, value); }
}
/// <summary>
/// Gets or sets the size of the control's text in points.
/// </summary>
public double FontSize
{
get { return GetValue(FontSizeProperty); }
set { SetValue(FontSizeProperty, value); }
}
/// <summary>
/// Gets or sets the font style used to draw the control's text.
/// </summary>
public FontStyle FontStyle
{
get { return GetValue(FontStyleProperty); }
set { SetValue(FontStyleProperty, value); }
}
/// <summary>
/// Gets or sets the font weight used to draw the control's text.
/// </summary>
public FontWeight FontWeight
{
get { return GetValue(FontWeightProperty); }
set { SetValue(FontWeightProperty, value); }
}
/// <summary>
/// Gets or sets the brush used to draw the control's text and other foreground elements.
/// </summary>
public IBrush? Foreground
{
get { return GetValue(ForegroundProperty); }
set { SetValue(ForegroundProperty, value); }
}
/// <summary>
/// Gets or sets the padding placed between the border of the control and its content.
/// </summary>

240
src/Avalonia.Controls/TextBlock.cs

@ -28,61 +28,14 @@ namespace Avalonia.Controls
public static readonly StyledProperty<Thickness> PaddingProperty =
Decorator.PaddingProperty.AddOwner<TextBlock>();
// TODO: Define these attached properties elsewhere (e.g. on a Text class) and AddOwner
// them into TextBlock.
/// <summary>
/// Defines the <see cref="FontFamily"/> property.
/// </summary>
public static readonly AttachedProperty<FontFamily> FontFamilyProperty =
AvaloniaProperty.RegisterAttached<TextBlock, Control, FontFamily>(
nameof(FontFamily),
defaultValue: FontFamily.Default,
inherits: true);
/// <summary>
/// Defines the <see cref="FontSize"/> property.
/// DependencyProperty for <see cref="BaselineOffset" /> property.
/// </summary>
public static readonly AttachedProperty<double> FontSizeProperty =
public static readonly AttachedProperty<double> BaselineOffsetProperty =
AvaloniaProperty.RegisterAttached<TextBlock, Control, double>(
nameof(FontSize),
defaultValue: 12,
inherits: true);
/// <summary>
/// Defines the <see cref="FontStyle"/> property.
/// </summary>
public static readonly AttachedProperty<FontStyle> FontStyleProperty =
AvaloniaProperty.RegisterAttached<TextBlock, Control, FontStyle>(
nameof(FontStyle),
inherits: true);
/// <summary>
/// Defines the <see cref="FontWeight"/> property.
/// </summary>
public static readonly AttachedProperty<FontWeight> FontWeightProperty =
AvaloniaProperty.RegisterAttached<TextBlock, Control, FontWeight>(
nameof(FontWeight),
inherits: true,
defaultValue: FontWeight.Normal);
/// <summary>
/// Defines the <see cref="FontStretch"/> property.
/// </summary>
public static readonly AttachedProperty<FontStretch> FontStretchProperty =
AvaloniaProperty.RegisterAttached<TextBlock, Control, FontStretch>(
nameof(FontStretch),
inherits: true,
defaultValue: FontStretch.Normal);
/// <summary>
/// Defines the <see cref="Foreground"/> property.
/// </summary>
public static readonly AttachedProperty<IBrush?> ForegroundProperty =
AvaloniaProperty.RegisterAttached<TextBlock, Control, IBrush?>(
nameof(Foreground),
Brushes.Black,
inherits: true);
nameof(BaselineOffset),
0,
true);
/// <summary>
/// Defines the <see cref="LineHeight"/> property.
@ -221,60 +174,6 @@ namespace Avalonia.Controls
[Content]
public InlineCollection Inlines { get; }
/// <summary>
/// Gets or sets the font family.
/// </summary>
public FontFamily FontFamily
{
get { return GetValue(FontFamilyProperty); }
set { SetValue(FontFamilyProperty, value); }
}
/// <summary>
/// Gets or sets the font size.
/// </summary>
public double FontSize
{
get { return GetValue(FontSizeProperty); }
set { SetValue(FontSizeProperty, value); }
}
/// <summary>
/// Gets or sets the font style.
/// </summary>
public FontStyle FontStyle
{
get { return GetValue(FontStyleProperty); }
set { SetValue(FontStyleProperty, value); }
}
/// <summary>
/// Gets or sets the font weight.
/// </summary>
public FontWeight FontWeight
{
get { return GetValue(FontWeightProperty); }
set { SetValue(FontWeightProperty, value); }
}
/// <summary>
/// Gets or sets the font stretch.
/// </summary>
public FontStretch FontStretch
{
get { return GetValue(FontStretchProperty); }
set { SetValue(FontStretchProperty, value); }
}
/// <summary>
/// Gets or sets a brush used to paint the text.
/// </summary>
public IBrush? Foreground
{
get { return GetValue(ForegroundProperty); }
set { SetValue(ForegroundProperty, value); }
}
/// <summary>
/// Gets or sets the height of each line of content.
/// </summary>
@ -328,125 +227,43 @@ namespace Avalonia.Controls
get => GetValue(TextDecorationsProperty);
set => SetValue(TextDecorationsProperty, value);
}
/// <summary>
/// Gets the value of the attached <see cref="FontFamilyProperty"/> on a control.
/// </summary>
/// <param name="control">The control.</param>
/// <returns>The font family.</returns>
public static FontFamily GetFontFamily(Control control)
{
return control.GetValue(FontFamilyProperty);
}
/// <summary>
/// Gets the value of the attached <see cref="FontSizeProperty"/> on a control.
/// </summary>
/// <param name="control">The control.</param>
/// <returns>The font size.</returns>
public static double GetFontSize(Control control)
{
return control.GetValue(FontSizeProperty);
}
/// <summary>
/// Gets the value of the attached <see cref="FontStyleProperty"/> on a control.
/// </summary>
/// <param name="control">The control.</param>
/// <returns>The font style.</returns>
public static FontStyle GetFontStyle(Control control)
{
return control.GetValue(FontStyleProperty);
}
/// <summary>
/// Gets the value of the attached <see cref="FontWeightProperty"/> on a control.
/// </summary>
/// <param name="control">The control.</param>
/// <returns>The font weight.</returns>
public static FontWeight GetFontWeight(Control control)
{
return control.GetValue(FontWeightProperty);
}
/// <summary>
/// Gets the value of the attached <see cref="FontStretchProperty"/> on a control.
/// </summary>
/// <param name="control">The control.</param>
/// <returns>The font stretch.</returns>
public static FontStretch GetFontStretch(Control control)
{
return control.GetValue(FontStretchProperty);
}
/// <summary>
/// Gets the value of the attached <see cref="ForegroundProperty"/> on a control.
/// </summary>
/// <param name="control">The control.</param>
/// <returns>The foreground.</returns>
public static IBrush? GetForeground(Control control)
{
return control.GetValue(ForegroundProperty);
}
/// <summary>
/// Sets the value of the attached <see cref="FontFamilyProperty"/> on a control.
/// The BaselineOffset property provides an adjustment to baseline offset
/// </summary>
/// <param name="control">The control.</param>
/// <param name="value">The property value to set.</param>
public static void SetFontFamily(Control control, FontFamily value)
public double BaselineOffset
{
control.SetValue(FontFamilyProperty, value);
get { return (double)GetValue(BaselineOffsetProperty); }
set { SetValue(BaselineOffsetProperty, value); }
}
/// <summary>
/// Sets the value of the attached <see cref="FontSizeProperty"/> on a control.
/// Reads the attached property from the given element
/// </summary>
/// <param name="control">The control.</param>
/// <param name="value">The property value to set.</param>
public static void SetFontSize(Control control, double value)
/// <param name="control">The element to which to read the attached property.</param>
public static double GetBaselineOffset(Control control)
{
control.SetValue(FontSizeProperty, value);
}
if (control == null)
{
throw new ArgumentNullException(nameof(control));
}
/// <summary>
/// Sets the value of the attached <see cref="FontStyleProperty"/> on a control.
/// </summary>
/// <param name="control">The control.</param>
/// <param name="value">The property value to set.</param>
public static void SetFontStyle(Control control, FontStyle value)
{
control.SetValue(FontStyleProperty, value);
return control.GetValue(BaselineOffsetProperty);
}
/// <summary>
/// Sets the value of the attached <see cref="FontWeightProperty"/> on a control.
/// Writes the attached property BaselineOffset to the given element.
/// </summary>
/// <param name="control">The control.</param>
/// <param name="value">The property value to set.</param>
public static void SetFontWeight(Control control, FontWeight value)
/// <param name="control">The element to which to write the attached property.</param>
/// <param name="value">The property value to set</param>
public static void SetBaselineOffset(Control control, double value)
{
control.SetValue(FontWeightProperty, value);
}
/// <summary>
/// Sets the value of the attached <see cref="FontStretchProperty"/> on a control.
/// </summary>
/// <param name="control">The control.</param>
/// <param name="value">The property value to set.</param>
public static void SetFontStretch(Control control, FontStretch value)
{
control.SetValue(FontStretchProperty, value);
}
if (control == null)
{
throw new ArgumentNullException(nameof(control));
}
/// <summary>
/// Sets the value of the attached <see cref="ForegroundProperty"/> on a control.
/// </summary>
/// <param name="control">The control.</param>
/// <param name="value">The property value to set.</param>
public static void SetForeground(Control control, IBrush? value)
{
control.SetValue(ForegroundProperty, value);
control.SetValue(BaselineOffsetProperty, value);
}
/// <summary>
@ -562,7 +379,7 @@ namespace Avalonia.Controls
return finalSize;
}
_constraint = finalSize;
_constraint = new Size(finalSize.Width, Math.Ceiling(finalSize.Height));
_textLayout = null;
@ -588,6 +405,7 @@ namespace Avalonia.Controls
case nameof (FontWeight):
case nameof (FontStyle):
case nameof (FontFamily):
case nameof (FontStretch):
case nameof (TextWrapping):
case nameof (TextTrimming):
@ -597,6 +415,8 @@ namespace Avalonia.Controls
case nameof (Padding):
case nameof (LineHeight):
case nameof (MaxLines):
case nameof (InlinesProperty):
case nameof (Text):
case nameof (TextDecorations):

109
src/Avalonia.Controls/TextBox.cs

@ -77,6 +77,9 @@ namespace Avalonia.Controls
public static readonly StyledProperty<int> MaxLengthProperty =
AvaloniaProperty.Register<TextBox, int>(nameof(MaxLength), defaultValue: 0);
public static readonly StyledProperty<int> MaxLinesProperty =
AvaloniaProperty.Register<TextBox, int>(nameof(MaxLines), defaultValue: 0);
public static readonly DirectProperty<TextBox, string?> TextProperty =
TextBlock.TextProperty.AddOwnerWithDataValidation<TextBox>(
o => o.Text,
@ -349,6 +352,12 @@ namespace Avalonia.Controls
set { SetValue(MaxLengthProperty, value); }
}
public int MaxLines
{
get { return GetValue(MaxLinesProperty); }
set { SetValue(MaxLinesProperty, value); }
}
[Content]
public string? Text
{
@ -670,6 +679,39 @@ namespace Avalonia.Controls
_selectedTextChangesMadeSinceLastUndoSnapshot++;
SnapshotUndoRedo(ignoreChangeCount: false);
if (_presenter != null && MaxLines > 0)
{
var lineCount = _presenter.TextLayout.TextLines.Count;
var length = 0;
var graphemeEnumerator = new GraphemeEnumerator(input.AsMemory());
while (graphemeEnumerator.MoveNext())
{
var grapheme = graphemeEnumerator.Current;
if (grapheme.FirstCodepoint.IsBreakChar)
{
if(lineCount + 1 > MaxLines)
{
break;
}
else
{
lineCount++;
}
}
length += grapheme.Text.Length;
}
if (length < input.Length)
{
input = input.Remove(Math.Max(0, length));
}
}
var text = Text ?? string.Empty;
var newLength = input.Length + text.Length - Math.Abs(SelectionStart - SelectionEnd);
@ -997,18 +1039,25 @@ namespace Avalonia.Controls
SetSelectionForControlBackspace();
}
if (!DeleteSelection() && caretIndex > 0)
if (!DeleteSelection())
{
_presenter.MoveCaretHorizontal(LogicalDirection.Backward);
var characterHit = _presenter.GetNextCharacterHit(LogicalDirection.Backward);
var removedCharacters = Math.Max(0, caretIndex - _presenter.CaretIndex);
var length = Math.Max(0, caretIndex - removedCharacters);
var backspacePosition = characterHit.FirstCharacterIndex + characterHit.TrailingLength;
SetTextInternal(text.Substring(0, length) +
text.Substring(caretIndex));
if (caretIndex != backspacePosition)
{
var start = Math.Min(backspacePosition, caretIndex);
var end = Math.Max(backspacePosition, caretIndex);
CaretIndex = _presenter.CaretIndex;
var length = end - start;
var editedText = text.Substring(0, start) + text.Substring(Math.Min(end, text.Length));
SetTextInternal(editedText);
CaretIndex = end;
}
}
SnapshotUndoRedo();
@ -1024,15 +1073,21 @@ namespace Avalonia.Controls
SetSelectionForControlDelete();
}
if (!DeleteSelection() && caretIndex < text.Length)
if (!DeleteSelection())
{
var characterHit = _presenter.GetNextCharacterHit();
var removedCharacters = Math.Max(0,
characterHit.FirstCharacterIndex + characterHit.TrailingLength - caretIndex);
var nextPosition = characterHit.FirstCharacterIndex + characterHit.TrailingLength;
if(nextPosition != caretIndex)
{
var start = Math.Min(nextPosition, caretIndex);
var end = Math.Max(nextPosition, caretIndex);
SetTextInternal(text.Substring(0, caretIndex) +
text.Substring(caretIndex + removedCharacters));
var editedText = text.Substring(0, start) + text.Substring(Math.Min(end, text.Length));
SetTextInternal(editedText);
}
}
SnapshotUndoRedo();
@ -1249,17 +1304,17 @@ namespace Avalonia.Controls
private void MoveHorizontal(int direction, bool wholeWord, bool isSelecting)
{
if (_presenter == null)
{
return;
}
var text = Text ?? string.Empty;
var selectionStart = SelectionStart;
var selectionEnd = SelectionEnd;
if (!wholeWord)
{
if (_presenter == null)
{
return;
}
if (isSelecting)
{
_presenter.MoveCaretToTextPosition(selectionEnd);
@ -1303,10 +1358,16 @@ namespace Avalonia.Controls
SelectionEnd += offset;
_presenter.MoveCaretToTextPosition(SelectionEnd);
if (!isSelecting)
{
CaretIndex = SelectionEnd;
}
else
{
SelectionStart = selectionStart;
}
}
}
@ -1326,7 +1387,7 @@ namespace Avalonia.Controls
else
{
var textLines = _presenter.TextLayout.TextLines;
var lineIndex = _presenter.TextLayout.GetLineIndexFromCharacterIndex(caretIndex, true);
var lineIndex = _presenter.TextLayout.GetLineIndexFromCharacterIndex(caretIndex, false);
var textLine = textLines[lineIndex];
_presenter.MoveCaretToTextPosition(textLine.TextRange.Start);
@ -1350,16 +1411,10 @@ namespace Avalonia.Controls
else
{
var textLines = _presenter.TextLayout.TextLines;
var lineIndex = _presenter.TextLayout.GetLineIndexFromCharacterIndex(caretIndex, true);
var lineIndex = _presenter.TextLayout.GetLineIndexFromCharacterIndex(caretIndex, false);
var textLine = textLines[lineIndex];
if (caretIndex == textLine.TextRange.Start + textLine.TextRange.Length - textLine.NewLineLength &&
lineIndex + 1 < textLines.Count)
{
textLine = textLines[++lineIndex];
}
var textPosition = textLine.TextRange.Start + textLine.TextRange.Length - textLine.NewLineLength;
var textPosition = textLine.TextRange.Start + textLine.TextRange.Length;
_presenter.MoveCaretToTextPosition(textPosition, true);
}

1
src/Avalonia.Controls/Window.cs

@ -12,7 +12,6 @@ using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Styling;
using JetBrains.Annotations;
namespace Avalonia.Controls
{

4
src/Avalonia.Diagnostics/Diagnostics/Views/ConsoleView.xaml

@ -11,7 +11,7 @@
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<DockPanel Margin="{TemplateBinding Padding}">
<TextBlock DockPanel.Dock="Left" Margin="0,0,4,0">></TextBlock>
<TextBlock DockPanel.Dock="Left" Margin="0,0,4,0"></TextBlock>
<TextPresenter Name="PART_TextPresenter"
Text="{TemplateBinding Text, Mode=TwoWay}"
CaretIndex="{TemplateBinding CaretIndex}"
@ -44,7 +44,7 @@
<DataTemplate>
<StackPanel Orientation="Vertical">
<DockPanel>
<TextBlock DockPanel.Dock="Left" Margin="0,0,4,0">></TextBlock>
<TextBlock DockPanel.Dock="Left" Margin="0,0,4,0"></TextBlock>
<TextBlock Text="{Binding Input}"/>
</DockPanel>
<TextBlock Foreground="{Binding Foreground}" Text="{Binding Output}"/>

2
src/Avalonia.Diagnostics/Diagnostics/Views/LayoutExplorerView.axaml

@ -47,7 +47,7 @@
<controls:ThicknessEditor Grid.Row="0" Grid.Column="0" Header="margin" VerticalAlignment="Top"
HorizontalAlignment="Center"
TextBlock.Foreground="{StaticResource ThicknessEditorForeground}"
TextElement.Foreground="{StaticResource ThicknessEditorForeground}"
Background="{StaticResource MarginBackgroundBrush}"
Highlight="{StaticResource MarginHighlightBrush}"
Thickness="{Binding MarginThickness}">

7
src/Avalonia.Headless/HeadlessPlatformStubs.cs

@ -133,9 +133,12 @@ namespace Avalonia.Headless
class HeadlessTextShaperStub : ITextShaperImpl
{
public ShapedBuffer ShapeText(ReadOnlySlice<char> text, GlyphTypeface typeface, double fontRenderingEmSize,
CultureInfo culture, sbyte bidiLevel)
public ShapedBuffer ShapeText(ReadOnlySlice<char> text, TextShaperOptions options)
{
var typeface = options.Typeface;
var fontRenderingEmSize = options.FontRenderingEmSize;
var bidiLevel = options.BidLevel;
return new ShapedBuffer(text, text.Length, typeface, fontRenderingEmSize, bidiLevel);
}
}

4
src/Avalonia.Themes.Default/Controls/Button.xaml

@ -3,7 +3,7 @@
<Setter Property="Background" Value="{DynamicResource ThemeControlMidBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderLowBrush}"/>
<Setter Property="BorderThickness" Value="{DynamicResource ThemeBorderThickness}"/>
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundBrush}"/>
<Setter Property="TextElement.Foreground" Value="{DynamicResource ThemeForegroundBrush}"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="4"/>
@ -18,7 +18,7 @@
Content="{TemplateBinding Content}"
Padding="{TemplateBinding Padding}"
RecognizesAccessKey="True"
TextBlock.Foreground="{TemplateBinding Foreground}"
Foreground="{TemplateBinding Foreground}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/>
</ControlTemplate>

4
src/Avalonia.Themes.Default/Controls/CaptionButtons.xaml

@ -1,10 +1,10 @@
<Styles xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style Selector="CaptionButtons">
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundBrush}"/>
<Setter Property="TextElement.Foreground" Value="{DynamicResource ThemeForegroundBrush}"/>
<Setter Property="MaxHeight" Value="30" />
<Setter Property="Template">
<ControlTemplate>
<StackPanel Spacing="2" VerticalAlignment="Stretch" TextBlock.FontSize="10" Orientation="Horizontal">
<StackPanel Spacing="2" VerticalAlignment="Stretch" FontSize="10" Orientation="Horizontal">
<StackPanel.Styles>
<Style Selector="Panel">
<Setter Property="Width" Value="45" />

4
src/Avalonia.Themes.Default/Controls/CheckBox.xaml

@ -1,6 +1,6 @@
<Styles xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style Selector="CheckBox">
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundBrush}"/>
<Setter Property="TextElement.Foreground" Value="{DynamicResource ThemeForegroundBrush}"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderMidBrush}"/>
<Setter Property="BorderThickness" Value="{DynamicResource ThemeBorderThickness}"/>
@ -37,7 +37,7 @@
</Panel>
</Border>
<ContentPresenter Name="PART_ContentPresenter"
TextBlock.Foreground="{TemplateBinding Foreground}"
Foreground="{TemplateBinding Foreground}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
Margin="{TemplateBinding Padding}"

22
src/Avalonia.Themes.Default/Controls/DatePicker.xaml

@ -36,7 +36,7 @@
</Style>
<Style Selector="ListBoxItem.DateTimePickerItem:selected /template/ ContentPresenter">
<Setter Property="Background" Value="Transparent" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ThemeForegroundBrush}"/>
<Setter Property="TextElement.Foreground" Value="{DynamicResource ThemeForegroundBrush}"/>
</Style>
<Style Selector="ListBoxItem.DateTimePickerItem.MonthItem">
<Setter Property="Padding" Value="{DynamicResource DatePickerFlyoutPresenterMonthPadding}"/>
@ -58,7 +58,7 @@
BorderBrush="{DynamicResource ThemeControlTransparentBrush}"
BorderThickness="{DynamicResource DateTimeFlyoutButtonBorderThickness}"
Content="{TemplateBinding Content}"
TextBlock.Foreground="{DynamicResource ThemeForegroundBrush}"
Foreground="{DynamicResource ThemeForegroundBrush}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Padding="{TemplateBinding Padding}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
@ -71,13 +71,13 @@
<Style Selector=":is(Button).DateTimeFlyoutButtonStyle:pointerover /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ThemeControlHighlightLowBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ThemeControlTransparentBrush}"/>
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ThemeForegroundBrush}"/>
<Setter Property="TextElement.Foreground" Value="{DynamicResource ThemeForegroundBrush}"/>
</Style>
<Style Selector=":is(Button).DateTimeFlyoutButtonStyle:pressed /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ThemeControlHighlightMidBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ThemeControlTransparentBrush}"/>
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ThemeForegroundBrush}"/>
<Setter Property="TextElement.Foreground" Value="{DynamicResource ThemeForegroundBrush}"/>
</Style>
@ -112,7 +112,7 @@
<Style Selector="DatePicker">
<Setter Property="FontSize" Value="{DynamicResource FontSizeNormal}" />
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundBrush}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource ThemeForegroundBrush}" />
<Setter Property="Background" Value="{DynamicResource ThemeBackgroundBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ThemeControlHighlightMidBrush}"/>
<Setter Property="BorderThickness" Value="1"/>
@ -151,7 +151,7 @@
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}"
Content="{TemplateBinding Content}"
TextBlock.Foreground="{TemplateBinding Foreground}"
Foreground="{TemplateBinding Foreground}"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"/>
</ControlTemplate>
@ -196,7 +196,7 @@
</Setter>
</Style>
<Style Selector="DatePicker /template/ ContentPresenter#HeaderContentPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ThemeForegroundBrush}"/>
<Setter Property="TextElement.Foreground" Value="{DynamicResource ThemeForegroundBrush}"/>
</Style>
<Style Selector="DatePicker:disabled /template/ Rectangle">
<!--<Setter Property="Fill" Value="{DynamicResource DatePickerSpacerFillDisabled}"/>-->
@ -206,7 +206,7 @@
<Style Selector="DatePicker /template/ Button#FlyoutButton:pointerover /template/ ContentPresenter">
<Setter Property="BorderBrush" Value="{DynamicResource ThemeControlHighBrush}"/>
<!--<Setter Property="Background" Value="{DynamicResource DatePickerButtonBackgroundPointerOver}"/>-->
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ThemeForegroundBrush}"/>
<Setter Property="TextElement.Foreground" Value="{DynamicResource ThemeForegroundBrush}"/>
</Style>
<Style Selector="DatePicker /template/ Button#FlyoutButton:pressed /template/ ContentPresenter">
@ -214,19 +214,19 @@
<Setter Property="Background">
<SolidColorBrush Color="{DynamicResource ThemeControlMidHighColor}" Opacity="0.6" />
</Setter>
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ThemeForegroundBrush}"/>
<Setter Property="TextElement.Foreground" Value="{DynamicResource ThemeForegroundBrush}"/>
</Style>
<Style Selector="DatePicker /template/ Button#FlyoutButton:disabled /template/ ContentPresenter">
<!--<Setter Property="BorderBrush" Value="{DynamicResource DatePickerButtonBorderBrushDisabled}"/>
<Setter Property="Background" Value="{DynamicResource DatePickerButtonBackgroundDisabled}"/>
<Setter Property="TextBlock.Foreground" Value="{DynamicResource DatePickerButtonForegroundDisabled}"/>-->
<Setter Property="TextElement.Foreground" Value="{DynamicResource DatePickerButtonForegroundDisabled}"/>-->
<Setter Property="Opacity" Value="{DynamicResource ThemeDisabledOpacity}" />
</Style>
<!-- Changes foreground for watermark text when SelectedDate is null-->
<Style Selector="DatePicker:hasnodate /template/ Button#FlyoutButton TextBlock">
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundLowBrush}"/>
<Setter Property="TextElement.Foreground" Value="{DynamicResource ThemeForegroundLowBrush}"/>
</Style>
<!--WinUI: DatePickerFlyoutPresenter-->

4
src/Avalonia.Themes.Default/Controls/RepeatButton.xaml

@ -6,7 +6,7 @@
Value="{DynamicResource ThemeBorderLowBrush}" />
<Setter Property="BorderThickness"
Value="{DynamicResource ThemeBorderThickness}" />
<Setter Property="Foreground"
<Setter Property="TextElement.Foreground"
Value="{DynamicResource ThemeForegroundBrush}" />
<Setter Property="HorizontalContentAlignment"
Value="Center" />
@ -24,7 +24,7 @@
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
Padding="{TemplateBinding Padding}"
TextBlock.Foreground="{TemplateBinding Foreground}"
Foreground="{TemplateBinding Foreground}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/>
</ControlTemplate>

46
src/Avalonia.Themes.Default/Controls/SplitButton.xaml

@ -56,7 +56,7 @@
<Style Selector="SplitButton">
<Setter Property="Background" Value="{DynamicResource SplitButtonBackground}" />
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForeground}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForeground}" />
<Setter Property="BorderBrush" Value="{DynamicResource SplitButtonBorderBrush}" />
<Setter Property="BorderThickness" Value="{DynamicResource SplitButtonBorderThemeThickness}" />
<Setter Property="HorizontalAlignment" Value="Left" />
@ -146,10 +146,10 @@
SplitButton /template/ Button#PART_SecondaryButton:pointerover /template/ ContentPresenter">
<Setter Property="Border.Background" Value="{DynamicResource SplitButtonBackgroundPointerOver}" />
<Setter Property="Border.BorderBrush" Value="{DynamicResource SplitButtonBorderBrushPointerOver}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundPointerOver}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForegroundPointerOver}" />
</Style>
<Style Selector="SplitButton /template/ Button#PART_SecondaryButton:pointerover PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundPointerOver}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForegroundPointerOver}" />
</Style>
<!-- Primary and Secondary buttons Pressed State -->
@ -157,10 +157,10 @@
SplitButton /template/ Button#PART_SecondaryButton:pressed /template/ ContentPresenter">
<Setter Property="Border.Background" Value="{DynamicResource SplitButtonBackgroundPressed}" />
<Setter Property="Border.BorderBrush" Value="{DynamicResource SplitButtonBorderBrushPressed}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundPressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForegroundPressed}" />
</Style>
<Style Selector="SplitButton /template/ Button#PART_SecondaryButton:pressed PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundPressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForegroundPressed}" />
</Style>
<!-- SplitButton Pressed State -->
@ -169,10 +169,10 @@
SplitButton:pressed /template/ Border#SeparatorBorder">
<Setter Property="Border.Background" Value="{DynamicResource SplitButtonBackgroundPressed}" />
<Setter Property="Border.BorderBrush" Value="{DynamicResource SplitButtonBorderBrushPressed}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundPressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForegroundPressed}" />
</Style>
<Style Selector="SplitButton:pressed /template/ Button#PART_SecondaryButton PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundPressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForegroundPressed}" />
</Style>
<!-- Primary and Secondary buttons Flyout Open State -->
@ -181,10 +181,10 @@
SplitButton:flyout-open /template/ Border#SeparatorBorder">
<Setter Property="Border.Background" Value="{DynamicResource SplitButtonBackgroundPressed}" />
<Setter Property="Border.BorderBrush" Value="{DynamicResource SplitButtonBorderBrushPressed}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundPressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForegroundPressed}" />
</Style>
<Style Selector="SplitButton:flyout-open /template/ Button#PART_SecondaryButton PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundPressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForegroundPressed}" />
</Style>
<!-- Disabled State -->
@ -193,10 +193,10 @@
SplitButton:disabled /template/ Border#SeparatorBorder">
<Setter Property="Border.Background" Value="{DynamicResource SplitButtonBackgroundDisabled}" />
<Setter Property="Border.BorderBrush" Value="{DynamicResource SplitButtonBorderBrushDisabled}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundDisabled}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForegroundDisabled}" />
</Style>
<Style Selector="SplitButton:disabled /template/ Button#PART_SecondaryButton PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundDisabled}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForegroundDisabled}" />
</Style>
<!-- Checked State -->
@ -205,10 +205,10 @@
SplitButton:checked /template/ Border#SeparatorBorder">
<Setter Property="Border.Background" Value="{DynamicResource SplitButtonBackgroundChecked}" />
<Setter Property="Border.BorderBrush" Value="{DynamicResource SplitButtonBorderBrushChecked}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundChecked}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForegroundChecked}" />
</Style>
<Style Selector="SplitButton:checked /template/ Button#PART_SecondaryButton PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundChecked}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForegroundChecked}" />
</Style>
<!-- Checked PointerOver State -->
@ -216,10 +216,10 @@
SplitButton:checked /template/ Button#PART_SecondaryButton:pointerover /template/ ContentPresenter">
<Setter Property="Border.Background" Value="{DynamicResource SplitButtonBackgroundCheckedPointerOver}" />
<Setter Property="Border.BorderBrush" Value="{DynamicResource SplitButtonBorderBrushCheckedPointerOver}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPointerOver}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPointerOver}" />
</Style>
<Style Selector="SplitButton:checked /template/ Button#PART_SecondaryButton:pointerover PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPointerOver}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPointerOver}" />
</Style>
<!-- Checked Pressed State -->
@ -227,10 +227,10 @@
SplitButton:checked /template/ Button#PART_SecondaryButton:pressed /template/ ContentPresenter">
<Setter Property="Border.Background" Value="{DynamicResource SplitButtonBackgroundCheckedPressed}" />
<Setter Property="Border.BorderBrush" Value="{DynamicResource SplitButtonBorderBrushCheckedPressed}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
</Style>
<Style Selector="SplitButton:checked /template/ Button#PART_SecondaryButton:pressed PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
</Style>
<!-- SplitButton Checked Pressed State -->
@ -239,10 +239,10 @@
SplitButton:pressed:checked /template/ Border#SeparatorBorder">
<Setter Property="Border.Background" Value="{DynamicResource SplitButtonBackgroundCheckedPressed}" />
<Setter Property="Border.BorderBrush" Value="{DynamicResource SplitButtonBorderBrushCheckedPressed}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
</Style>
<Style Selector="SplitButton:pressed:checked /template/ Button#PART_SecondaryButton PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
</Style>
<!-- Checked Flyout Open State -->
@ -251,10 +251,10 @@
SplitButton:checked:flyout-open /template/ Border#SeparatorBorder">
<Setter Property="Border.Background" Value="{DynamicResource SplitButtonBackgroundCheckedPressed}" />
<Setter Property="Border.BorderBrush" Value="{DynamicResource SplitButtonBorderBrushCheckedPressed}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
</Style>
<Style Selector="SplitButton:checked:flyout-open /template/ Button#PART_SecondaryButton PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
</Style>
<!-- Disabled Checked State -->
@ -263,9 +263,9 @@
SplitButton:checked:disabled /template/ Border#SeparatorBorder">
<Setter Property="Border.Background" Value="{DynamicResource SplitButtonBackgroundCheckedDisabled}" />
<Setter Property="Border.BorderBrush" Value="{DynamicResource SplitButtonBorderBrushCheckedDisabled}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundCheckedDisabled}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForegroundCheckedDisabled}" />
</Style>
<Style Selector="SplitButton:checked:disabled /template/ Button#PART_SecondaryButton PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundCheckedDisabled}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForegroundCheckedDisabled}" />
</Style>
</Styles>

16
src/Avalonia.Themes.Default/Controls/TimePicker.xaml

@ -33,7 +33,7 @@
<Style Selector="TimePicker">
<Setter Property="FontSize" Value="{DynamicResource FontSizeNormal}" />
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundBrush}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource ThemeForegroundBrush}" />
<Setter Property="Background" Value="{DynamicResource ThemeBackgroundBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ThemeControlHighlightMidBrush}"/>
<Setter Property="BorderThickness" Value="{DynamicResource TimePickerBorderThemeThickness}"/>
@ -48,7 +48,7 @@
ContentTemplate="{TemplateBinding HeaderTemplate}"
Margin="{DynamicResource TimePickerTopHeaderMargin}"
MaxWidth="{DynamicResource TimePickerThemeMaxWidth}"
TextBlock.Foreground="{DynamicResource ThemeForegroundColor}"
TextElement.Foreground="{DynamicResource ThemeForegroundColor}"
HorizontalAlignment="Stretch"
VerticalAlignment="Top" />
@ -74,7 +74,7 @@
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}"
Content="{TemplateBinding Content}"
TextBlock.Foreground="{TemplateBinding Foreground}"
TextElement.Foreground="{TemplateBinding Foreground}"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch" />
</ControlTemplate>
@ -135,7 +135,7 @@
</Style>
<Style Selector="TimePicker:disabled /template/ ContentPresenter#HeaderContentPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ThemeForegroundBrush}"/>
<Setter Property="TextElement.Foreground" Value="{DynamicResource ThemeForegroundBrush}"/>
</Style>
<Style Selector="TimePicker:disabled /template/ Rectangle">
<Setter Property="Opacity" Value="{DynamicResource ThemeDisabledOpacity}"/>
@ -144,7 +144,7 @@
<Style Selector="TimePicker /template/ Button#FlyoutButton:pointerover /template/ ContentPresenter">
<!--<Setter Property="Background" Value="{DynamicResource TimePickerButtonBackgroundPointerOver}"/>-->
<Setter Property="BorderBrush" Value="{DynamicResource ThemeControlHighBrush}"/>
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ThemeForegroundBrush}"/>
<Setter Property="TextElement.Foreground" Value="{DynamicResource ThemeForegroundBrush}"/>
</Style>
<Style Selector="TimePicker /template/ Button:pressed /template/ ContentPresenter">
@ -152,18 +152,18 @@
<SolidColorBrush Color="{DynamicResource ThemeControlMidHighColor}" Opacity="0.6" />
</Setter>
<Setter Property="BorderBrush" Value="{DynamicResource ThemeControlLowBrush}"/>
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ThemeForegroundBrush}"/>
<Setter Property="TextElement.Foreground" Value="{DynamicResource ThemeForegroundBrush}"/>
</Style>
<Style Selector="TimePicker /template/ Button:disabled /template/ ContentPresenter">
<!--<Setter Property="Background" Value="{DynamicResource TimePickerButtonBackgroundDisabled}"/>
<Setter Property="BorderBrush" Value="{DynamicResource TimePickerButtonBorderBrushDisabled}"/>
<Setter Property="TextBlock.Foreground" Value="{DynamicResource TimePickerButtonForegroundDisabled}"/>-->
<Setter Property="TextElement.Foreground" Value="{DynamicResource TimePickerButtonForegroundDisabled}"/>-->
<Setter Property="Opacity" Value="{DynamicResource ThemeDisabledOpacity}" />
</Style>
<Style Selector="TimePicker:hasnotime /template/ Button#FlyoutButton TextBlock">
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundLowBrush}"/>
<Setter Property="TextElement.Foreground" Value="{DynamicResource ThemeForegroundLowBrush}"/>
</Style>
<Style Selector="TimePickerPresenter">

2
src/Avalonia.Themes.Default/Controls/ToggleButton.xaml

@ -18,7 +18,7 @@
Content="{TemplateBinding Content}"
Padding="{TemplateBinding Padding}"
RecognizesAccessKey="True"
TextBlock.Foreground="{TemplateBinding Foreground}"
TextElement.Foreground="{TemplateBinding Foreground}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/>
</ControlTemplate>

16
src/Avalonia.Themes.Fluent/Controls/Button.xaml

@ -13,7 +13,7 @@
<Style Selector="Button">
<Setter Property="Background" Value="{DynamicResource ButtonBackground}" />
<!--<Setter Property="BackgroundSizing" Value="OuterBorderEdge" />-->
<Setter Property="Foreground" Value="{DynamicResource ButtonForeground}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource ButtonForeground}" />
<Setter Property="BorderBrush" Value="{DynamicResource ButtonBorderBrush}" />
<Setter Property="BorderThickness" Value="{DynamicResource ButtonBorderThemeThickness}" />
<Setter Property="CornerRadius" Value="{DynamicResource ControlCornerRadius}" />
@ -45,37 +45,37 @@
<Style Selector="Button:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ButtonBackgroundPointerOver}" />
<Setter Property="BorderBrush" Value="{DynamicResource ButtonBorderBrushPointerOver}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ButtonForegroundPointerOver}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource ButtonForegroundPointerOver}" />
</Style>
<Style Selector="Button:pressed /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ButtonBackgroundPressed}" />
<Setter Property="BorderBrush" Value="{DynamicResource ButtonBorderBrushPressed}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ButtonForegroundPressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource ButtonForegroundPressed}" />
</Style>
<Style Selector="Button:disabled /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ButtonBackgroundDisabled}" />
<Setter Property="BorderBrush" Value="{DynamicResource ButtonBorderBrushDisabled}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ButtonForegroundDisabled}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource ButtonForegroundDisabled}" />
</Style>
<Style Selector="Button.accent /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{DynamicResource AccentButtonBackground}" />
<Setter Property="BorderBrush" Value="{DynamicResource AccentButtonBorderBrush}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource AccentButtonForeground}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource AccentButtonForeground}" />
</Style>
<Style Selector="Button.accent:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{DynamicResource AccentButtonBackgroundPointerOver}" />
<Setter Property="BorderBrush" Value="{DynamicResource AccentButtonBorderBrushPointerOver}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource AccentButtonForegroundPointerOver}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource AccentButtonForegroundPointerOver}" />
</Style>
<Style Selector="Button.accent:pressed /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{DynamicResource AccentButtonBackgroundPressed}" />
<Setter Property="BorderBrush" Value="{DynamicResource AccentButtonBorderBrushPressed}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource AccentButtonForegroundPressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource AccentButtonForegroundPressed}" />
</Style>
<Style Selector="Button, RepeatButton, ToggleButton">
@ -94,6 +94,6 @@
<Style Selector="Button.accent:disabled /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{DynamicResource AccentButtonBackgroundDisabled}" />
<Setter Property="BorderBrush" Value="{DynamicResource AccentButtonBorderBrushDisabled}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource AccentButtonForegroundDisabled}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource AccentButtonForegroundDisabled}" />
</Style>
</Styles>

6
src/Avalonia.Themes.Fluent/Controls/CalendarItem.xaml

@ -61,17 +61,17 @@
</Style>
<Style Selector="Button.CalendarHeader:pointerover /template/ ContentPresenter#Text">
<Setter Property="BorderBrush" Value="{DynamicResource CalendarViewNavigationButtonBorderBrushPointerOver}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource CalendarViewNavigationButtonForegroundPointerOver}"/>
<Setter Property="TextElement.Foreground" Value="{DynamicResource CalendarViewNavigationButtonForegroundPointerOver}"/>
<!-- Prevent base button template:pointerover from overriding background here -->
<Setter Property="Background" Value="{DynamicResource CalendarViewNavigationButtonBackground}"/>
</Style>
<Style Selector="Button.CalendarHeader:pressed /template/ ContentPresenter#Text">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource CalendarViewNavigationButtonForegroundPressed}"/>
<Setter Property="TextElement.Foreground" Value="{DynamicResource CalendarViewNavigationButtonForegroundPressed}"/>
<!-- Prevent base button template:pointerover from overriding background here -->
<Setter Property="Background" Value="{DynamicResource CalendarViewNavigationButtonBackground}"/>
</Style>
<Style Selector="Button.CalendarHeader:disabled /template/ ContentPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource CalendarViewWeekDayForegroundDisabled}"/>
<Setter Property="TextElement.Foreground" Value="{DynamicResource CalendarViewWeekDayForegroundDisabled}"/>
</Style>
</Border.Styles>
<!-- To keep calendar from resizing when switching DisplayMode

2
src/Avalonia.Themes.Fluent/Controls/CaptionButtons.xaml

@ -3,7 +3,7 @@
<Setter Property="MaxHeight" Value="30" />
<Setter Property="Template">
<ControlTemplate>
<StackPanel Spacing="2" VerticalAlignment="Stretch" TextBlock.FontSize="10" Orientation="Horizontal">
<StackPanel Spacing="2" VerticalAlignment="Stretch" FontSize="10" Orientation="Horizontal">
<StackPanel.Styles>
<Style Selector="Panel">
<Setter Property="Width" Value="45" />

24
src/Avalonia.Themes.Fluent/Controls/CheckBox.xaml

@ -56,7 +56,7 @@
<!-- Unchecked Normal State -->
<Style Selector="CheckBox">
<Setter Property="Foreground" Value="{DynamicResource CheckBoxForegroundUnchecked}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource CheckBoxForegroundUnchecked}" />
</Style>
<Style Selector="CheckBox">
@ -76,7 +76,7 @@
<!-- Unchecked PointerOver State -->
<Style Selector="CheckBox:pointerover /template/ ContentPresenter#ContentPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource CheckBoxForegroundUncheckedPointerOver}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource CheckBoxForegroundUncheckedPointerOver}" />
</Style>
<Style Selector="CheckBox:pointerover /template/ Border#PART_Border">
@ -95,7 +95,7 @@
<!-- Unchecked Pressed State -->
<Style Selector="CheckBox:pressed /template/ ContentPresenter#ContentPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource CheckBoxForegroundUncheckedPressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource CheckBoxForegroundUncheckedPressed}" />
</Style>
<Style Selector="CheckBox:pressed /template/ Border#PART_Border">
@ -114,7 +114,7 @@
<!-- Unchecked Disabled state -->
<Style Selector="CheckBox:disabled /template/ ContentPresenter#ContentPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource CheckBoxForegroundUncheckedDisabled}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource CheckBoxForegroundUncheckedDisabled}" />
</Style>
<Style Selector="CheckBox:disabled /template/ Border#PART_Border">
@ -134,7 +134,7 @@
<!-- Checked Normal State -->
<Style Selector="CheckBox:checked">
<Setter Property="Foreground" Value="{DynamicResource CheckBoxForegroundChecked}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource CheckBoxForegroundChecked}" />
</Style>
<Style Selector="CheckBox:checked">
@ -156,7 +156,7 @@
<!-- Checked PointerOver State -->
<Style Selector="CheckBox:checked:pointerover /template/ ContentPresenter#ContentPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource CheckBoxForegroundCheckedPointerOver}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource CheckBoxForegroundCheckedPointerOver}" />
</Style>
<Style Selector="CheckBox:checked:pointerover /template/ Border#PART_Border">
@ -175,7 +175,7 @@
<!-- Checked Pressed State -->
<Style Selector="CheckBox:checked:pressed /template/ ContentPresenter#ContentPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource CheckBoxForegroundCheckedPressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource CheckBoxForegroundCheckedPressed}" />
</Style>
<Style Selector="CheckBox:checked:pressed /template/ Border#PART_Border">
@ -194,7 +194,7 @@
<!-- Checked Disabled State -->
<Style Selector="CheckBox:checked:disabled /template/ ContentPresenter#ContentPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource CheckBoxForegroundCheckedDisabled}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource CheckBoxForegroundCheckedDisabled}" />
</Style>
<Style Selector="CheckBox:checked:disabled /template/ Border#PART_Border">
@ -214,7 +214,7 @@
<!-- Indeterminate Normal State -->
<Style Selector="CheckBox:indeterminate">
<Setter Property="Foreground" Value="{DynamicResource CheckBoxForegroundIndeterminate}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource CheckBoxForegroundIndeterminate}" />
</Style>
<Style Selector="CheckBox:indeterminate">
@ -236,7 +236,7 @@
<!-- Indeterminate PointerOver State -->
<Style Selector="CheckBox:indeterminate:pointerover /template/ ContentPresenter#ContentPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource CheckBoxForegroundIndeterminatePointerOver}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource CheckBoxForegroundIndeterminatePointerOver}" />
</Style>
<Style Selector="CheckBox:indeterminate:pointerover /template/ Border#PART_Border">
@ -255,7 +255,7 @@
<!-- Indeterminate Pressed State -->
<Style Selector="CheckBox:indeterminate:pressed /template/ ContentPresenter#ContentPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource CheckBoxForegroundIndeterminatePressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource CheckBoxForegroundIndeterminatePressed}" />
</Style>
<Style Selector="CheckBox:indeterminate:pressed /template/ Border#PART_Border">
@ -274,7 +274,7 @@
<!-- Indeterminate Disabled State -->
<Style Selector="CheckBox:indeterminate:disabled /template/ ContentPresenter#ContentPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource CheckBoxForegroundIndeterminateDisabled}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource CheckBoxForegroundIndeterminateDisabled}" />
</Style>
<Style Selector="CheckBox:indeterminate:disabled /template/ Border#PART_Border">

28
src/Avalonia.Themes.Fluent/Controls/ComboBox.xaml

@ -30,7 +30,7 @@
<Setter Property="Padding" Value="{DynamicResource ComboBoxPadding}" />
<Setter Property="FocusAdorner" Value="{x:Null}" />
<Setter Property="MaxDropDownHeight" Value="504" />
<Setter Property="Foreground" Value="{DynamicResource ComboBoxForeground}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource ComboBoxForeground}" />
<Setter Property="Background" Value="{DynamicResource ComboBoxBackground}" />
<Setter Property="BorderBrush" Value="{DynamicResource ComboBoxBorderBrush}" />
<Setter Property="BorderThickness" Value="{DynamicResource ComboBoxBorderThemeThickness}" />
@ -53,7 +53,7 @@
Grid.Column="0"
Grid.ColumnSpan="2"
IsVisible="False"
TextBlock.FontWeight="{DynamicResource ComboBoxHeaderThemeFontWeight}"
FontWeight="{DynamicResource ComboBoxHeaderThemeFontWeight}"
Margin="{DynamicResource ComboBoxTopHeaderMargin}"
VerticalAlignment="Top" />
<Border x:Name="Background"
@ -146,7 +146,7 @@
<!-- Normal State -->
<Style Selector="ComboBox /template/ TextBlock#PlaceholderTextBlock">
<Setter Property="Foreground" Value="{DynamicResource ComboBoxPlaceHolderForeground}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource ComboBoxPlaceHolderForeground}" />
</Style>
<Style Selector="ComboBox /template/ Border#HighlightBackground">
@ -154,7 +154,7 @@
</Style>
<Style Selector="ComboBox /template/ PathIcon#DropDownGlyph">
<Setter Property="Foreground" Value="{DynamicResource ComboBoxDropDownGlyphForeground}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource ComboBoxDropDownGlyphForeground}" />
<Setter Property="Data" Value="M1939 486L2029 576L1024 1581L19 576L109 486L1024 1401L1939 486Z" />
</Style>
@ -177,19 +177,19 @@
</Style>
<Style Selector="ComboBox:disabled /template/ ContentPresenter#HeaderContentPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ComboBoxForegroundDisabled}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource ComboBoxForegroundDisabled}" />
</Style>
<Style Selector="ComboBox:disabled /template/ ContentControl#ContentPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ComboBoxForegroundDisabled}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource ComboBoxForegroundDisabled}" />
</Style>
<Style Selector="ComboBox:disabled /template/ TextBlock#PlaceholderTextBlock">
<Setter Property="Foreground" Value="{DynamicResource ComboBoxForegroundDisabled}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource ComboBoxForegroundDisabled}" />
</Style>
<Style Selector="ComboBox:disabled /template/ PathIcon#DropDownGlyph">
<Setter Property="Foreground" Value="{DynamicResource ComboBoxDropDownGlyphForegroundDisabled}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource ComboBoxDropDownGlyphForegroundDisabled}" />
</Style>
<!-- Focused State -->
@ -199,28 +199,28 @@
</Style>
<Style Selector="ComboBox:focus-visible /template/ ContentControl#ContentPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ComboBoxForegroundFocused}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource ComboBoxForegroundFocused}" />
</Style>
<Style Selector="ComboBox:focus-visible /template/ TextBlock#PlaceholderTextBlock">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ComboBoxForegroundFocused}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource ComboBoxForegroundFocused}" />
</Style>
<Style Selector="ComboBox:focus-visible /template/ PathIcon#DropDownGlyph">
<Setter Property="Foreground" Value="{DynamicResource ComboBoxDropDownGlyphForegroundFocused}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource ComboBoxDropDownGlyphForegroundFocused}" />
</Style>
<!-- Focus Pressed State -->
<Style Selector="ComboBox:focused:pressed /template/ ContentControl#ContentPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ComboBoxForegroundFocusedPressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource ComboBoxForegroundFocusedPressed}" />
</Style>
<Style Selector="ComboBox:focused:pressed /template/ TextBlock#PlaceholderTextBlock">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ComboBoxPlaceHolderForegroundFocusedPressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource ComboBoxPlaceHolderForegroundFocusedPressed}" />
</Style>
<Style Selector="ComboBox:focused:pressed /template/ PathIcon#DropDownGlyph">
<Setter Property="Foreground" Value="{DynamicResource ComboBoxDropDownGlyphForegroundFocusedPressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource ComboBoxDropDownGlyphForegroundFocusedPressed}" />
</Style>
<!-- Error State -->

18
src/Avalonia.Themes.Fluent/Controls/ComboBoxItem.xaml

@ -18,14 +18,14 @@
</Design.PreviewWith>
<Style Selector="ComboBoxItem">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ComboBoxItemForeground}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource ComboBoxItemForeground}" />
<Setter Property="Background" Value="{DynamicResource ComboBoxItemBackground}" />
<Setter Property="Padding" Value="{DynamicResource ComboBoxItemThemePadding}" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="Template">
<ControlTemplate>
<ContentPresenter Name="PART_ContentPresenter"
TextBlock.Foreground="{TemplateBinding Foreground}"
Foreground="{TemplateBinding Foreground}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
@ -43,48 +43,48 @@
<Style Selector="ComboBoxItem:pointerover /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ComboBoxItemBackgroundPointerOver}" />
<Setter Property="BorderBrush" Value="{DynamicResource ComboBoxItemBorderBrushPointerOver}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ComboBoxItemForegroundPointerOver}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource ComboBoxItemForegroundPointerOver}" />
</Style>
<!-- Disabled state -->
<Style Selector="ComboBoxItem:disabled /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ComboBoxItemBackgroundDisabled}" />
<Setter Property="BorderBrush" Value="{DynamicResource ComboBoxItemBorderBrushDisabled}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ComboBoxItemForegroundDisabled}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource ComboBoxItemForegroundDisabled}" />
</Style>
<!-- Pressed state -->
<Style Selector="ComboBoxItem:pressed /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ComboBoxItemBackgroundPressed}" />
<Setter Property="BorderBrush" Value="{DynamicResource ComboBoxItemBorderBrushPressed}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ComboBoxItemForegroundPressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource ComboBoxItemForegroundPressed}" />
</Style>
<!-- Selected state -->
<Style Selector="ComboBoxItem:selected /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ComboBoxItemBackgroundSelected}" />
<Setter Property="BorderBrush" Value="{DynamicResource ComboBoxItemBorderBrushSelected}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ComboBoxItemForegroundSelected}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource ComboBoxItemForegroundSelected}" />
</Style>
<!-- Selected Disabled state -->
<Style Selector="ComboBoxItem:selected:disabled /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ComboBoxItemBackgroundSelectedDisabled}" />
<Setter Property="BorderBrush" Value="{DynamicResource ComboBoxItemBorderBrushSelectedDisabled}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ComboBoxItemForegroundSelectedDisabled}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource ComboBoxItemForegroundSelectedDisabled}" />
</Style>
<!-- Selected PointerOver state -->
<Style Selector="ComboBoxItem:selected:pointerover /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ComboBoxItemBackgroundSelectedPointerOver}" />
<Setter Property="BorderBrush" Value="{DynamicResource ComboBoxItemBorderBrushSelectedPointerOver}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ComboBoxItemForegroundSelectedPointerOver}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource ComboBoxItemForegroundSelectedPointerOver}" />
</Style>
<!-- Selected Pressed state -->
<Style Selector="ComboBoxItem:selected:pressed /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ComboBoxItemBackgroundSelectedPressed}" />
<Setter Property="BorderBrush" Value="{DynamicResource ComboBoxItemBorderBrushSelectedPressed}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ComboBoxItemForegroundSelectedPressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource ComboBoxItemForegroundSelectedPressed}" />
</Style>
</Styles>

2
src/Avalonia.Themes.Fluent/Controls/DataValidationErrors.xaml

@ -29,7 +29,7 @@
<Style Selector="DataValidationErrors">
<Style.Resources>
<DataTemplate x:Key="InlineDataValidationErrorTemplate">
<ItemsControl Items="{Binding}" x:DataType="DataValidationErrors" TextBlock.Foreground="{DynamicResource SystemControlErrorTextForegroundBrush}">
<ItemsControl Items="{Binding}" x:DataType="DataValidationErrors" TextElement.Foreground="{DynamicResource SystemControlErrorTextForegroundBrush}">
<ItemsControl.Styles>
<Style Selector="TextBlock">
<Setter Property="TextWrapping" Value="Wrap" />

22
src/Avalonia.Themes.Fluent/Controls/DatePicker.xaml

@ -43,7 +43,7 @@
</Style>
<Style Selector="ListBoxItem.DateTimePickerItem:selected /template/ ContentPresenter">
<Setter Property="Background" Value="Transparent" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SystemControlForegroundBaseHighBrush}"/>
<Setter Property="TextElement.Foreground" Value="{DynamicResource SystemControlForegroundBaseHighBrush}"/>
</Style>
<Style Selector="ListBoxItem.DateTimePickerItem.MonthItem">
<Setter Property="Padding" Value="{DynamicResource DatePickerFlyoutPresenterMonthPadding}"/>
@ -70,7 +70,7 @@
BorderBrush="{DynamicResource DateTimePickerFlyoutButtonBorderBrush}"
BorderThickness="{DynamicResource DateTimeFlyoutButtonBorderThickness}"
Content="{TemplateBinding Content}"
TextBlock.Foreground="{DynamicResource SystemControlHighlightAltBaseHighBrush}"
Foreground="{DynamicResource SystemControlHighlightAltBaseHighBrush}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Padding="{TemplateBinding Padding}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
@ -84,13 +84,13 @@
<Style Selector=":is(Button).DateTimeFlyoutButtonStyle:pointerover /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource DateTimePickerFlyoutButtonBackgroundPointerOver}"/>
<Setter Property="BorderBrush" Value="{DynamicResource DateTimePickerFlyoutButtonBorderBrushPointerOver}"/>
<Setter Property="TextBlock.Foreground" Value="{DynamicResource DateTimePickerFlyoutButtonForegroundPointerOver}"/>
<Setter Property="TextElement.Foreground" Value="{DynamicResource DateTimePickerFlyoutButtonForegroundPointerOver}"/>
</Style>
<Style Selector=":is(Button).DateTimeFlyoutButtonStyle:pressed /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource DateTimePickerFlyoutButtonBackgroundPressed}"/>
<Setter Property="BorderBrush" Value="{DynamicResource DateTimePickerFlyoutButtonBorderBrushPressed}"/>
<Setter Property="TextBlock.Foreground" Value="{DynamicResource DateTimePickerFlyoutButtonForegroundPressed}"/>
<Setter Property="TextElement.Foreground" Value="{DynamicResource DateTimePickerFlyoutButtonForegroundPressed}"/>
</Style>
@ -125,7 +125,7 @@
<Style Selector="DatePicker">
<Setter Property="FontSize" Value="{DynamicResource ControlContentThemeFontSize}" />
<Setter Property="Foreground" Value="{DynamicResource DatePickerButtonForeground}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource DatePickerButtonForeground}" />
<Setter Property="Background" Value="{DynamicResource DatePickerButtonBackground}"/>
<Setter Property="BorderBrush" Value="{DynamicResource DatePickerButtonBorderBrush}"/>
<Setter Property="BorderThickness" Value="1"/>
@ -165,7 +165,7 @@
Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness}"
Content="{TemplateBinding Content}"
TextBlock.Foreground="{TemplateBinding Foreground}"
Foreground="{TemplateBinding Foreground}"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
CornerRadius="{TemplateBinding CornerRadius}"/>
@ -212,7 +212,7 @@
</Setter>
</Style>
<Style Selector="DatePicker /template/ ContentPresenter#HeaderContentPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource DatePickerHeaderForeground}"/>
<Setter Property="TextElement.Foreground" Value="{DynamicResource DatePickerHeaderForeground}"/>
</Style>
<Style Selector="DatePicker:disabled /template/ Rectangle">
<Setter Property="Fill" Value="{DynamicResource DatePickerSpacerFillDisabled}"/>
@ -221,24 +221,24 @@
<Style Selector="DatePicker /template/ Button#FlyoutButton:pointerover /template/ ContentPresenter">
<Setter Property="BorderBrush" Value="{DynamicResource DatePickerButtonBorderBrushPointerOver}"/>
<Setter Property="Background" Value="{DynamicResource DatePickerButtonBackgroundPointerOver}"/>
<Setter Property="TextBlock.Foreground" Value="{DynamicResource DatePickerButtonForegroundPointerOver}"/>
<Setter Property="TextElement.Foreground" Value="{DynamicResource DatePickerButtonForegroundPointerOver}"/>
</Style>
<Style Selector="DatePicker /template/ Button#FlyoutButton:pressed /template/ ContentPresenter">
<Setter Property="BorderBrush" Value="{DynamicResource DatePickerButtonBorderBrushPressed}"/>
<Setter Property="Background" Value="{DynamicResource DatePickerButtonBackgroundPressed}"/>
<Setter Property="TextBlock.Foreground" Value="{DynamicResource DatePickerButtonForegroundPressed}"/>
<Setter Property="TextElement.Foreground" Value="{DynamicResource DatePickerButtonForegroundPressed}"/>
</Style>
<Style Selector="DatePicker /template/ Button#FlyoutButton:disabled /template/ ContentPresenter">
<Setter Property="BorderBrush" Value="{DynamicResource DatePickerButtonBorderBrushDisabled}"/>
<Setter Property="Background" Value="{DynamicResource DatePickerButtonBackgroundDisabled}"/>
<Setter Property="TextBlock.Foreground" Value="{DynamicResource DatePickerButtonForegroundDisabled}"/>
<Setter Property="TextElement.Foreground" Value="{DynamicResource DatePickerButtonForegroundDisabled}"/>
</Style>
<!-- Changes foreground for watermark text when SelectedDate is null-->
<Style Selector="DatePicker:hasnodate /template/ Button#FlyoutButton TextBlock">
<Setter Property="Foreground" Value="{DynamicResource TextControlPlaceholderForeground}"/>
<Setter Property="TextElement.Foreground" Value="{DynamicResource TextControlPlaceholderForeground}"/>
</Style>
<Style Selector="DatePicker:error /template/ Button#FlyoutButton /template/ ContentPresenter">

2
src/Avalonia.Themes.Fluent/Controls/Expander.xaml

@ -110,7 +110,7 @@
BorderThickness="0"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
TextBlock.Foreground="{DynamicResource ExpanderForeground}" />
TextElement.Foreground="{DynamicResource ExpanderForeground}" />
<Border x:Name="ExpandCollapseChevronBorder"
Grid.Column="1"
Width="32"

2
src/Avalonia.Themes.Fluent/Controls/ListBox.xaml

@ -10,7 +10,7 @@
</Border>
</Design.PreviewWith>
<Style Selector="ListBox">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SystemControlForegroundBaseHighBrush}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SystemControlForegroundBaseHighBrush}" />
<Setter Property="Background" Value="{DynamicResource SystemControlBackgroundChromeMediumLowBrush}" />
<Setter Property="BorderBrush" Value="{DynamicResource SystemControlForegroundBaseHighBrush}" />
<Setter Property="BorderThickness" Value="{DynamicResource ListBoxBorderThemeThickness}" />

14
src/Avalonia.Themes.Fluent/Controls/ListBoxItem.xaml

@ -41,7 +41,7 @@
<!-- Disabled State -->
<Style Selector="ListBoxItem:disabled /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SystemControlDisabledBaseMediumLowBrush}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SystemControlDisabledBaseMediumLowBrush}" />
</Style>
<!-- PointerOver State -->
@ -49,7 +49,7 @@
<Setter Property="Background" Value="{DynamicResource SystemControlHighlightListLowBrush}" />
</Style>
<Style Selector="ListBoxItem:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SystemControlHighlightAltBaseHighBrush}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SystemControlHighlightAltBaseHighBrush}" />
</Style>
<!-- Pressed State -->
@ -57,7 +57,7 @@
<Setter Property="Background" Value="{DynamicResource SystemControlHighlightListMediumBrush}" />
</Style>
<Style Selector="ListBoxItem:pressed /template/ ContentPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SystemControlHighlightAltBaseHighBrush}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SystemControlHighlightAltBaseHighBrush}" />
</Style>
<!-- Selected State -->
@ -65,7 +65,7 @@
<Setter Property="Background" Value="{DynamicResource SystemControlHighlightListAccentLowBrush}" />
</Style>
<Style Selector="ListBoxItem:selected /template/ ContentPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SystemControlHighlightAltBaseHighBrush}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SystemControlHighlightAltBaseHighBrush}" />
</Style>
<!-- Selected Unfocused State -->
@ -73,7 +73,7 @@
<Setter Property="Background" Value="{DynamicResource SystemControlHighlightListAccentLowBrush}" />
</Style>
<Style Selector="ListBoxItem:selected:not(:focus) /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SystemControlHighlightAltBaseHighBrush}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SystemControlHighlightAltBaseHighBrush}" />
</Style>
<!-- Selected PointerOver State -->
@ -81,7 +81,7 @@
<Setter Property="Background" Value="{DynamicResource SystemControlHighlightListAccentMediumBrush}" />
</Style>
<Style Selector="ListBoxItem:selected:pointerover /template/ ContentPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SystemControlHighlightAltBaseHighBrush}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SystemControlHighlightAltBaseHighBrush}" />
</Style>
<!-- Selected Pressed State -->
@ -89,6 +89,6 @@
<Setter Property="Background" Value="{DynamicResource SystemControlHighlightListAccentHighBrush}" />
</Style>
<Style Selector="ListBoxItem:selected:pressed /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SystemControlHighlightAltBaseHighBrush}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SystemControlHighlightAltBaseHighBrush}" />
</Style>
</Styles>

16
src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml

@ -53,7 +53,7 @@
<Style Selector="MenuItem">
<Setter Property="Background" Value="{DynamicResource MenuFlyoutItemBackground}" />
<Setter Property="Foreground" Value="{DynamicResource MenuFlyoutItemForeground}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource MenuFlyoutItemForeground}" />
<Setter Property="Padding" Value="{DynamicResource MenuFlyoutItemThemePadding}" />
<Setter Property="FontSize" Value="{DynamicResource ControlContentThemeFontSize}" />
<Setter Property="Template">
@ -208,7 +208,7 @@
</Style>
<Style Selector="MenuItem /template/ TextBlock#PART_InputGestureText">
<Setter Property="Foreground" Value="{DynamicResource MenuFlyoutItemKeyboardAcceleratorTextForeground}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource MenuFlyoutItemKeyboardAcceleratorTextForeground}" />
</Style>
<Style Selector="MenuItem /template/ Path#PART_ChevronPath">
<Setter Property="Fill" Value="{DynamicResource MenuFlyoutSubItemChevron}" />
@ -218,10 +218,10 @@
<Setter Property="Background" Value="{DynamicResource MenuFlyoutItemBackgroundPointerOver}" />
</Style>
<Style Selector="MenuItem:selected /template/ ContentPresenter#PART_HeaderPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource MenuFlyoutItemForegroundPointerOver}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource MenuFlyoutItemForegroundPointerOver}" />
</Style>
<Style Selector="MenuItem:selected /template/ TextBlock#PART_InputGestureText">
<Setter Property="Foreground" Value="{DynamicResource MenuFlyoutItemKeyboardAcceleratorTextForegroundPointerOver}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource MenuFlyoutItemKeyboardAcceleratorTextForegroundPointerOver}" />
</Style>
<Style Selector="MenuItem:selected /template/ Path#PART_ChevronPath">
<Setter Property="Fill" Value="{DynamicResource MenuFlyoutSubItemChevronPointerOver}" />
@ -232,10 +232,10 @@
<Setter Property="Background" Value="{DynamicResource MenuFlyoutItemBackgroundPressed}" />
</Style>
<Style Selector="MenuItem:pressed /template/ Border#PART_LayoutRoot:pointerover ContentPresenter#PART_HeaderPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource MenuFlyoutItemForegroundPressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource MenuFlyoutItemForegroundPressed}" />
</Style>
<Style Selector="MenuItem:pressed /template/ Border#PART_LayoutRoot:pointerover TextBlock#PART_InputGestureText">
<Setter Property="Foreground" Value="{DynamicResource MenuFlyoutItemKeyboardAcceleratorTextForegroundPressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource MenuFlyoutItemKeyboardAcceleratorTextForegroundPressed}" />
</Style>
<Style Selector="MenuItem:pressed /template/ Border#PART_LayoutRoot:pointerover Path#PART_ChevronPath">
<Setter Property="Fill" Value="{DynamicResource MenuFlyoutSubItemChevronPressed}" />
@ -245,10 +245,10 @@
<Setter Property="Background" Value="{DynamicResource MenuFlyoutItemBackgroundDisabled}" />
</Style>
<Style Selector="MenuItem:disabled /template/ ContentPresenter#PART_HeaderPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource MenuFlyoutItemForegroundDisabled}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource MenuFlyoutItemForegroundDisabled}" />
</Style>
<Style Selector="MenuItem:disabled /template/ TextBlock#PART_InputGestureText">
<Setter Property="Foreground" Value="{DynamicResource MenuFlyoutItemKeyboardAcceleratorTextForegroundDisabled}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource MenuFlyoutItemKeyboardAcceleratorTextForegroundDisabled}" />
</Style>
<Style Selector="MenuItem:disabled /template/ Path#PART_ChevronPath">
<Setter Property="Fill" Value="{DynamicResource MenuFlyoutSubItemChevronDisabled}" />

10
src/Avalonia.Themes.Fluent/Controls/RadioButton.xaml

@ -11,7 +11,7 @@
</Design.PreviewWith>
<Style Selector="RadioButton">
<Setter Property="Background" Value="{DynamicResource RadioButtonBackground}" />
<Setter Property="Foreground" Value="{DynamicResource RadioButtonForeground}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource RadioButtonForeground}" />
<Setter Property="BorderBrush" Value="{DynamicResource RadioButtonBorderBrush}" />
<Setter Property="CornerRadius" Value="{DynamicResource ControlCornerRadius}" />
<Setter Property="Padding" Value="8,0,0,0" />
@ -49,7 +49,7 @@
<ContentPresenter Name="PART_ContentPresenter"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
TextBlock.Foreground="{TemplateBinding Foreground}"
Foreground="{TemplateBinding Foreground}"
Margin="{TemplateBinding Padding}"
RecognizesAccessKey="True"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
@ -81,7 +81,7 @@
<!-- PointerOver State -->
<Style Selector="RadioButton:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="(TextBlock.Foreground)" Value="{DynamicResource RadioButtonForegroundPointerOver}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource RadioButtonForegroundPointerOver}" />
</Style>
<Style Selector="RadioButton:pointerover /template/ Border#RootBorder">
@ -107,7 +107,7 @@
<!-- Pressed State -->
<Style Selector="RadioButton:pressed /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="(TextBlock.Foreground)" Value="{DynamicResource RadioButtonForegroundPressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource RadioButtonForegroundPressed}" />
</Style>
<Style Selector="RadioButton:pressed /template/ Border#RootBorder">
@ -133,7 +133,7 @@
<!-- Disabled State -->
<Style Selector="RadioButton:disabled /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="(TextBlock.Foreground)" Value="{DynamicResource RadioButtonForegroundDisabled}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource RadioButtonForegroundDisabled}" />
</Style>
<Style Selector="RadioButton:disabled /template/ Border#RootBorder">

8
src/Avalonia.Themes.Fluent/Controls/RepeatButton.xaml

@ -12,7 +12,7 @@
<Style Selector="RepeatButton">
<Setter Property="Background" Value="{DynamicResource RepeatButtonBackground}" />
<!--<Setter Property="BackgroundSizing" Value="OuterBorderEdge" />-->
<Setter Property="Foreground" Value="{DynamicResource RepeatButtonForeground}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource RepeatButtonForeground}" />
<Setter Property="BorderBrush" Value="{DynamicResource RepeatButtonBorderBrush}" />
<Setter Property="BorderThickness" Value="{DynamicResource ButtonBorderThemeThickness}" />
<Setter Property="CornerRadius" Value="{DynamicResource ControlCornerRadius}" />
@ -46,18 +46,18 @@
<Style Selector="RepeatButton:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{DynamicResource RepeatButtonBackgroundPointerOver}" />
<Setter Property="BorderBrush" Value="{DynamicResource RepeatButtonBorderBrushPointerOver}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource RepeatButtonForegroundPointerOver}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource RepeatButtonForegroundPointerOver}" />
</Style>
<Style Selector="RepeatButton:pressed /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{DynamicResource RepeatButtonBackgroundPressed}" />
<Setter Property="BorderBrush" Value="{DynamicResource RepeatButtonBorderBrushPressed}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource RepeatButtonForegroundPressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource RepeatButtonForegroundPressed}" />
</Style>
<Style Selector="RepeatButton:disabled /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{DynamicResource RepeatButtonBackgroundDisabled}" />
<Setter Property="BorderBrush" Value="{DynamicResource RepeatButtonBorderBrushDisabled}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource RepeatButtonForegroundDisabled}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource RepeatButtonForegroundDisabled}" />
</Style>
</Styles>

12
src/Avalonia.Themes.Fluent/Controls/Slider.xaml

@ -38,14 +38,14 @@
<Style Selector="Slider:horizontal">
<Setter Property="Background" Value="{DynamicResource SliderTrackFill}" />
<Setter Property="BorderThickness" Value="{DynamicResource SliderBorderThemeThickness}" />
<Setter Property="Foreground" Value="{DynamicResource SliderTrackValueFill}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SliderTrackValueFill}" />
<Setter Property="FontSize" Value="{DynamicResource ControlContentThemeFontSize}" />
<Setter Property="Template">
<ControlTemplate>
<DataValidationErrors>
<Border BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}" CornerRadius="{TemplateBinding CornerRadius}">
<Grid Name="grid" Margin="{TemplateBinding Padding}" RowDefinitions="Auto, *">
<ContentPresenter x:Name="HeaderContentPresenter" Grid.Row="0" TextBlock.FontWeight="{DynamicResource SliderHeaderThemeFontWeight}" TextBlock.Foreground="{DynamicResource SliderHeaderForeground}"
<ContentPresenter x:Name="HeaderContentPresenter" Grid.Row="0" FontWeight="{DynamicResource SliderHeaderThemeFontWeight}" Foreground="{DynamicResource SliderHeaderForeground}"
Margin="{DynamicResource SliderTopHeaderMargin}" />
<Grid x:Name="SliderContainer" Grid.Row="1">
<Grid.Styles>
@ -100,14 +100,14 @@
<Style Selector="Slider:vertical">
<Setter Property="Background" Value="{DynamicResource SliderTrackFill}" />
<Setter Property="BorderThickness" Value="{DynamicResource SliderBorderThemeThickness}" />
<Setter Property="Foreground" Value="{DynamicResource SliderTrackValueFill}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SliderTrackValueFill}" />
<Setter Property="FontSize" Value="{DynamicResource ControlContentThemeFontSize}" />
<Setter Property="Template">
<ControlTemplate>
<DataValidationErrors>
<Border BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}" CornerRadius="{TemplateBinding CornerRadius}">
<Grid Name="grid" Margin="{TemplateBinding Padding}" RowDefinitions="Auto, *">
<ContentPresenter x:Name="HeaderContentPresenter" Grid.Row="0" TextBlock.FontWeight="{DynamicResource SliderHeaderThemeFontWeight}" TextBlock.Foreground="{DynamicResource SliderHeaderForeground}"
<ContentPresenter x:Name="HeaderContentPresenter" Grid.Row="0" FontWeight="{DynamicResource SliderHeaderThemeFontWeight}" Foreground="{DynamicResource SliderHeaderForeground}"
Margin="{DynamicResource SliderTopHeaderMargin}" />
<Grid x:Name="SliderContainer" Grid.Row="1">
<Grid.Styles>
@ -208,7 +208,7 @@
<!-- Disabled State -->
<Style Selector="Slider:disabled /template/ ContentPresenter#HeaderContentPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SliderHeaderForegroundDisabled}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SliderHeaderForegroundDisabled}" />
</Style>
<Style Selector="Slider:disabled /template/ RepeatButton#PART_DecreaseButton">
@ -270,6 +270,6 @@
</Style>
<Style Selector="Slider:error">
<Setter Property="Foreground" Value="{DynamicResource SystemControlErrorTextForegroundBrush}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SystemControlErrorTextForegroundBrush}" />
</Style>
</Styles>

46
src/Avalonia.Themes.Fluent/Controls/SplitButton.xaml

@ -32,7 +32,7 @@
<Style Selector="SplitButton">
<Setter Property="Background" Value="{DynamicResource SplitButtonBackground}" />
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForeground}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForeground}" />
<Setter Property="BorderBrush" Value="{DynamicResource SplitButtonBorderBrush}" />
<Setter Property="BorderThickness" Value="{DynamicResource SplitButtonBorderThemeThickness}" />
<Setter Property="HorizontalAlignment" Value="Left" />
@ -122,10 +122,10 @@
SplitButton /template/ Button#PART_SecondaryButton:pointerover /template/ ContentPresenter">
<Setter Property="Border.Background" Value="{DynamicResource SplitButtonBackgroundPointerOver}" />
<Setter Property="Border.BorderBrush" Value="{DynamicResource SplitButtonBorderBrushPointerOver}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundPointerOver}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForegroundPointerOver}" />
</Style>
<Style Selector="SplitButton /template/ Button#PART_SecondaryButton:pointerover PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundPointerOver}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForegroundPointerOver}" />
</Style>
<!-- Primary and Secondary buttons Pressed State -->
@ -133,10 +133,10 @@
SplitButton /template/ Button#PART_SecondaryButton:pressed /template/ ContentPresenter">
<Setter Property="Border.Background" Value="{DynamicResource SplitButtonBackgroundPressed}" />
<Setter Property="Border.BorderBrush" Value="{DynamicResource SplitButtonBorderBrushPressed}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundPressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForegroundPressed}" />
</Style>
<Style Selector="SplitButton /template/ Button#PART_SecondaryButton:pressed PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundPressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForegroundPressed}" />
</Style>
<!-- SplitButton Pressed State -->
@ -145,10 +145,10 @@
SplitButton:pressed /template/ Border#SeparatorBorder">
<Setter Property="Border.Background" Value="{DynamicResource SplitButtonBackgroundPressed}" />
<Setter Property="Border.BorderBrush" Value="{DynamicResource SplitButtonBorderBrushPressed}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundPressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForegroundPressed}" />
</Style>
<Style Selector="SplitButton:pressed /template/ Button#PART_SecondaryButton PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundPressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForegroundPressed}" />
</Style>
<!-- Primary and Secondary buttons Flyout Open State -->
@ -157,10 +157,10 @@
SplitButton:flyout-open /template/ Border#SeparatorBorder">
<Setter Property="Border.Background" Value="{DynamicResource SplitButtonBackgroundPressed}" />
<Setter Property="Border.BorderBrush" Value="{DynamicResource SplitButtonBorderBrushPressed}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundPressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForegroundPressed}" />
</Style>
<Style Selector="SplitButton:flyout-open /template/ Button#PART_SecondaryButton PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundPressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForegroundPressed}" />
</Style>
<!-- Disabled State -->
@ -169,10 +169,10 @@
SplitButton:disabled /template/ Border#SeparatorBorder">
<Setter Property="Border.Background" Value="{DynamicResource SplitButtonBackgroundDisabled}" />
<Setter Property="Border.BorderBrush" Value="{DynamicResource SplitButtonBorderBrushDisabled}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundDisabled}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForegroundDisabled}" />
</Style>
<Style Selector="SplitButton:disabled /template/ Button#PART_SecondaryButton PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundDisabled}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForegroundDisabled}" />
</Style>
<!-- Checked State -->
@ -181,10 +181,10 @@
SplitButton:checked /template/ Border#SeparatorBorder">
<Setter Property="Border.Background" Value="{DynamicResource SplitButtonBackgroundChecked}" />
<Setter Property="Border.BorderBrush" Value="{DynamicResource SplitButtonBorderBrushChecked}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundChecked}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForegroundChecked}" />
</Style>
<Style Selector="SplitButton:checked /template/ Button#PART_SecondaryButton PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundChecked}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForegroundChecked}" />
</Style>
<!-- Checked PointerOver State -->
@ -192,10 +192,10 @@
SplitButton:checked /template/ Button#PART_SecondaryButton:pointerover /template/ ContentPresenter">
<Setter Property="Border.Background" Value="{DynamicResource SplitButtonBackgroundCheckedPointerOver}" />
<Setter Property="Border.BorderBrush" Value="{DynamicResource SplitButtonBorderBrushCheckedPointerOver}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPointerOver}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPointerOver}" />
</Style>
<Style Selector="SplitButton:checked /template/ Button#PART_SecondaryButton:pointerover PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPointerOver}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPointerOver}" />
</Style>
<!-- Checked Pressed State -->
@ -203,10 +203,10 @@
SplitButton:checked /template/ Button#PART_SecondaryButton:pressed /template/ ContentPresenter">
<Setter Property="Border.Background" Value="{DynamicResource SplitButtonBackgroundCheckedPressed}" />
<Setter Property="Border.BorderBrush" Value="{DynamicResource SplitButtonBorderBrushCheckedPressed}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
</Style>
<Style Selector="SplitButton:checked /template/ Button#PART_SecondaryButton:pressed PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
</Style>
<!-- SplitButton Checked Pressed State -->
@ -215,10 +215,10 @@
SplitButton:pressed:checked /template/ Border#SeparatorBorder">
<Setter Property="Border.Background" Value="{DynamicResource SplitButtonBackgroundCheckedPressed}" />
<Setter Property="Border.BorderBrush" Value="{DynamicResource SplitButtonBorderBrushCheckedPressed}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
</Style>
<Style Selector="SplitButton:pressed:checked /template/ Button#PART_SecondaryButton PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
</Style>
<!-- Checked Flyout Open State -->
@ -227,10 +227,10 @@
SplitButton:checked:flyout-open /template/ Border#SeparatorBorder">
<Setter Property="Border.Background" Value="{DynamicResource SplitButtonBackgroundCheckedPressed}" />
<Setter Property="Border.BorderBrush" Value="{DynamicResource SplitButtonBorderBrushCheckedPressed}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
</Style>
<Style Selector="SplitButton:checked:flyout-open /template/ Button#PART_SecondaryButton PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
</Style>
<!-- Disabled Checked State -->
@ -239,9 +239,9 @@
SplitButton:checked:disabled /template/ Border#SeparatorBorder">
<Setter Property="Border.Background" Value="{DynamicResource SplitButtonBackgroundCheckedDisabled}" />
<Setter Property="Border.BorderBrush" Value="{DynamicResource SplitButtonBorderBrushCheckedDisabled}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundCheckedDisabled}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForegroundCheckedDisabled}" />
</Style>
<Style Selector="SplitButton:checked:disabled /template/ Button#PART_SecondaryButton PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundCheckedDisabled}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SplitButtonForegroundCheckedDisabled}" />
</Style>
</Styles>

20
src/Avalonia.Themes.Fluent/Controls/TabItem.xaml

@ -19,7 +19,7 @@
<Setter Property="FontSize" Value="{DynamicResource TabItemHeaderFontSize}" />
<Setter Property="FontWeight" Value="{DynamicResource TabItemHeaderThemeFontWeight}" />
<Setter Property="Background" Value="{DynamicResource TabItemHeaderBackgroundUnselected}" />
<Setter Property="Foreground" Value="{DynamicResource TabItemHeaderForegroundUnselected}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource TabItemHeaderForegroundUnselected}" />
<Setter Property="Padding" Value="{DynamicResource TabItemHeaderMargin}" />
<Setter Property="Margin" Value="0" />
<Setter Property="MinHeight" Value="{DynamicResource TabItemMinHeight}" />
@ -37,9 +37,9 @@
Content="{TemplateBinding Header}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
TextBlock.FontFamily="{TemplateBinding FontFamily}"
TextBlock.FontSize="{TemplateBinding FontSize}"
TextBlock.FontWeight="{TemplateBinding FontWeight}" />
TextElement.FontFamily="{TemplateBinding FontFamily}"
TextElement.FontSize="{TemplateBinding FontSize}"
TextElement.FontWeight="{TemplateBinding FontWeight}" />
<Border Name="PART_SelectedPipe"
Background="{DynamicResource TabItemHeaderSelectedPipeFill}" />
</Panel>
@ -61,7 +61,7 @@
<!-- We don't use selector to PART_LayoutRoot, so developer can override selected item background with TabStripItem.Background -->
<Style Selector="TabItem:selected">
<Setter Property="Background" Value="{DynamicResource TabItemHeaderBackgroundSelected}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource TabItemHeaderForegroundSelected}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource TabItemHeaderForegroundSelected}" />
</Style>
<Style Selector="TabItem:selected /template/ Border#PART_SelectedPipe">
<Setter Property="IsVisible" Value="True" />
@ -70,31 +70,31 @@
<!-- PointerOver state -->
<Style Selector="TabItem:pointerover /template/ Border#PART_LayoutRoot">
<Setter Property="Background" Value="{DynamicResource TabItemHeaderBackgroundUnselectedPointerOver}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource TabItemHeaderForegroundUnselectedPointerOver}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource TabItemHeaderForegroundUnselectedPointerOver}" />
</Style>
<!-- Selected PointerOver state -->
<Style Selector="TabItem:selected:pointerover /template/ Border#PART_LayoutRoot">
<Setter Property="Background" Value="{DynamicResource TabItemHeaderBackgroundSelectedPointerOver}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource TabItemHeaderForegroundSelectedPointerOver}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource TabItemHeaderForegroundSelectedPointerOver}" />
</Style>
<!-- Pressed state -->
<Style Selector="TabItem:pressed /template/ Border#PART_LayoutRoot">
<Setter Property="Background" Value="{DynamicResource TabItemHeaderBackgroundUnselectedPressed}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource TabItemHeaderForegroundUnselectedPressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource TabItemHeaderForegroundUnselectedPressed}" />
</Style>
<!-- Selected Pressed state -->
<Style Selector="TabItem:selected:pressed /template/ Border#PART_LayoutRoot">
<Setter Property="Background" Value="{DynamicResource TabItemHeaderBackgroundSelectedPressed}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource TabItemHeaderForegroundSelectedPressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource TabItemHeaderForegroundSelectedPressed}" />
</Style>
<!-- Disabled state -->
<Style Selector="TabItem:disabled /template/ Border#PART_LayoutRoot">
<Setter Property="Background" Value="{DynamicResource TabItemHeaderBackgroundDisabled}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource TabItemHeaderForegroundDisabled}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource TabItemHeaderForegroundDisabled}" />
</Style>
<!-- TabStripPlacement States Group -->

20
src/Avalonia.Themes.Fluent/Controls/TabStripItem.xaml

@ -18,7 +18,7 @@
<Setter Property="FontSize" Value="{DynamicResource TabItemHeaderFontSize}" />
<Setter Property="FontWeight" Value="{DynamicResource TabItemHeaderThemeFontWeight}" />
<Setter Property="Background" Value="{DynamicResource TabItemHeaderBackgroundUnselected}" />
<Setter Property="Foreground" Value="{DynamicResource TabItemHeaderForegroundUnselected}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource TabItemHeaderForegroundUnselected}" />
<Setter Property="Padding" Value="{DynamicResource TabItemHeaderMargin}" />
<Setter Property="Margin" Value="0" />
<Setter Property="MinHeight" Value="{DynamicResource TabStripItemMinHeight}" />
@ -36,9 +36,9 @@
Content="{TemplateBinding Content}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
TextBlock.FontFamily="{TemplateBinding FontFamily}"
TextBlock.FontSize="{TemplateBinding FontSize}"
TextBlock.FontWeight="{TemplateBinding FontWeight}" />
TextElement.FontFamily="{TemplateBinding FontFamily}"
TextElement.FontSize="{TemplateBinding FontSize}"
TextElement.FontWeight="{TemplateBinding FontWeight}" />
<Border Name="PART_SelectedPipe"
Background="{DynamicResource TabItemHeaderSelectedPipeFill}" />
</Panel>
@ -69,7 +69,7 @@
<!-- We don't use selector to PART_LayoutRoot, so developer can override selected item background with TabStripItem.Background -->
<Style Selector="TabStripItem:selected">
<Setter Property="Background" Value="{DynamicResource TabItemHeaderBackgroundSelected}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource TabItemHeaderForegroundSelected}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource TabItemHeaderForegroundSelected}" />
</Style>
<Style Selector="TabStripItem:selected /template/ Border#PART_SelectedPipe">
<Setter Property="IsVisible" Value="True" />
@ -78,30 +78,30 @@
<!-- PointerOver state -->
<Style Selector="TabStripItem:pointerover /template/ Border#PART_LayoutRoot">
<Setter Property="Background" Value="{DynamicResource TabItemHeaderBackgroundUnselectedPointerOver}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource TabItemHeaderForegroundUnselectedPointerOver}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource TabItemHeaderForegroundUnselectedPointerOver}" />
</Style>
<!-- Selected PointerOver state -->
<Style Selector="TabStripItem:selected:pointerover /template/ Border#PART_LayoutRoot">
<Setter Property="Background" Value="{DynamicResource TabItemHeaderBackgroundSelectedPointerOver}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource TabItemHeaderForegroundSelectedPointerOver}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource TabItemHeaderForegroundSelectedPointerOver}" />
</Style>
<!-- Pressed state -->
<Style Selector="TabStripItem:pressed /template/ Border#PART_LayoutRoot">
<Setter Property="Background" Value="{DynamicResource TabItemHeaderBackgroundUnselectedPressed}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource TabItemHeaderForegroundUnselectedPressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource TabItemHeaderForegroundUnselectedPressed}" />
</Style>
<!-- Selected Pressed state -->
<Style Selector="TabStripItem:selected:pressed /template/ Border#PART_LayoutRoot">
<Setter Property="Background" Value="{DynamicResource TabItemHeaderBackgroundSelectedPressed}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource TabItemHeaderForegroundSelectedPressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource TabItemHeaderForegroundSelectedPressed}" />
</Style>
<!-- Disabled state -->
<Style Selector="TabStripItem:disabled /template/ Border#PART_LayoutRoot">
<Setter Property="Background" Value="{DynamicResource TabItemHeaderBackgroundDisabled}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource TabItemHeaderForegroundDisabled}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource TabItemHeaderForegroundDisabled}" />
</Style>
</Styles>

16
src/Avalonia.Themes.Fluent/Controls/TimePicker.xaml

@ -34,7 +34,7 @@
<Style Selector="TimePicker">
<Setter Property="FontSize" Value="{DynamicResource ControlContentThemeFontSize}" />
<Setter Property="Foreground" Value="{DynamicResource TimePickerButtonForeground}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource TimePickerButtonForeground}" />
<Setter Property="Background" Value="{DynamicResource TimePickerButtonBackground}"/>
<Setter Property="BorderBrush" Value="{DynamicResource TimePickerButtonBorderBrush}"/>
<Setter Property="BorderThickness" Value="{DynamicResource TimePickerBorderThemeThickness}"/>
@ -51,7 +51,7 @@
ContentTemplate="{TemplateBinding HeaderTemplate}"
Margin="{DynamicResource TimePickerTopHeaderMargin}"
MaxWidth="{DynamicResource TimePickerThemeMaxWidth}"
TextBlock.Foreground="{DynamicResource TimePickerHeaderForeground}"
Foreground="{DynamicResource TimePickerHeaderForeground}"
HorizontalAlignment="Stretch"
VerticalAlignment="Top" />
@ -77,7 +77,7 @@
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}"
Content="{TemplateBinding Content}"
TextBlock.Foreground="{TemplateBinding Foreground}"
Foreground="{TemplateBinding Foreground}"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch" />
</ControlTemplate>
@ -139,7 +139,7 @@
</Style>
<Style Selector="TimePicker:disabled /template/ ContentPresenter#HeaderContentPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource TimePickerHeaderForegroundDisabled}"/>
<Setter Property="TextElement.Foreground" Value="{DynamicResource TimePickerHeaderForegroundDisabled}"/>
</Style>
<Style Selector="TimePicker:disabled /template/ Rectangle">
<Setter Property="Fill" Value="{DynamicResource TimePickerSpacerFillDisabled}"/>
@ -148,23 +148,23 @@
<Style Selector="TimePicker /template/ Button#FlyoutButton:pointerover /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource TimePickerButtonBackgroundPointerOver}"/>
<Setter Property="BorderBrush" Value="{DynamicResource TimePickerButtonBorderBrushPointerOver}"/>
<Setter Property="TextBlock.Foreground" Value="{DynamicResource TimePickerButtonForegroundPointerOver}"/>
<Setter Property="TextElement.Foreground" Value="{DynamicResource TimePickerButtonForegroundPointerOver}"/>
</Style>
<Style Selector="TimePicker /template/ Button:pressed /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource TimePickerButtonBackgroundPressed}"/>
<Setter Property="BorderBrush" Value="{DynamicResource TimePickerButtonBorderBrushPressed}"/>
<Setter Property="TextBlock.Foreground" Value="{DynamicResource TimePickerButtonForegroundPressed}"/>
<Setter Property="TextElement.Foreground" Value="{DynamicResource TimePickerButtonForegroundPressed}"/>
</Style>
<Style Selector="TimePicker /template/ Button:disabled /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource TimePickerButtonBackgroundDisabled}"/>
<Setter Property="BorderBrush" Value="{DynamicResource TimePickerButtonBorderBrushDisabled}"/>
<Setter Property="TextBlock.Foreground" Value="{DynamicResource TimePickerButtonForegroundDisabled}"/>
<Setter Property="TextElement.Foreground" Value="{DynamicResource TimePickerButtonForegroundDisabled}"/>
</Style>
<Style Selector="TimePicker:hasnotime /template/ Button#FlyoutButton TextBlock">
<Setter Property="Foreground" Value="{DynamicResource TextControlPlaceholderForeground}"/>
<Setter Property="TextElement.Foreground" Value="{DynamicResource TextControlPlaceholderForeground}"/>
</Style>
<Style Selector="TimePicker:error /template/ Button#FlyoutButton /template/ ContentPresenter">

24
src/Avalonia.Themes.Fluent/Controls/ToggleButton.xaml

@ -15,7 +15,7 @@
</Styles.Resources>
<Style Selector="ToggleButton">
<Setter Property="Background" Value="{DynamicResource ToggleButtonBackground}" />
<Setter Property="Foreground" Value="{DynamicResource ToggleButtonForeground}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource ToggleButtonForeground}" />
<Setter Property="BorderBrush" Value="{DynamicResource ToggleButtonBorderBrush}" />
<Setter Property="BorderThickness" Value="{DynamicResource ToggleButtonBorderThemeThickness}" />
<Setter Property="CornerRadius" Value="{DynamicResource ControlCornerRadius}" />
@ -48,66 +48,66 @@
<Style Selector="ToggleButton:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ToggleButtonBackgroundPointerOver}" />
<Setter Property="BorderBrush" Value="{DynamicResource ToggleButtonBorderBrushPointerOver}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ToggleButtonForegroundPointerOver}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource ToggleButtonForegroundPointerOver}" />
</Style>
<Style Selector="ToggleButton:pressed /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ToggleButtonBackgroundPressed}" />
<Setter Property="BorderBrush" Value="{DynamicResource ToggleButtonBorderBrushPressed}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ToggleButtonForegroundPressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource ToggleButtonForegroundPressed}" />
</Style>
<Style Selector="ToggleButton:disabled /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ToggleButtonBackgroundDisabled}" />
<Setter Property="BorderBrush" Value="{DynamicResource ToggleButtonBorderBrushDisabled}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ToggleButtonForegroundDisabled}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource ToggleButtonForegroundDisabled}" />
</Style>
<Style Selector="ToggleButton:checked /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ToggleButtonBackgroundChecked}" />
<Setter Property="BorderBrush" Value="{DynamicResource ToggleButtonBorderBrushChecked}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ToggleButtonForegroundChecked}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource ToggleButtonForegroundChecked}" />
</Style>
<Style Selector="ToggleButton:checked:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ToggleButtonBackgroundCheckedPointerOver}" />
<Setter Property="BorderBrush" Value="{DynamicResource ToggleButtonBorderBrushCheckedPointerOver}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ToggleButtonForegroundCheckedPointerOver}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource ToggleButtonForegroundCheckedPointerOver}" />
</Style>
<Style Selector="ToggleButton:checked:pressed /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ToggleButtonBackgroundCheckedPressed}" />
<Setter Property="BorderBrush" Value="{DynamicResource ToggleButtonBorderBrushCheckedPressed}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ToggleButtonForegroundCheckedPressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource ToggleButtonForegroundCheckedPressed}" />
</Style>
<Style Selector="ToggleButton:checked:disabled /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ToggleButtonBackgroundCheckedDisabled}" />
<Setter Property="BorderBrush" Value="{DynamicResource ToggleButtonBorderBrushCheckedDisabled}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ToggleButtonForegroundCheckedDisabled}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource ToggleButtonForegroundCheckedDisabled}" />
</Style>
<Style Selector="ToggleButton:indeterminate /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ToggleButtonBackgroundIndeterminate}" />
<Setter Property="BorderBrush" Value="{DynamicResource ToggleButtonBorderBrushIndeterminate}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ToggleButtonForegroundIndeterminate}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource ToggleButtonForegroundIndeterminate}" />
</Style>
<Style Selector="ToggleButton:indeterminate:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ToggleButtonBackgroundIndeterminatePointerOver}" />
<Setter Property="BorderBrush" Value="{DynamicResource ToggleButtonBorderBrushIndeterminatePointerOver}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ToggleButtonForegroundIndeterminatePointerOver}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource ToggleButtonForegroundIndeterminatePointerOver}" />
</Style>
<Style Selector="ToggleButton:indeterminate:pressed /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ToggleButtonBackgroundIndeterminatePressed}" />
<Setter Property="BorderBrush" Value="{DynamicResource ToggleButtonBorderBrushIndeterminatePressed}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ToggleButtonForegroundIndeterminatePressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource ToggleButtonForegroundIndeterminatePressed}" />
</Style>
<Style Selector="ToggleButton:indeterminate:disabled /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ToggleButtonBackgroundIndeterminateDisabled}" />
<Setter Property="BorderBrush" Value="{DynamicResource ToggleButtonBorderBrushIndeterminateDisabled}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ToggleButtonForegroundIndeterminateDisabled}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource ToggleButtonForegroundIndeterminateDisabled}" />
</Style>
</Styles>

16
src/Avalonia.Themes.Fluent/Controls/TreeViewItem.xaml

@ -58,7 +58,7 @@
<Setter Property="Background" Value="{DynamicResource TreeViewItemBackground}" />
<Setter Property="BorderBrush" Value="{DynamicResource TreeViewItemBorderBrush}" />
<Setter Property="BorderThickness" Value="{DynamicResource TreeViewItemBorderThemeThickness}" />
<Setter Property="Foreground" Value="{DynamicResource TreeViewItemForeground}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource TreeViewItemForeground}" />
<Setter Property="MinHeight" Value="{DynamicResource TreeViewItemMinHeight}" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Template">
@ -107,7 +107,7 @@
<Setter Property="BorderBrush" Value="{DynamicResource TreeViewItemBorderBrushPointerOver}" />
</Style>
<Style Selector="TreeViewItem /template/ Border#PART_LayoutRoot:pointerover > ContentPresenter#PART_HeaderPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource TreeViewItemForegroundPointerOver}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource TreeViewItemForegroundPointerOver}" />
</Style>
<!-- Pressed state -->
@ -116,7 +116,7 @@
<Setter Property="BorderBrush" Value="{DynamicResource TreeViewItemBorderBrushPressed}" />
</Style>
<Style Selector="TreeViewItem:pressed /template/ Border#PART_LayoutRoot:pointerover > ContentPresenter#PART_HeaderPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource TreeViewItemForegroundPressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource TreeViewItemForegroundPressed}" />
</Style>
<!-- Disabled state -->
@ -125,7 +125,7 @@
<Setter Property="BorderBrush" Value="{DynamicResource TreeViewItemBorderBrushDisabled}" />
</Style>
<Style Selector="TreeViewItem:disabled /template/ Border#PART_LayoutRoot > ContentPresenter#PART_HeaderPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource TreeViewItemForegroundDisabled}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource TreeViewItemForegroundDisabled}" />
</Style>
<!-- Selected state -->
@ -134,7 +134,7 @@
<Setter Property="BorderBrush" Value="{DynamicResource TreeViewItemBorderBrushSelected}" />
</Style>
<Style Selector="TreeViewItem:selected /template/ Border#PART_LayoutRoot > ContentPresenter#PART_HeaderPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource TreeViewItemForegroundSelected}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource TreeViewItemForegroundSelected}" />
</Style>
<!-- Selected PointerOver state -->
@ -143,7 +143,7 @@
<Setter Property="BorderBrush" Value="{DynamicResource TreeViewItemBorderBrushSelectedPointerOver}" />
</Style>
<Style Selector="TreeViewItem:selected /template/ Border#PART_LayoutRoot:pointerover > ContentPresenter#PART_HeaderPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource TreeViewItemForegroundSelectedPointerOver}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource TreeViewItemForegroundSelectedPointerOver}" />
</Style>
<!-- Selected Pressed state -->
@ -152,7 +152,7 @@
<Setter Property="BorderBrush" Value="{DynamicResource TreeViewItemBorderBrushSelectedPressed}" />
</Style>
<Style Selector="TreeViewItem:pressed:selected /template/ Border#PART_LayoutRoot:pointerover > ContentPresenter#PART_HeaderPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource TreeViewItemForegroundSelectedPressed}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource TreeViewItemForegroundSelectedPressed}" />
</Style>
<!-- Disabled Selected state -->
@ -161,7 +161,7 @@
<Setter Property="BorderBrush" Value="{DynamicResource TreeViewItemBorderBrushSelectedDisabled}" />
</Style>
<Style Selector="TreeViewItem:disabled:selected /template/ Border#PART_LayoutRoot > ContentPresenter#PART_HeaderPresenter">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource TreeViewItemForegroundSelectedDisabled}" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource TreeViewItemForegroundSelectedDisabled}" />
</Style>
<!-- ExpandCollapseChevron Group states -->

2
src/Avalonia.Themes.Fluent/Controls/WindowNotificationManager.xaml

@ -9,7 +9,7 @@
<ReversibleStackPanel Name="PART_Items">
<ReversibleStackPanel.DataTemplates>
<DataTemplate DataType="INotification">
<StackPanel Spacing="8" Margin="12" TextBlock.Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}">
<StackPanel Spacing="8" Margin="12" TextElement.Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}">
<TextBlock Text="{Binding Title}" FontWeight="Medium" />
<TextBlock MaxHeight="80" Text="{Binding Message}" TextWrapping="Wrap" Margin="0,0,12,0"/>
</StackPanel>

12
src/Avalonia.Visuals/ApiCompatBaseline.txt

@ -67,6 +67,8 @@ MembersMustExist : Member 'public void Avalonia.Media.Immutable.ImmutableRadialG
TypeCannotChangeClassification : Type 'Avalonia.Media.Immutable.ImmutableSolidColorBrush' is a 'class' in the implementation but is a 'struct' in the contract.
MembersMustExist : Member 'public void Avalonia.Media.Immutable.ImmutableSolidColorBrush..ctor(Avalonia.Media.Color, System.Double)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'protected void Avalonia.Media.Immutable.ImmutableTileBrush..ctor(Avalonia.Media.AlignmentX, Avalonia.Media.AlignmentY, Avalonia.RelativeRect, System.Double, Avalonia.RelativeRect, Avalonia.Media.Stretch, Avalonia.Media.TileMode, Avalonia.Visuals.Media.Imaging.BitmapInterpolationMode)' does not exist in the implementation but it does exist in the contract.
CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.DrawableTextRun.Baseline' is abstract in the implementation but is missing in the contract.
CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.DrawableTextRun.Baseline.get()' is abstract in the implementation but is missing in the contract.
MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.DrawableTextRun.Draw(Avalonia.Media.DrawingContext)' does not exist in the implementation but it does exist in the contract.
CannotAddAbstractMembers : Member 'public void Avalonia.Media.TextFormatting.DrawableTextRun.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' is abstract in the implementation but is missing in the contract.
CannotSealType : Type 'Avalonia.Media.TextFormatting.GenericTextParagraphProperties' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract.
@ -85,7 +87,7 @@ MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.ShapedTextC
MembersMustExist : Member 'public Avalonia.Media.TextFormatting.ShapedTextCharacters.SplitTextCharactersResult Avalonia.Media.TextFormatting.ShapedTextCharacters.Split(System.Int32)' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'Avalonia.Media.TextFormatting.ShapedTextCharacters.SplitTextCharactersResult' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'protected System.Boolean Avalonia.Media.TextFormatting.TextCharacters.TryGetRunProperties(Avalonia.Utilities.ReadOnlySlice<System.Char>, Avalonia.Media.Typeface, Avalonia.Media.Typeface, System.Int32)' does not exist in the implementation but it does exist in the contract.
CannotAddAbstractMembers : Member 'public System.Collections.Generic.IReadOnlyList<Avalonia.Media.TextFormatting.TextRun> Avalonia.Media.TextFormatting.TextCollapsingProperties.Collapse(Avalonia.Media.TextFormatting.TextLine)' is abstract in the implementation but is missing in the contract.
CannotAddAbstractMembers : Member 'public System.Collections.Generic.List<Avalonia.Media.TextFormatting.DrawableTextRun> Avalonia.Media.TextFormatting.TextCollapsingProperties.Collapse(Avalonia.Media.TextFormatting.TextLine)' is abstract in the implementation but is missing in the contract.
MembersMustExist : Member 'public Avalonia.Media.TextFormatting.TextCollapsingStyle Avalonia.Media.TextFormatting.TextCollapsingProperties.Style.get()' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'Avalonia.Media.TextFormatting.TextCollapsingStyle' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextEndOfLine..ctor()' does not exist in the implementation but it does exist in the contract.
@ -108,6 +110,7 @@ CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextForma
MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextLine.Draw(Avalonia.Media.DrawingContext)' does not exist in the implementation but it does exist in the contract.
CannotAddAbstractMembers : Member 'public void Avalonia.Media.TextFormatting.TextLine.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' is abstract in the implementation but is missing in the contract.
CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.Extent.get()' is abstract in the implementation but is missing in the contract.
CannotAddAbstractMembers : Member 'public System.Collections.Generic.IReadOnlyList<Avalonia.Media.TextFormatting.TextBounds> Avalonia.Media.TextFormatting.TextLine.GetTextBounds(System.Int32, System.Int32)' is abstract in the implementation but is missing in the contract.
CannotAddAbstractMembers : Member 'public System.Boolean Avalonia.Media.TextFormatting.TextLine.HasOverflowed.get()' is abstract in the implementation but is missing in the contract.
CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.Height.get()' is abstract in the implementation but is missing in the contract.
MembersMustExist : Member 'public Avalonia.Media.TextFormatting.TextLineMetrics Avalonia.Media.TextFormatting.TextLine.LineMetrics.get()' does not exist in the implementation but it does exist in the contract.
@ -120,6 +123,7 @@ CannotAddAbstractMembers : Member 'public System.Int32 Avalonia.Media.TextFormat
CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.Width.get()' is abstract in the implementation but is missing in the contract.
CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.WidthIncludingTrailingWhitespace.get()' is abstract in the implementation but is missing in the contract.
MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextLineBreak..ctor(System.Collections.Generic.IReadOnlyList<Avalonia.Media.TextFormatting.ShapedTextCharacters>)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public System.Collections.Generic.IReadOnlyList<Avalonia.Media.TextFormatting.ShapedTextCharacters> Avalonia.Media.TextFormatting.TextLineBreak.RemainingCharacters.get()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextLineMetrics..ctor(Avalonia.Size, System.Double, Avalonia.Media.TextFormatting.TextRange, System.Boolean)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.Media.TextFormatting.TextLineMetrics Avalonia.Media.TextFormatting.TextLineMetrics.Create(System.Collections.Generic.IEnumerable<Avalonia.Media.TextFormatting.TextRun>, Avalonia.Media.TextFormatting.TextRange, System.Double, Avalonia.Media.TextFormatting.TextParagraphProperties)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.Size Avalonia.Media.TextFormatting.TextLineMetrics.Size.get()' does not exist in the implementation but it does exist in the contract.
@ -130,8 +134,6 @@ CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextForma
CannotAddAbstractMembers : Member 'public System.Boolean Avalonia.Media.TextFormatting.TextParagraphProperties.FirstLineInParagraph.get()' is abstract in the implementation but is missing in the contract.
CannotAddAbstractMembers : Member 'public Avalonia.Media.FlowDirection Avalonia.Media.TextFormatting.TextParagraphProperties.FlowDirection.get()' is abstract in the implementation but is missing in the contract.
CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextParagraphProperties.Indent.get()' is abstract in the implementation but is missing in the contract.
CannotAddAbstractMembers : Member 'public Avalonia.Media.BaselineAlignment Avalonia.Media.TextFormatting.TextRunProperties.BaselineAlignment' is abstract in the implementation but is missing in the contract.
CannotAddAbstractMembers : Member 'public Avalonia.Media.BaselineAlignment Avalonia.Media.TextFormatting.TextRunProperties.BaselineAlignment.get()' is abstract in the implementation but is missing in the contract.
MembersMustExist : Member 'public Avalonia.Media.GlyphRun Avalonia.Media.TextFormatting.TextShaper.ShapeText(Avalonia.Utilities.ReadOnlySlice<System.Char>, Avalonia.Media.Typeface, System.Double, System.Globalization.CultureInfo)' does not exist in the implementation but it does exist in the contract.
CannotSealType : Type 'Avalonia.Media.TextFormatting.TextTrailingCharacterEllipsis' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract.
MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextTrailingCharacterEllipsis..ctor(System.Double, Avalonia.Media.TextFormatting.TextRunProperties)' does not exist in the implementation but it does exist in the contract.
@ -171,9 +173,9 @@ InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Size Avaloni
InterfacesShouldHaveSameMembers : Interface member 'public System.TimeSpan Avalonia.Platform.IPlatformSettings.TouchDoubleClickTime' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Size Avalonia.Platform.IPlatformSettings.TouchDoubleClickSize.get()' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public System.TimeSpan Avalonia.Platform.IPlatformSettings.TouchDoubleClickTime.get()' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Media.TextFormatting.ShapedBuffer Avalonia.Platform.ITextShaperImpl.ShapeText(Avalonia.Utilities.ReadOnlySlice<System.Char>, Avalonia.Media.GlyphTypeface, System.Double, System.Globalization.CultureInfo, System.SByte)' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Media.TextFormatting.ShapedBuffer Avalonia.Platform.ITextShaperImpl.ShapeText(Avalonia.Utilities.ReadOnlySlice<System.Char>, Avalonia.Media.TextFormatting.TextShaperOptions)' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Media.GlyphRun Avalonia.Platform.ITextShaperImpl.ShapeText(Avalonia.Utilities.ReadOnlySlice<System.Char>, Avalonia.Media.Typeface, System.Double, System.Globalization.CultureInfo)' is present in the contract but not in the implementation.
MembersMustExist : Member 'public Avalonia.Media.GlyphRun Avalonia.Platform.ITextShaperImpl.ShapeText(Avalonia.Utilities.ReadOnlySlice<System.Char>, Avalonia.Media.Typeface, System.Double, System.Globalization.CultureInfo)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'protected void Avalonia.Rendering.RendererBase.RenderFps(Avalonia.Platform.IDrawingContextImpl, Avalonia.Rect, System.Nullable<System.Int32>)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Utilities.ReadOnlySlice<T>..ctor(System.ReadOnlyMemory<T>, System.Int32, System.Int32)' does not exist in the implementation but it does exist in the contract.
Total Issues: 177
Total Issues: 179

4
src/Avalonia.Visuals/Media/FormattedText.cs

@ -56,7 +56,7 @@ namespace Avalonia.Media
FlowDirection flowDirection,
Typeface typeface,
double emSize,
IBrush foreground)
IBrush? foreground)
{
if (culture is null)
{
@ -160,7 +160,7 @@ namespace Avalonia.Media
/// <param name="foregroundBrush">Foreground brush</param>
/// <param name="startIndex">The start index of initial character to apply the change to.</param>
/// <param name="count">The number of characters the change should be applied to.</param>
public void SetForegroundBrush(IBrush foregroundBrush, int startIndex, int count)
public void SetForegroundBrush(IBrush? foregroundBrush, int startIndex, int count)
{
var limit = ValidateRange(startIndex, count);
for (var i = startIndex; i < limit;)

10
src/Avalonia.Visuals/Media/TextDecorationCollection.cs

@ -10,6 +10,16 @@ namespace Avalonia.Media
/// </summary>
public class TextDecorationCollection : AvaloniaList<TextDecoration>
{
public TextDecorationCollection()
{
}
public TextDecorationCollection(IEnumerable<TextDecoration> textDecorations) : base(textDecorations)
{
}
/// <summary>
/// Parses a <see cref="TextDecorationCollection"/> string.
/// </summary>

5
src/Avalonia.Visuals/Media/TextFormatting/DrawableTextRun.cs

@ -9,6 +9,11 @@
/// Gets the size.
/// </summary>
public abstract Size Size { get; }
/// <summary>
/// Run baseline in ratio relative to run height
/// </summary>
public abstract double Baseline { get; }
/// <summary>
/// Draws the <see cref="DrawableTextRun"/> at the given origin.

2
src/Avalonia.Visuals/Media/TextFormatting/FormattedTextSource.cs

@ -30,7 +30,7 @@ namespace Avalonia.Media.TextFormatting
if (runText.IsEmpty)
{
return new TextEndOfParagraph();
return null;
}
var textStyleRun = CreateTextStyleRun(runText, _defaultProperties, _textModifier);

2
src/Avalonia.Visuals/Media/TextFormatting/ShapedTextCharacters.cs

@ -38,6 +38,8 @@ namespace Avalonia.Media.TextFormatting
public FontMetrics FontMetrics { get; }
public override double Baseline => -FontMetrics.Ascent;
public override Size Size => GlyphRun.Size;
public GlyphRun GlyphRun

29
src/Avalonia.Visuals/Media/TextFormatting/TextBounds.cs

@ -0,0 +1,29 @@
using System.Collections.Generic;
namespace Avalonia.Media.TextFormatting
{
/// <summary>
/// The bounding rectangle of a range of characters
/// </summary>
public sealed class TextBounds
{
/// <summary>
/// Constructing TextBounds object
/// </summary>
internal TextBounds(Rect bounds, FlowDirection flowDirection)
{
Rectangle = bounds;
FlowDirection = flowDirection;
}
/// <summary>
/// Bounds rectangle
/// </summary>
public Rect Rectangle { get; }
/// <summary>
/// Text flow direction inside the boundary rectangle
/// </summary>
public FlowDirection FlowDirection { get; }
}
}

25
src/Avalonia.Visuals/Media/TextFormatting/TextCharacters.cs

@ -38,7 +38,7 @@ namespace Avalonia.Media.TextFormatting
/// Gets a list of <see cref="ShapeableTextCharacters"/>.
/// </summary>
/// <returns>The shapeable text characters.</returns>
internal IList<ShapeableTextCharacters> GetShapeableCharacters(ReadOnlySlice<char> runText, sbyte biDiLevel,
internal IReadOnlyList<ShapeableTextCharacters> GetShapeableCharacters(ReadOnlySlice<char> runText, sbyte biDiLevel,
ref TextRunProperties? previousProperties)
{
var shapeableCharacters = new List<ShapeableTextCharacters>(2);
@ -72,11 +72,11 @@ namespace Avalonia.Media.TextFormatting
var currentTypeface = defaultTypeface;
var previousTypeface = previousProperties?.Typeface;
if (TryGetShapeableLength(text, currentTypeface, out var count, out var script))
if (TryGetShapeableLength(text, currentTypeface, null, out var count, out var script))
{
if (script == Script.Common && previousTypeface is not null)
{
if(TryGetShapeableLength(text, previousTypeface.Value, out var fallbackCount, out _))
if(TryGetShapeableLength(text, previousTypeface.Value, defaultTypeface, out var fallbackCount, out _))
{
return new ShapeableTextCharacters(text.Take(fallbackCount),
defaultProperties.WithTypeface(previousTypeface.Value), biDiLevel);
@ -89,7 +89,7 @@ namespace Avalonia.Media.TextFormatting
if (previousTypeface is not null)
{
if(TryGetShapeableLength(text, previousTypeface.Value, out count, out _))
if(TryGetShapeableLength(text, previousTypeface.Value, defaultTypeface, out count, out _))
{
return new ShapeableTextCharacters(text.Take(count),
defaultProperties.WithTypeface(previousTypeface.Value), biDiLevel);
@ -118,7 +118,7 @@ namespace Avalonia.Media.TextFormatting
defaultTypeface.Stretch, defaultTypeface.FontFamily, defaultProperties.CultureInfo,
out currentTypeface);
if (matchFound && TryGetShapeableLength(text, currentTypeface, out count, out _))
if (matchFound && TryGetShapeableLength(text, currentTypeface, defaultTypeface, out count, out _))
{
//Fallback found
return new ShapeableTextCharacters(text.Take(count), defaultProperties.WithTypeface(currentTypeface),
@ -152,14 +152,19 @@ namespace Avalonia.Media.TextFormatting
/// </summary>
/// <param name="text">The text.</param>
/// <param name="typeface">The typeface that is used to find matching characters.</param>
/// <param name="defaultTypeface"></param>
/// <param name="length">The shapeable length.</param>
/// <param name="script"></param>
/// <returns></returns>
protected static bool TryGetShapeableLength(ReadOnlySlice<char> text, Typeface typeface, out int length,
protected static bool TryGetShapeableLength(
ReadOnlySlice<char> text,
Typeface typeface,
Typeface? defaultTypeface,
out int length,
out Script script)
{
length = 0;
script = Script.Unknown;
script = Script.Unknown;
if (text.Length == 0)
{
@ -167,6 +172,7 @@ namespace Avalonia.Media.TextFormatting
}
var font = typeface.GlyphTypeface;
var defaultFont = defaultTypeface?.GlyphTypeface;
var enumerator = new GraphemeEnumerator(text);
@ -176,6 +182,11 @@ namespace Avalonia.Media.TextFormatting
var currentScript = currentGrapheme.FirstCodepoint.Script;
if (currentScript != Script.Common && defaultFont != null && defaultFont.TryGetGlyph(currentGrapheme.FirstCodepoint, out _))
{
break;
}
//Stop at the first missing glyph
if (!currentGrapheme.FirstCodepoint.IsBreakChar && !font.TryGetGlyph(currentGrapheme.FirstCodepoint, out _))
{

2
src/Avalonia.Visuals/Media/TextFormatting/TextCollapsingProperties.cs

@ -21,6 +21,6 @@ namespace Avalonia.Media.TextFormatting
/// Collapses given text line.
/// </summary>
/// <param name="textLine">Text line to collapse.</param>
public abstract IReadOnlyList<TextRun>? Collapse(TextLine textLine);
public abstract List<DrawableTextRun>? Collapse(TextLine textLine);
}
}

112
src/Avalonia.Visuals/Media/TextFormatting/TextEllipsisHelper.cs

@ -3,13 +3,11 @@ using Avalonia.Media.TextFormatting.Unicode;
namespace Avalonia.Media.TextFormatting
{
internal class TextEllipsisHelper
internal static class TextEllipsisHelper
{
public static List<ShapedTextCharacters>? Collapse(TextLine textLine, TextCollapsingProperties properties, bool isWordEllipsis)
public static List<DrawableTextRun>? Collapse(TextLine textLine, TextCollapsingProperties properties, bool isWordEllipsis)
{
var shapedTextRuns = textLine.TextRuns as List<ShapedTextCharacters>;
if (shapedTextRuns is null)
if (textLine.TextRuns is not List<DrawableTextRun> textRuns || textRuns.Count == 0)
{
return null;
}
@ -22,69 +20,103 @@ namespace Avalonia.Media.TextFormatting
if (properties.Width < shapedSymbol.GlyphRun.Size.Width)
{
return new List<ShapedTextCharacters>(0);
//Not enough space to fit in the symbol
return new List<DrawableTextRun>(0);
}
var availableWidth = properties.Width - shapedSymbol.Size.Width;
while (runIndex < shapedTextRuns.Count)
while (runIndex < textRuns.Count)
{
var currentRun = shapedTextRuns[runIndex];
currentWidth += currentRun.Size.Width;
var currentRun = textRuns[runIndex];
if (currentWidth > availableWidth)
switch (currentRun)
{
if (currentRun.TryMeasureCharacters(availableWidth, out var measuredLength))
case ShapedTextCharacters shapedRun:
{
if (isWordEllipsis && measuredLength < textRange.End)
currentWidth += shapedRun.Size.Width;
if (currentWidth > availableWidth)
{
var currentBreakPosition = 0;
if (shapedRun.TryMeasureCharacters(availableWidth, out var measuredLength))
{
if (isWordEllipsis && measuredLength < textRange.End)
{
var currentBreakPosition = 0;
var lineBreaker = new LineBreakEnumerator(currentRun.Text);
var lineBreaker = new LineBreakEnumerator(currentRun.Text);
while (currentBreakPosition < measuredLength && lineBreaker.MoveNext())
{
var nextBreakPosition = lineBreaker.Current.PositionMeasure;
while (currentBreakPosition < measuredLength && lineBreaker.MoveNext())
{
var nextBreakPosition = lineBreaker.Current.PositionMeasure;
if (nextBreakPosition == 0)
{
break;
}
if (nextBreakPosition == 0)
{
break;
}
if (nextBreakPosition >= measuredLength)
{
break;
if (nextBreakPosition >= measuredLength)
{
break;
}
currentBreakPosition = nextBreakPosition;
}
measuredLength = currentBreakPosition;
}
}
collapsedLength += measuredLength;
var collapsedRuns = new List<DrawableTextRun>(textRuns.Count);
currentBreakPosition = nextBreakPosition;
if (collapsedLength > 0)
{
var splitResult = TextFormatterImpl.SplitDrawableRuns(textRuns, collapsedLength);
collapsedRuns.AddRange(splitResult.First);
TextLineImpl.SortRuns(collapsedRuns);
}
measuredLength = currentBreakPosition;
collapsedRuns.Add(shapedSymbol);
return collapsedRuns;
}
}
collapsedLength += measuredLength;
availableWidth -= currentRun.Size.Width;
var shapedTextCharacters = new List<ShapedTextCharacters>(shapedTextRuns.Count);
break;
}
if (collapsedLength > 0)
case { } drawableRun:
{
var splitResult = TextFormatterImpl.SplitShapedRuns(shapedTextRuns, collapsedLength);
//The whole run needs to fit into available space
if (currentWidth + drawableRun.Size.Width > availableWidth)
{
var collapsedRuns = new List<DrawableTextRun>(textRuns.Count);
shapedTextCharacters.AddRange(splitResult.First);
if (collapsedLength > 0)
{
var splitResult = TextFormatterImpl.SplitDrawableRuns(textRuns, collapsedLength);
TextLineImpl.SortRuns(shapedTextCharacters);
}
collapsedRuns.AddRange(splitResult.First);
shapedTextCharacters.Add(shapedSymbol);
TextLineImpl.SortRuns(collapsedRuns);
}
return shapedTextCharacters;
}
collapsedRuns.Add(shapedSymbol);
availableWidth -= currentRun.Size.Width;
return collapsedRuns;
}
break;
}
}
collapsedLength += currentRun.GlyphRun.Characters.Length;
collapsedLength += currentRun.TextSourceLength;
runIndex++;
}

243
src/Avalonia.Visuals/Media/TextFormatting/TextFormatterImpl.cs

@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Runtime.InteropServices;
using Avalonia.Media.TextFormatting.Unicode;
using Avalonia.Utilities;
@ -17,20 +15,20 @@ namespace Avalonia.Media.TextFormatting
var textWrapping = paragraphProperties.TextWrapping;
FlowDirection flowDirection;
TextLineBreak? nextLineBreak = null;
List<ShapedTextCharacters> shapedRuns;
List<DrawableTextRun> drawableTextRuns;
var textRuns = FetchTextRuns(textSource, firstTextSourceIndex,
out var textEndOfLine, out var textRange);
if (previousLineBreak?.RemainingCharacters != null)
if (previousLineBreak?.RemainingRuns != null)
{
flowDirection = previousLineBreak.FlowDirection;
shapedRuns = previousLineBreak.RemainingCharacters.ToList();
drawableTextRuns = previousLineBreak.RemainingRuns.ToList();
nextLineBreak = previousLineBreak;
}
else
{
shapedRuns = ShapeTextRuns(textRuns, paragraphProperties.FlowDirection,out flowDirection);
drawableTextRuns = ShapeTextRuns(textRuns, paragraphProperties, out flowDirection);
if(nextLineBreak == null && textEndOfLine != null)
{
@ -44,9 +42,9 @@ namespace Avalonia.Media.TextFormatting
{
case TextWrapping.NoWrap:
{
TextLineImpl.SortRuns(shapedRuns);
TextLineImpl.SortRuns(drawableTextRuns);
textLine = new TextLineImpl(shapedRuns, textRange, paragraphWidth, paragraphProperties,
textLine = new TextLineImpl(drawableTextRuns, textRange, paragraphWidth, paragraphProperties,
flowDirection, nextLineBreak);
textLine.FinalizeLine();
@ -56,7 +54,7 @@ namespace Avalonia.Media.TextFormatting
case TextWrapping.WrapWithOverflow:
case TextWrapping.Wrap:
{
textLine = PerformTextWrapping(shapedRuns, textRange, paragraphWidth, paragraphProperties,
textLine = PerformTextWrapping(drawableTextRuns, textRange, paragraphWidth, paragraphProperties,
flowDirection, nextLineBreak);
break;
}
@ -73,7 +71,7 @@ namespace Avalonia.Media.TextFormatting
/// <param name="textRuns">The text run's.</param>
/// <param name="length">The length to split at.</param>
/// <returns>The split text runs.</returns>
internal static SplitResult<List<ShapedTextCharacters>> SplitShapedRuns(List<ShapedTextCharacters> textRuns, int length)
internal static SplitResult<List<DrawableTextRun>> SplitDrawableRuns(List<DrawableTextRun> textRuns, int length)
{
var currentLength = 0;
@ -83,13 +81,14 @@ namespace Avalonia.Media.TextFormatting
if (currentLength + currentRun.Text.Length < length)
{
currentLength += currentRun.Text.Length;
currentLength += currentRun.TextSourceLength;
continue;
}
var firstCount = currentRun.Text.Length >= 1 ? i + 1 : i;
var first = new List<ShapedTextCharacters>(firstCount);
var first = new List<DrawableTextRun>(firstCount);
if (firstCount > 1)
{
@ -103,7 +102,7 @@ namespace Avalonia.Media.TextFormatting
if (currentLength + currentRun.Text.Length == length)
{
var second = secondCount > 0 ? new List<ShapedTextCharacters>(secondCount) : null;
var second = secondCount > 0 ? new List<DrawableTextRun>(secondCount) : null;
if (second != null)
{
@ -117,15 +116,20 @@ namespace Avalonia.Media.TextFormatting
first.Add(currentRun);
return new SplitResult<List<ShapedTextCharacters>>(first, second);
return new SplitResult<List<DrawableTextRun>>(first, second);
}
else
{
secondCount++;
var second = new List<ShapedTextCharacters>(secondCount);
var second = new List<DrawableTextRun>(secondCount);
var split = currentRun.Split(length - currentLength);
if (currentRun is not ShapedTextCharacters shapedTextCharacters)
{
throw new NotSupportedException("Only shaped runs can be split in between.");
}
var split = shapedTextCharacters.Split(length - currentLength);
first.Add(split.First);
@ -136,32 +140,43 @@ namespace Avalonia.Media.TextFormatting
second.Add(textRuns[i + j]);
}
return new SplitResult<List<ShapedTextCharacters>>(first, second);
return new SplitResult<List<DrawableTextRun>>(first, second);
}
}
return new SplitResult<List<ShapedTextCharacters>>(textRuns, null);
return new SplitResult<List<DrawableTextRun>>(textRuns, null);
}
/// <summary>
/// Shape specified text runs with specified paragraph embedding.
/// </summary>
/// <param name="textRuns">The text runs to shape.</param>
/// <param name="flowDirection">The paragraph embedding level.</param>
/// <param name="paragraphProperties">The default paragraph properties.</param>
/// <param name="resolvedFlowDirection">The resolved flow direction.</param>
/// <returns>
/// A list of shaped text characters.
/// </returns>
private static List<ShapedTextCharacters> ShapeTextRuns(List<TextCharacters> textRuns,
FlowDirection flowDirection, out FlowDirection resolvedFlowDirection)
private static List<DrawableTextRun> ShapeTextRuns(List<TextRun> textRuns, TextParagraphProperties paragraphProperties,
out FlowDirection resolvedFlowDirection)
{
var shapedTextCharacters = new List<ShapedTextCharacters>();
var flowDirection = paragraphProperties.FlowDirection;
var drawableTextRuns = new List<DrawableTextRun>();
var biDiData = new BidiData((sbyte)flowDirection);
foreach (var textRun in textRuns)
{
biDiData.Append(textRun.Text);
if (textRun.Text.IsEmpty)
{
var text = new char[textRun.TextSourceLength];
biDiData.Append(text);
}
else
{
biDiData.Append(textRun.Text);
}
}
var biDi = BidiAlgorithm.Instance.Value!;
@ -173,68 +188,90 @@ namespace Avalonia.Media.TextFormatting
resolvedFlowDirection =
(resolvedEmbeddingLevel & 1) == 0 ? FlowDirection.LeftToRight : FlowDirection.RightToLeft;
var shapeableRuns = new List<ShapeableTextCharacters>(textRuns.Count);
var processedRuns = new List<TextRun>(textRuns.Count);
foreach (var coalescedRuns in CoalesceLevels(textRuns, biDi.ResolvedLevels))
{
shapeableRuns.AddRange(coalescedRuns);
processedRuns.AddRange(coalescedRuns);
}
for (var index = 0; index < shapeableRuns.Count; index++)
for (var index = 0; index < processedRuns.Count; index++)
{
var currentRun = shapeableRuns[index];
var groupedRuns = new List<ShapeableTextCharacters>(2) { currentRun };
var text = currentRun.Text;
var start = currentRun.Text.Start;
var length = currentRun.Text.Length;
var bufferOffset = currentRun.Text.BufferOffset;
while (index + 1 < shapeableRuns.Count)
var currentRun = processedRuns[index];
switch (currentRun)
{
var nextRun = shapeableRuns[index + 1];
case DrawableTextRun drawableRun:
{
drawableTextRuns.Add(drawableRun);
if (currentRun.CanShapeTogether(nextRun))
break;
}
case ShapeableTextCharacters shapeableRun:
{
groupedRuns.Add(nextRun);
var groupedRuns = new List<ShapeableTextCharacters>(2) { shapeableRun };
var text = currentRun.Text;
var start = currentRun.Text.Start;
var length = currentRun.Text.Length;
var bufferOffset = currentRun.Text.BufferOffset;
length += nextRun.Text.Length;
if (start > nextRun.Text.Start)
while (index + 1 < processedRuns.Count)
{
start = nextRun.Text.Start;
}
if (processedRuns[index + 1] is not ShapeableTextCharacters nextRun)
{
break;
}
if (bufferOffset > nextRun.Text.BufferOffset)
{
bufferOffset = nextRun.Text.BufferOffset;
if (shapeableRun.CanShapeTogether(nextRun))
{
groupedRuns.Add(nextRun);
length += nextRun.Text.Length;
if (start > nextRun.Text.Start)
{
start = nextRun.Text.Start;
}
if (bufferOffset > nextRun.Text.BufferOffset)
{
bufferOffset = nextRun.Text.BufferOffset;
}
text = new ReadOnlySlice<char>(text.Buffer, start, length, bufferOffset);
index++;
shapeableRun = nextRun;
continue;
}
break;
}
text = new ReadOnlySlice<char>(text.Buffer, start, length, bufferOffset);
index++;
var shaperOptions = new TextShaperOptions(currentRun.Properties!.Typeface.GlyphTypeface,
currentRun.Properties.FontRenderingEmSize,
shapeableRun.BidiLevel, currentRun.Properties.CultureInfo, paragraphProperties.DefaultIncrementalTab);
currentRun = nextRun;
drawableTextRuns.AddRange(ShapeTogether(groupedRuns, text, shaperOptions));
continue;
break;
}
break;
}
shapedTextCharacters.AddRange(ShapeTogether(groupedRuns, text));
}
return shapedTextCharacters;
return drawableTextRuns;
}
private static IReadOnlyList<ShapedTextCharacters> ShapeTogether(
IReadOnlyList<ShapeableTextCharacters> textRuns, ReadOnlySlice<char> text)
IReadOnlyList<ShapeableTextCharacters> textRuns, ReadOnlySlice<char> text, TextShaperOptions options)
{
var shapedRuns = new List<ShapedTextCharacters>(textRuns.Count);
var firstRun = textRuns[0];
var shapedBuffer = TextShaper.Current.ShapeText(text, firstRun.Properties.Typeface.GlyphTypeface,
firstRun.Properties.FontRenderingEmSize, firstRun.Properties.CultureInfo, firstRun.BidiLevel);
var shapedBuffer = TextShaper.Current.ShapeText(text, options);
for (var i = 0; i < textRuns.Count; i++)
{
@ -256,8 +293,8 @@ namespace Avalonia.Media.TextFormatting
/// <param name="textCharacters">The text characters to form <see cref="ShapeableTextCharacters"/> from.</param>
/// <param name="levels">The bidi levels.</param>
/// <returns></returns>
private static IEnumerable<IList<ShapeableTextCharacters>> CoalesceLevels(
IReadOnlyList<TextCharacters> textCharacters,
private static IEnumerable<IReadOnlyList<TextRun>> CoalesceLevels(
IReadOnlyList<TextRun> textCharacters,
ReadOnlySlice<sbyte> levels)
{
if (levels.Length == 0)
@ -275,7 +312,19 @@ namespace Avalonia.Media.TextFormatting
for (var i = 0; i < textCharacters.Count; i++)
{
var j = 0;
currentRun = textCharacters[i];
currentRun = textCharacters[i] as TextCharacters;
if (currentRun == null)
{
var drawableRun = textCharacters[i];
yield return new[] { drawableRun };
levelIndex += drawableRun.TextSourceLength;
continue;
}
runText = currentRun.Text;
for (; j < runText.Length;)
@ -334,14 +383,14 @@ namespace Avalonia.Media.TextFormatting
/// <returns>
/// The formatted text runs.
/// </returns>
private static List<TextCharacters> FetchTextRuns(ITextSource textSource, int firstTextSourceIndex,
private static List<TextRun> FetchTextRuns(ITextSource textSource, int firstTextSourceIndex,
out TextEndOfLine? endOfLine, out TextRange textRange)
{
var length = 0;
endOfLine = null;
var textRuns = new List<TextCharacters>();
var textRuns = new List<TextRun>();
var textRunEnumerator = new TextRunEnumerator(textSource, firstTextSourceIndex);
@ -354,6 +403,17 @@ namespace Avalonia.Media.TextFormatting
break;
}
if (textRun is TextEndOfLine textEndOfLine)
{
endOfLine = textEndOfLine;
textRuns.Add(textRun);
length += textRun.TextSourceLength;
break;
}
switch (textRun)
{
case TextCharacters textCharacters:
@ -376,12 +436,14 @@ namespace Avalonia.Media.TextFormatting
break;
}
case TextEndOfLine textEndOfLine:
endOfLine = textEndOfLine;
case DrawableTextRun drawableTextRun:
{
textRuns.Add(drawableTextRun);
break;
}
}
length += textRun.Text.Length;
length += textRun.TextSourceLength;
}
textRange = new TextRange(firstTextSourceIndex, length);
@ -415,7 +477,7 @@ namespace Avalonia.Media.TextFormatting
return false;
}
private static int MeasureLength(IReadOnlyList<ShapedTextCharacters> textRuns, TextRange textRange,
private static int MeasureLength(IReadOnlyList<DrawableTextRun> textRuns, TextRange textRange,
double paragraphWidth)
{
var currentWidth = 0.0;
@ -423,19 +485,42 @@ namespace Avalonia.Media.TextFormatting
foreach (var currentRun in textRuns)
{
for (var i = 0; i < currentRun.ShapedBuffer.Length; i++)
switch (currentRun)
{
var glyphInfo = currentRun.ShapedBuffer[i];
if (currentWidth + glyphInfo.GlyphAdvance > paragraphWidth)
case ShapedTextCharacters shapedTextCharacters:
{
var measuredLength = lastCluster - textRange.Start;
for (var i = 0; i < shapedTextCharacters.ShapedBuffer.Length; i++)
{
var glyphInfo = shapedTextCharacters.ShapedBuffer[i];
return measuredLength == 0 ? 1 : measuredLength;
if (currentWidth + glyphInfo.GlyphAdvance > paragraphWidth)
{
var measuredLength = lastCluster - textRange.Start;
return measuredLength == 0 ? 1 : measuredLength;
}
lastCluster = glyphInfo.GlyphCluster;
currentWidth += glyphInfo.GlyphAdvance;
}
break;
}
lastCluster = glyphInfo.GlyphCluster;
currentWidth += glyphInfo.GlyphAdvance;
case { } drawableTextRun:
{
if (currentWidth + drawableTextRun.Size.Width > paragraphWidth)
{
var measuredLength = lastCluster - textRange.Start;
return measuredLength == 0 ? 1 : measuredLength;
}
lastCluster += currentRun.TextSourceLength;
currentWidth += currentRun.Size.Width;
break;
}
}
}
@ -452,7 +537,7 @@ namespace Avalonia.Media.TextFormatting
/// <param name="flowDirection"></param>
/// <param name="currentLineBreak">The current line break if the line was explicitly broken.</param>
/// <returns>The wrapped text line.</returns>
private static TextLineImpl PerformTextWrapping(List<ShapedTextCharacters> textRuns, TextRange textRange,
private static TextLineImpl PerformTextWrapping(List<DrawableTextRun> textRuns, TextRange textRange,
double paragraphWidth, TextParagraphProperties paragraphProperties, FlowDirection flowDirection,
TextLineBreak? currentLineBreak)
{
@ -568,7 +653,7 @@ namespace Avalonia.Media.TextFormatting
break;
}
var splitResult = SplitShapedRuns(textRuns, measuredLength);
var splitResult = SplitDrawableRuns(textRuns, measuredLength);
textRange = new TextRange(textRange.Start, measuredLength);
@ -644,7 +729,9 @@ namespace Avalonia.Media.TextFormatting
var cultureInfo = textRun.Properties.CultureInfo;
var shapedBuffer = textShaper.ShapeText(textRun.Text, glyphTypeface, fontRenderingEmSize, cultureInfo, (sbyte)flowDirection);
var shaperOptions = new TextShaperOptions(glyphTypeface, fontRenderingEmSize, (sbyte)flowDirection, cultureInfo);
var shapedBuffer = textShaper.ShapeText(textRun.Text, shaperOptions);
return new ShapedTextCharacters(shapedBuffer, textRun.Properties);
}

175
src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs

@ -193,187 +193,34 @@ namespace Avalonia.Media.TextFormatting
var result = new List<Rect>(TextLines.Count);
var currentY = 0d;
var currentPosition = 0;
var currentRect = Rect.Empty;
foreach (var textLine in TextLines)
{
//Current line isn't covered.
if (currentPosition + textLine.TextRange.Length <= start)
if (textLine.TextRange.Start + textLine.TextRange.Length <= start)
{
currentY += textLine.Height;
currentPosition += textLine.TextRange.Length;
continue;
}
//The whole line is covered.
if (currentPosition >= start && start + length > currentPosition + textLine.TextRange.Length)
{
currentRect = new Rect(textLine.Start, currentY, textLine.WidthIncludingTrailingWhitespace,
textLine.Height);
result.Add(currentRect);
currentY += textLine.Height;
currentPosition += textLine.TextRange.Length;
continue;
}
var startX = textLine.Start;
//A portion of the line is covered.
for (var index = 0; index < textLine.TextRuns.Count; index++)
{
var currentRun = (ShapedTextCharacters)textLine.TextRuns[index];
ShapedTextCharacters? nextRun = null;
if (index + 1 < textLine.TextRuns.Count)
{
nextRun = (ShapedTextCharacters)textLine.TextRuns[index + 1];
}
if (nextRun != null)
{
if (nextRun.Text.Start < currentRun.Text.Start && start + length < currentRun.Text.End)
{
goto skip;
}
if (currentRun.Text.Start >= start + length)
{
goto skip;
}
if (currentRun.Text.Start > nextRun.Text.Start && currentRun.Text.Start < start)
{
goto skip;
}
if (currentRun.Text.End < start)
{
goto skip;
}
goto noop;
skip:
{
startX += currentRun.Size.Width;
currentPosition = currentRun.Text.Start;
}
continue;
noop:{ }
}
var endOffset = currentRun.GlyphRun.GetDistanceFromCharacterHit(
currentRun.ShapedBuffer.IsLeftToRight ?
new CharacterHit(start + length) :
new CharacterHit(start));
var textBounds = textLine.GetTextBounds(start, length);
var endX = startX + endOffset;
var startOffset = currentRun.GlyphRun.GetDistanceFromCharacterHit(
currentRun.ShapedBuffer.IsLeftToRight ?
new CharacterHit(start) :
new CharacterHit(start + length));
startX += startOffset;
var characterHit = currentRun.GlyphRun.IsLeftToRight ?
currentRun.GlyphRun.GetCharacterHitFromDistance(endOffset, out _) :
currentRun.GlyphRun.GetCharacterHitFromDistance(startOffset, out _);
currentPosition = characterHit.FirstCharacterIndex + characterHit.TrailingLength;
if(nextRun != null)
{
if (currentRun.ShapedBuffer.IsLeftToRight == nextRun.ShapedBuffer.IsLeftToRight)
{
endOffset = nextRun.GlyphRun.GetDistanceFromCharacterHit(
nextRun.ShapedBuffer.IsLeftToRight ?
new CharacterHit(start + length) :
new CharacterHit(start));
index++;
endX += endOffset;
currentRun = nextRun;
if (currentRun.ShapedBuffer.IsLeftToRight)
{
characterHit = nextRun.GlyphRun.GetCharacterHitFromDistance(endOffset, out _);
currentPosition = characterHit.FirstCharacterIndex + characterHit.TrailingLength;
}
}
}
if (endX < startX)
{
(endX, startX) = (startX, endX);
}
var width = endX - startX;
foreach (var bounds in textBounds)
{
Rect? last = result.Count > 0 ? result[result.Count - 1] : null;
if (result.Count > 0 && MathUtilities.AreClose(currentRect.Top, currentY) &&
MathUtilities.AreClose(currentRect.Right, startX))
{
result[result.Count - 1] = currentRect.WithWidth(currentRect.Width + width);
}
else
{
currentRect = new Rect(startX, currentY, width, textLine.Height);
result.Add(currentRect);
}
if (currentRun.ShapedBuffer.IsLeftToRight)
if (last.HasValue && MathUtilities.AreClose(last.Value.Right, bounds.Rectangle.Left) && MathUtilities.AreClose(last.Value.Top, currentY))
{
if (nextRun != null)
{
if (nextRun.Text.Start > currentRun.Text.Start && nextRun.Text.Start >= start + length)
{
break;
}
currentPosition = nextRun.Text.End;
}
else
{
if (currentPosition >= start + length)
{
break;
}
}
result[result.Count - 1] = last.Value.WithWidth(last.Value.Width + bounds.Rectangle.Width);
}
else
{
if (currentPosition <= start)
{
break;
}
}
if (!currentRun.ShapedBuffer.IsLeftToRight && currentPosition != currentRun.Text.Start)
{
endX += currentRun.GlyphRun.Size.Width - endOffset;
}
startX = endX;
result.Add(bounds.Rectangle.WithY(currentY));
}
}
if (currentPosition == start || currentPosition == start + length)
{
break;
}
if (textLine.TextRange.Start + textLine.TextRange.Length >= start + length)
if(textLine.TextRange.Start + textLine.TextRange.Length >= start + length)
{
break;
}
@ -541,7 +388,7 @@ namespace Avalonia.Media.TextFormatting
var shapedBuffer = new ShapedBuffer(text, glyphInfos, glyphTypeface, properties.FontRenderingEmSize,
(sbyte)flowDirection);
var textRuns = new List<ShapedTextCharacters> { new ShapedTextCharacters(shapedBuffer, properties) };
var textRuns = new List<DrawableTextRun> { new ShapedTextCharacters(shapedBuffer, properties) };
var textRange = new TextRange(startingIndex, 1);

127
src/Avalonia.Visuals/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs

@ -17,7 +17,7 @@ namespace Avalonia.Media.TextFormatting
/// <param name="ellipsis">Text used as collapsing symbol.</param>
/// <param name="prefixLength">Length of leading prefix.</param>
/// <param name="width">width in which collapsing is constrained to</param>
/// <param name="textRunProperties">text run properties of ellispis symbol</param>
/// <param name="textRunProperties">text run properties of ellipsis symbol</param>
public TextLeadingPrefixCharacterEllipsis(
ReadOnlySlice<char> ellipsis,
int prefixLength,
@ -35,16 +35,14 @@ namespace Avalonia.Media.TextFormatting
}
/// <inheritdoc/>
public sealed override double Width { get; }
public override double Width { get; }
/// <inheritdoc/>
public sealed override TextRun Symbol { get; }
public override TextRun Symbol { get; }
public override IReadOnlyList<TextRun>? Collapse(TextLine textLine)
public override List<DrawableTextRun>? Collapse(TextLine textLine)
{
var shapedTextRuns = textLine.TextRuns as List<ShapedTextCharacters>;
if (shapedTextRuns is null)
if (textLine.TextRuns is not List<DrawableTextRun> textRuns || textRuns.Count == 0)
{
return null;
}
@ -55,84 +53,105 @@ namespace Avalonia.Media.TextFormatting
if (Width < shapedSymbol.GlyphRun.Size.Width)
{
return new List<ShapedTextCharacters>(0);
return new List<DrawableTextRun>(0);
}
// Overview of ellipsis structure
// Prefix length run | Ellipsis symbol | Post split run growing from the end |
var availableWidth = Width - shapedSymbol.Size.Width;
while (runIndex < shapedTextRuns.Count)
while (runIndex < textRuns.Count)
{
var currentRun = shapedTextRuns[runIndex];
currentWidth += currentRun.Size.Width;
var currentRun = textRuns[runIndex];
if (currentWidth > availableWidth)
switch (currentRun)
{
currentRun.TryMeasureCharacters(availableWidth, out var measuredLength);
var shapedTextCharacters = new List<ShapedTextCharacters>(shapedTextRuns.Count);
if (measuredLength > 0)
case ShapedTextCharacters shapedRun:
{
List<ShapedTextCharacters>? preSplitRuns = null;
List<ShapedTextCharacters>? postSplitRuns = null;
currentWidth += currentRun.Size.Width;
if (_prefixLength > 0)
if (currentWidth > availableWidth)
{
var splitResult = TextFormatterImpl.SplitShapedRuns(shapedTextRuns, Math.Min(_prefixLength, measuredLength));
shapedRun.TryMeasureCharacters(availableWidth, out var measuredLength);
shapedTextCharacters.AddRange(splitResult.First);
var collapsedRuns = new List<DrawableTextRun>(textRuns.Count);
TextLineImpl.SortRuns(shapedTextCharacters);
if (measuredLength > 0)
{
List<DrawableTextRun>? preSplitRuns = null;
List<DrawableTextRun>? postSplitRuns;
preSplitRuns = splitResult.First;
postSplitRuns = splitResult.Second;
}
else
{
postSplitRuns = shapedTextRuns;
}
if (_prefixLength > 0)
{
var splitResult = TextFormatterImpl.SplitDrawableRuns(textRuns,
Math.Min(_prefixLength, measuredLength));
shapedTextCharacters.Add(shapedSymbol);
collapsedRuns.AddRange(splitResult.First);
if (measuredLength > _prefixLength && postSplitRuns is not null)
{
var availableSuffixWidth = availableWidth;
TextLineImpl.SortRuns(collapsedRuns);
if (preSplitRuns is not null)
{
foreach (var run in preSplitRuns)
preSplitRuns = splitResult.First;
postSplitRuns = splitResult.Second;
}
else
{
availableSuffixWidth -= run.Size.Width;
postSplitRuns = textRuns;
}
}
for (int i = postSplitRuns.Count - 1; i >= 0; i--)
{
var run = postSplitRuns[i];
collapsedRuns.Add(shapedSymbol);
if (run.TryMeasureCharactersBackwards(availableSuffixWidth, out int suffixCount, out double suffixWidth))
if (measuredLength <= _prefixLength || postSplitRuns is null)
{
availableSuffixWidth -= suffixWidth;
return collapsedRuns;
}
if (suffixCount > 0)
var availableSuffixWidth = availableWidth;
if (preSplitRuns is not null)
{
foreach (var run in preSplitRuns)
{
var splitSuffix = run.Split(run.TextSourceLength - suffixCount);
availableSuffixWidth -= run.Size.Width;
}
}
shapedTextCharacters.Add(splitSuffix.Second!);
for (var i = postSplitRuns.Count - 1; i >= 0; i--)
{
var run = postSplitRuns[i];
switch (run)
{
case ShapedTextCharacters endShapedRun:
{
if (endShapedRun.TryMeasureCharactersBackwards(availableSuffixWidth,
out var suffixCount, out var suffixWidth))
{
availableSuffixWidth -= suffixWidth;
if (suffixCount > 0)
{
var splitSuffix =
endShapedRun.Split(run.TextSourceLength - suffixCount);
collapsedRuns.Add(splitSuffix.Second!);
}
}
break;
}
}
}
}
else
{
collapsedRuns.Add(shapedSymbol);
}
return collapsedRuns;
}
}
else
{
shapedTextCharacters.Add(shapedSymbol);
}
return shapedTextCharacters;
break;
}
}
availableWidth -= currentRun.Size.Width;

8
src/Avalonia.Visuals/Media/TextFormatting/TextLine.cs

@ -189,6 +189,14 @@ namespace Avalonia.Media.TextFormatting
/// <returns>The <see cref="CharacterHit"/> after backspacing.</returns>
public abstract CharacterHit GetBackspaceCaretCharacterHit(CharacterHit characterHit);
/// <summary>
/// Get an array of bounding rectangles of a range of characters within a text line.
/// </summary>
/// <param name="firstTextSourceCharacterIndex">index of first character of specified range</param>
/// <param name="textLength">number of characters of the specified range</param>
/// <returns>an array of bounding rectangles.</returns>
public abstract IReadOnlyList<TextBounds> GetTextBounds(int firstTextSourceCharacterIndex, int textLength);
/// <summary>
/// Gets the text line offset x.
/// </summary>

8
src/Avalonia.Visuals/Media/TextFormatting/TextLineBreak.cs

@ -5,11 +5,11 @@ namespace Avalonia.Media.TextFormatting
public class TextLineBreak
{
public TextLineBreak(TextEndOfLine? textEndOfLine = null, FlowDirection flowDirection = FlowDirection.LeftToRight,
IReadOnlyList<ShapedTextCharacters>? remainingCharacters = null)
IReadOnlyList<DrawableTextRun>? remainingRuns = null)
{
TextEndOfLine = textEndOfLine;
FlowDirection = flowDirection;
RemainingCharacters = remainingCharacters;
RemainingRuns = remainingRuns;
}
/// <summary>
@ -23,8 +23,8 @@ namespace Avalonia.Media.TextFormatting
public FlowDirection FlowDirection { get; }
/// <summary>
/// Get the remaining shaped characters that were split up by the <see cref="TextFormatter"/> during the formatting process.
/// Get the remaining runs that were split up by the <see cref="TextFormatter"/> during the formatting process.
/// </summary>
public IReadOnlyList<ShapedTextCharacters>? RemainingCharacters { get; }
public IReadOnlyList<DrawableTextRun>? RemainingRuns { get; }
}
}

896
src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs

File diff suppressed because it is too large

8
src/Avalonia.Visuals/Media/TextFormatting/TextParagraphProperties.cs

@ -63,5 +63,13 @@
{
get { return 0; }
}
/// <summary>
/// Default Incremental Tab
/// </summary>
public virtual double DefaultIncrementalTab
{
get { return 4 * DefaultTextRunProperties.FontRenderingEmSize; }
}
}
}

2
src/Avalonia.Visuals/Media/TextFormatting/TextRunProperties.cs

@ -45,7 +45,7 @@ namespace Avalonia.Media.TextFormatting
/// <summary>
/// Run vertical box alignment
/// </summary>
public abstract BaselineAlignment BaselineAlignment { get; }
public virtual BaselineAlignment BaselineAlignment => BaselineAlignment.Baseline;
public bool Equals(TextRunProperties? other)
{

5
src/Avalonia.Visuals/Media/TextFormatting/TextShaper.cs

@ -45,10 +45,9 @@ namespace Avalonia.Media.TextFormatting
}
/// <inheritdoc cref="ITextShaperImpl.ShapeText"/>
public ShapedBuffer ShapeText(ReadOnlySlice<char> text, GlyphTypeface typeface, double fontRenderingEmSize,
CultureInfo? culture, sbyte bidiLevel)
public ShapedBuffer ShapeText(ReadOnlySlice<char> text, TextShaperOptions options)
{
return _platformImpl.ShapeText(text, typeface, fontRenderingEmSize, culture, bidiLevel);
return _platformImpl.ShapeText(text, options);
}
}
}

49
src/Avalonia.Visuals/Media/TextFormatting/TextShaperOptions.cs

@ -0,0 +1,49 @@
using System.Globalization;
namespace Avalonia.Media.TextFormatting
{
/// <summary>
/// Options to customize text shaping.
/// </summary>
public readonly struct TextShaperOptions
{
public TextShaperOptions(
GlyphTypeface typeface,
double fontRenderingEmSize = 12,
sbyte bidiLevel = 0,
CultureInfo? culture = null,
double incrementalTabWidth = 0)
{
Typeface = typeface;
FontRenderingEmSize = fontRenderingEmSize;
BidLevel = bidiLevel;
Culture = culture;
IncrementalTabWidth = incrementalTabWidth;
}
/// <summary>
/// Get the typeface.
/// </summary>
public GlyphTypeface Typeface { get; }
/// <summary>
/// Get the font rendering em size.
/// </summary>
public double FontRenderingEmSize { get; }
/// <summary>
/// Get the bidi level of the text.
/// </summary>
public sbyte BidLevel { get; }
/// <summary>
/// Get the culture.
/// </summary>
public CultureInfo? Culture { get; }
/// <summary>
/// Get the incremental tab width.
/// </summary>
public double IncrementalTabWidth { get; }
}
}

8
src/Avalonia.Visuals/Media/TextFormatting/TextTrailingCharacterEllipsis.cs

@ -14,7 +14,7 @@ namespace Avalonia.Media.TextFormatting
/// </summary>
/// <param name="ellipsis">Text used as collapsing symbol.</param>
/// <param name="width">Width in which collapsing is constrained to.</param>
/// <param name="textRunProperties">Text run properties of ellispis symbol.</param>
/// <param name="textRunProperties">Text run properties of ellipsis symbol.</param>
public TextTrailingCharacterEllipsis(ReadOnlySlice<char> ellipsis, double width, TextRunProperties textRunProperties)
{
Width = width;
@ -22,12 +22,12 @@ namespace Avalonia.Media.TextFormatting
}
/// <inheritdoc/>
public sealed override double Width { get; }
public override double Width { get; }
/// <inheritdoc/>
public sealed override TextRun Symbol { get; }
public override TextRun Symbol { get; }
public override IReadOnlyList<TextRun>? Collapse(TextLine textLine)
public override List<DrawableTextRun>? Collapse(TextLine textLine)
{
return TextEllipsisHelper.Collapse(textLine, this, false);
}

8
src/Avalonia.Visuals/Media/TextFormatting/TextTrailingWordEllipsis.cs

@ -14,7 +14,7 @@ namespace Avalonia.Media.TextFormatting
/// </summary>
/// <param name="ellipsis">Text used as collapsing symbol.</param>
/// <param name="width">width in which collapsing is constrained to.</param>
/// <param name="textRunProperties">text run properties of ellispis symbol.</param>
/// <param name="textRunProperties">text run properties of ellipsis symbol.</param>
public TextTrailingWordEllipsis(
ReadOnlySlice<char> ellipsis,
double width,
@ -26,12 +26,12 @@ namespace Avalonia.Media.TextFormatting
}
/// <inheritdoc/>
public sealed override double Width { get; }
public override double Width { get; }
/// <inheritdoc/>
public sealed override TextRun Symbol { get; }
public override TextRun Symbol { get; }
public override IReadOnlyList<TextRun>? Collapse(TextLine textLine)
public override List<DrawableTextRun>? Collapse(TextLine textLine)
{
return TextEllipsisHelper.Collapse(textLine, this, true);
}

13
src/Avalonia.Visuals/Platform/ITextShaperImpl.cs

@ -1,6 +1,4 @@
using System.Globalization;
using Avalonia.Media;
using Avalonia.Media.TextFormatting;
using Avalonia.Media.TextFormatting;
using Avalonia.Utilities;
namespace Avalonia.Platform
@ -14,11 +12,8 @@ namespace Avalonia.Platform
/// Shapes the specified region within the text and returns a shaped buffer.
/// </summary>
/// <param name="text">The text.</param>
/// <param name="typeface">The typeface.</param>
/// <param name="fontRenderingEmSize">The font rendering em size.</param>
/// <param name="culture">The culture.</param>
/// <param name="bidiLevel">The bidi level.</param>
/// <param name="options">Text shaper options to customize the shaping process.</param>
/// <returns>A shaped glyph run.</returns>
ShapedBuffer ShapeText(ReadOnlySlice<char> text, GlyphTypeface typeface, double fontRenderingEmSize, CultureInfo? culture, sbyte bidiLevel);
}
ShapedBuffer ShapeText(ReadOnlySlice<char> text, TextShaperOptions options);
}
}

19
src/Skia/Avalonia.Skia/TextShaperImpl.cs

@ -13,12 +13,16 @@ namespace Avalonia.Skia
{
internal class TextShaperImpl : ITextShaperImpl
{
public ShapedBuffer ShapeText(ReadOnlySlice<char> text, GlyphTypeface typeface, double fontRenderingEmSize,
CultureInfo culture, sbyte bidiLevel)
public ShapedBuffer ShapeText(ReadOnlySlice<char> text, TextShaperOptions options)
{
var typeface = options.Typeface;
var fontRenderingEmSize = options.FontRenderingEmSize;
var bidiLevel = options.BidLevel;
var culture = options.Culture;
using (var buffer = new Buffer())
{
buffer.AddUtf16(text.Buffer.Span, text.Start, text.Length);
buffer.AddUtf16(text.Buffer.Span, text.BufferOffset, text.Length);
MergeBreakPair(buffer);
@ -61,6 +65,15 @@ namespace Avalonia.Skia
var glyphOffset = GetGlyphOffset(glyphPositions, i, textScale);
if(glyphIndex == 0 && text.Buffer.Span[glyphCluster] == '\t')
{
glyphIndex = typeface.GetGlyph(' ');
glyphAdvance = options.IncrementalTabWidth > 0 ?
options.IncrementalTabWidth :
4 * typeface.GetGlyphAdvance(glyphIndex) * textScale;
}
var targetInfo = new Media.TextFormatting.GlyphInfo(glyphIndex, glyphCluster, glyphAdvance, glyphOffset);
shapedBuffer[i] = targetInfo;

17
src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs

@ -14,9 +14,13 @@ namespace Avalonia.Direct2D1.Media
internal class TextShaperImpl : ITextShaperImpl
{
public ShapedBuffer ShapeText(ReadOnlySlice<char> text, GlyphTypeface typeface, double fontRenderingEmSize,
CultureInfo culture, sbyte bidiLevel)
public ShapedBuffer ShapeText(ReadOnlySlice<char> text, TextShaperOptions options)
{
var typeface = options.Typeface;
var fontRenderingEmSize = options.FontRenderingEmSize;
var bidiLevel = options.BidLevel;
var culture = options.Culture;
using (var buffer = new Buffer())
{
buffer.AddUtf16(text.Buffer.Span, text.Start, text.Length);
@ -62,6 +66,15 @@ internal class TextShaperImpl : ITextShaperImpl
var glyphOffset = GetGlyphOffset(glyphPositions, i, textScale);
if (glyphIndex == 0 && text[glyphCluster] == '\t')
{
glyphIndex = typeface.GetGlyph(' ');
glyphAdvance = options.IncrementalTabWidth > 0 ?
options.IncrementalTabWidth :
4 * typeface.GetGlyphAdvance(glyphIndex) * textScale;
}
var targetInfo =
new Avalonia.Media.TextFormatting.GlyphInfo(glyphIndex, glyphCluster, glyphAdvance,
glyphOffset);

29
tests/Avalonia.Controls.UnitTests/TextBoxTests.cs

@ -865,6 +865,35 @@ namespace Avalonia.Controls.UnitTests
}
}
[Fact]
public void Should_Fullfill_MaxLines_Contraint()
{
using (UnitTestApplication.Start(Services))
{
var target = new TextBox
{
Template = CreateTemplate(),
Text = "ABC",
MaxLines = 1,
AcceptsReturn= true
};
target.Measure(Size.Infinity);
AvaloniaLocator.CurrentMutable.Bind<IClipboard>().ToSingleton<ClipboardStub>();
var clipboard = AvaloniaLocator.CurrentMutable.GetService<IClipboard>();
clipboard.SetTextAsync(Environment.NewLine).GetAwaiter().GetResult();
RaiseKeyEvent(target, Key.V, KeyModifiers.Control);
clipboard.ClearAsync().GetAwaiter().GetResult();
RaiseTextEvent(target, Environment.NewLine);
Assert.Equal("ABC", target.Text);
}
}
private static TestServices FocusServices => TestServices.MockThreadingInterface.With(
focusManager: new FocusManager(),
keyboardDevice: () => new KeyboardDevice(),

9
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs

@ -16,6 +16,7 @@ using System.ComponentModel;
using System.Linq;
using System.Xml;
using Xunit;
using Avalonia.Controls.Documents;
namespace Avalonia.Markup.Xaml.UnitTests.Xaml
{
@ -47,12 +48,12 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
public void Attached_Property_Is_Set()
{
var xaml =
@"<ContentControl xmlns='https://github.com/avaloniaui' TextBlock.FontSize='21'/>";
@"<ContentControl xmlns='https://github.com/avaloniaui' TextElement.FontSize='21'/>";
var target = AvaloniaRuntimeXamlLoader.Parse<ContentControl>(xaml);
Assert.NotNull(target);
Assert.Equal(21.0, TextBlock.GetFontSize(target));
Assert.Equal(21.0, TextElement.GetFontSize(target));
}
[Fact]
@ -90,13 +91,13 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
using (UnitTestApplication.Start(TestServices.MockWindowingPlatform))
{
var xaml =
@"<Window xmlns='https://github.com/avaloniaui' TextBlock.FontSize='{Binding}'/>";
@"<Window xmlns='https://github.com/avaloniaui' TextElement.FontSize='{Binding}'/>";
var target = AvaloniaRuntimeXamlLoader.Parse<ContentControl>(xaml);
target.DataContext = 21.0;
Assert.Equal(21.0, TextBlock.GetFontSize(target));
Assert.Equal(21.0, TextElement.GetFontSize(target));
}
}

9
tests/Avalonia.Skia.UnitTests/Media/GlyphRunTests.cs

@ -17,8 +17,9 @@ namespace Avalonia.Skia.UnitTests.Media
{
using (Start())
{
var options = new TextShaperOptions(Typeface.Default.GlyphTypeface, 10, direction, CultureInfo.CurrentCulture);
var shapedBuffer =
TextShaper.Current.ShapeText(text.AsMemory(), Typeface.Default.GlyphTypeface, 10, CultureInfo.CurrentCulture, direction);
TextShaper.Current.ShapeText(text.AsMemory(), options);
var glyphRun = CreateGlyphRun(shapedBuffer);
@ -59,8 +60,9 @@ namespace Avalonia.Skia.UnitTests.Media
{
using (Start())
{
var options = new TextShaperOptions(Typeface.Default.GlyphTypeface, 10, direction, CultureInfo.CurrentCulture);
var shapedBuffer =
TextShaper.Current.ShapeText(text.AsMemory(), Typeface.Default.GlyphTypeface, 10, CultureInfo.CurrentCulture, direction);
TextShaper.Current.ShapeText(text.AsMemory(), options);
var glyphRun = CreateGlyphRun(shapedBuffer);
@ -103,8 +105,9 @@ namespace Avalonia.Skia.UnitTests.Media
{
using (Start())
{
var options = new TextShaperOptions(Typeface.Default.GlyphTypeface, 10, direction, CultureInfo.CurrentCulture);
var shapedBuffer =
TextShaper.Current.ShapeText(text.AsMemory(), Typeface.Default.GlyphTypeface, 10, CultureInfo.CurrentCulture, direction);
TextShaper.Current.ShapeText(text.AsMemory(), options);
var glyphRun = CreateGlyphRun(shapedBuffer);

7
tests/Avalonia.Skia.UnitTests/Media/TextFormatting/MultiBufferTextSource.cs

@ -20,15 +20,10 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
public TextRun GetTextRun(int textSourceIndex)
{
if (textSourceIndex > 50)
if (textSourceIndex >= 50)
{
return null;
}
if (textSourceIndex == 50)
{
return new TextEndOfParagraph();
}
var index = textSourceIndex / 10;

7
tests/Avalonia.Skia.UnitTests/Media/TextFormatting/SingleBufferTextSource.cs

@ -23,13 +23,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
}
var runText = _text.Skip(textSourceIndex);
if (runText.IsEmpty)
{
return new TextEndOfParagraph();
}
return new TextCharacters(runText, _defaultGenericPropertiesRunProperties);
return runText.IsEmpty ? null : new TextCharacters(runText, _defaultGenericPropertiesRunProperties);
}
}
}

94
tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs

@ -508,7 +508,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
var textLine =
formatter.FormatLine(textSource, 0, 33, paragraphProperties);
Assert.NotNull(textLine.TextLineBreak?.RemainingCharacters);
Assert.NotNull(textLine.TextLineBreak?.RemainingRuns);
}
}
@ -562,6 +562,98 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
}
}
[Fact]
public void Should_FormatLine_With_DrawableRuns()
{
var defaultRunProperties = new GenericTextRunProperties(Typeface.Default, foregroundBrush: Brushes.Black);
var paragraphProperties = new GenericTextParagraphProperties(defaultRunProperties);
var textSource = new CustomTextSource("Hello World ->");
using (Start())
{
var textLine =
TextFormatter.Current.FormatLine(textSource, 0, double.PositiveInfinity, paragraphProperties);
Assert.Equal(3, textLine.TextRuns.Count);
Assert.True(textLine.TextRuns[1] is RectangleRun);
}
}
[Fact]
public void Should_Format_With_EndOfLineRun()
{
using (Start())
{
var defaultRunProperties = new GenericTextRunProperties(Typeface.Default);
var paragraphProperties = new GenericTextParagraphProperties(defaultRunProperties);
var textSource = new EndOfLineTextSource();
var textLine =
TextFormatter.Current.FormatLine(textSource, 0, double.PositiveInfinity, paragraphProperties);
Assert.NotNull(textLine.TextLineBreak);
Assert.Equal(TextRun.DefaultTextSourceLength, textLine.TextRange.Length);
}
}
private class EndOfLineTextSource : ITextSource
{
public TextRun? GetTextRun(int textSourceIndex)
{
return new TextEndOfLine();
}
}
private class CustomTextSource : ITextSource
{
private readonly string _text;
public CustomTextSource(string text)
{
_text = text;
}
public TextRun? GetTextRun(int textSourceIndex)
{
if (textSourceIndex >= _text.Length + TextRun.DefaultTextSourceLength + _text.Length)
{
return null;
}
if (textSourceIndex == _text.Length)
{
return new RectangleRun(new Rect(0, 0, 50, 50), Brushes.Green);
}
return new TextCharacters(_text.AsMemory(),
new GenericTextRunProperties(Typeface.Default, foregroundBrush: Brushes.Black));
}
}
private class RectangleRun : DrawableTextRun
{
private readonly Rect _rect;
private readonly IBrush _fill;
public RectangleRun(Rect rect, IBrush fill)
{
_rect = rect;
_fill = fill;
}
public override Size Size => _rect.Size;
public override double Baseline => 0;
public override void Draw(DrawingContext drawingContext, Point origin)
{
using (drawingContext.PushPreTransform(Matrix.CreateTranslation(new Vector(origin.X, 0))))
{
drawingContext.FillRectangle(_fill, _rect);
}
}
}
public static IDisposable Start()
{
var disposable = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface

197
tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs

@ -277,6 +277,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
firstCharacterHit = previousCharacterHit;
firstCharacterHit = textLine.GetPreviousCaretCharacterHit(firstCharacterHit);
previousCharacterHit = textLine.GetPreviousCaretCharacterHit(firstCharacterHit);
Assert.Equal(firstCharacterHit.FirstCharacterIndex, previousCharacterHit.FirstCharacterIndex);
@ -416,24 +418,144 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
}
[Fact]
public void TextLineBreak_Should_Contain_TextEndOfLine()
public void Should_Get_Next_CharacterHit_For_Drawable_Runs()
{
using (Start())
{
var defaultProperties = new GenericTextRunProperties(Typeface.Default);
var textSource = new DrawableRunTextSource();
var formatter = new TextFormatterImpl();
var textLine =
formatter.FormatLine(textSource, 0, double.PositiveInfinity,
new GenericTextParagraphProperties(defaultProperties));
Assert.Equal(4, textLine.TextRuns.Count);
var currentHit = textLine.GetNextCaretCharacterHit(new CharacterHit(0));
Assert.Equal(1, currentHit.FirstCharacterIndex);
Assert.Equal(0, currentHit.TrailingLength);
currentHit = textLine.GetNextCaretCharacterHit(currentHit);
Assert.Equal(2, currentHit.FirstCharacterIndex);
Assert.Equal(0, currentHit.TrailingLength);
currentHit = textLine.GetNextCaretCharacterHit(currentHit);
Assert.Equal(3, currentHit.FirstCharacterIndex);
Assert.Equal(0, currentHit.TrailingLength);
currentHit = textLine.GetNextCaretCharacterHit(currentHit);
Assert.Equal(3, currentHit.FirstCharacterIndex);
Assert.Equal(1, currentHit.TrailingLength);
}
}
[Fact]
public void Should_Get_Previous_CharacterHit_For_Drawable_Runs()
{
using (Start())
{
var defaultTextRunProperties =
new GenericTextRunProperties(Typeface.Default);
var defaultProperties = new GenericTextRunProperties(Typeface.Default);
var textSource = new DrawableRunTextSource();
var formatter = new TextFormatterImpl();
var textLine =
formatter.FormatLine(textSource, 0, double.PositiveInfinity,
new GenericTextParagraphProperties(defaultProperties));
Assert.Equal(4, textLine.TextRuns.Count);
var currentHit = textLine.GetPreviousCaretCharacterHit(new CharacterHit(3,1));
const string text = "0123456789";
Assert.Equal(3, currentHit.FirstCharacterIndex);
Assert.Equal(0, currentHit.TrailingLength);
var source = new SingleBufferTextSource(text, defaultTextRunProperties);
currentHit = textLine.GetPreviousCaretCharacterHit(currentHit);
var textParagraphProperties = new GenericTextParagraphProperties(defaultTextRunProperties);
Assert.Equal(2, currentHit.FirstCharacterIndex);
Assert.Equal(0, currentHit.TrailingLength);
var formatter = TextFormatter.Current;
currentHit = textLine.GetPreviousCaretCharacterHit(currentHit);
var textLine = formatter.FormatLine(source, 0, double.PositiveInfinity, textParagraphProperties);
Assert.Equal(1, currentHit.FirstCharacterIndex);
Assert.Equal(0, currentHit.TrailingLength);
Assert.NotNull(textLine.TextLineBreak.TextEndOfLine);
currentHit = textLine.GetPreviousCaretCharacterHit(currentHit);
Assert.Equal(0, currentHit.FirstCharacterIndex);
Assert.Equal(0, currentHit.TrailingLength);
}
}
[Fact]
public void Should_Get_CharacterHit_From_Distance_For_Drawable_Runs()
{
using (Start())
{
var defaultProperties = new GenericTextRunProperties(Typeface.Default);
var textSource = new DrawableRunTextSource();
var formatter = new TextFormatterImpl();
var textLine =
formatter.FormatLine(textSource, 0, double.PositiveInfinity,
new GenericTextParagraphProperties(defaultProperties));
var characterHit = textLine.GetCharacterHitFromDistance(50);
Assert.Equal(3, characterHit.FirstCharacterIndex);
Assert.Equal(1, characterHit.TrailingLength);
characterHit = textLine.GetCharacterHitFromDistance(32);
Assert.Equal(3, characterHit.FirstCharacterIndex);
Assert.Equal(0, characterHit.TrailingLength);
}
}
private class DrawableRunTextSource : ITextSource
{
const string Text = "A_A_";
public TextRun? GetTextRun(int textSourceIndex)
{
switch (textSourceIndex)
{
case 0:
return new TextCharacters(new ReadOnlySlice<char>(Text.AsMemory(), 0, 1), new GenericTextRunProperties(Typeface.Default));
case 1:
return new CustomDrawableRun(1);
case 2:
return new TextCharacters(new ReadOnlySlice<char>(Text.AsMemory(), 2, 1, 2), new GenericTextRunProperties(Typeface.Default));
case 3:
return new CustomDrawableRun(3);
default:
return null;
}
}
}
private class CustomDrawableRun : DrawableTextRun
{
public CustomDrawableRun(int start)
{
Text = new ReadOnlySlice<char>(new char[1], start, DefaultTextSourceLength);
}
public override ReadOnlySlice<char> Text { get; }
public override Size Size => new(14, 14);
public override double Baseline => 14;
public override void Draw(DrawingContext drawingContext, Point origin)
{
}
}
@ -517,6 +639,63 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
return rects;
}
[Fact]
public void Should_Get_TextBounds()
{
using (Start())
{
var defaultProperties = new GenericTextRunProperties(Typeface.Default);
var text = "0123".AsMemory();
var ltrOptions = new TextShaperOptions(Typeface.Default.GlyphTypeface, 10, 0, CultureInfo.CurrentCulture);
var rtlOptions = new TextShaperOptions(Typeface.Default.GlyphTypeface, 10, 1, CultureInfo.CurrentCulture);
var textRuns = new List<TextRun>
{
new ShapedTextCharacters(TextShaper.Current.ShapeText(new ReadOnlySlice<char>(text), ltrOptions), defaultProperties),
new ShapedTextCharacters(TextShaper.Current.ShapeText(new ReadOnlySlice<char>(text, text.Length, text.Length), ltrOptions), defaultProperties),
new ShapedTextCharacters(TextShaper.Current.ShapeText(new ReadOnlySlice<char>(text, text.Length * 2, text.Length), rtlOptions), defaultProperties),
new ShapedTextCharacters(TextShaper.Current.ShapeText(new ReadOnlySlice<char>(text, text.Length * 3, text.Length), ltrOptions), defaultProperties)
};
var textSource = new FixedRunsTextSource(textRuns);
var formatter = new TextFormatterImpl();
var textLine =
formatter.FormatLine(textSource, 0, double.PositiveInfinity,
new GenericTextParagraphProperties(defaultProperties));
var textBounds = textLine.GetTextBounds(0, text.Length * 4);
Assert.Equal(3, textBounds.Count);
Assert.Equal(textLine.WidthIncludingTrailingWhitespace, textBounds.Sum(x => x.Rectangle.Width));
}
}
private class FixedRunsTextSource : ITextSource
{
private readonly IReadOnlyList<TextRun> _textRuns;
public FixedRunsTextSource(IReadOnlyList<TextRun> textRuns)
{
_textRuns = textRuns;
}
public TextRun? GetTextRun(int textSourceIndex)
{
foreach (var textRun in _textRuns)
{
if(textRun.Text.Start == textSourceIndex)
{
return textRun;
}
}
return null;
}
}
private static IDisposable Start()
{
var disposable = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface

24
tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextShaperTests.cs

@ -15,12 +15,8 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
using (Start())
{
var text = "\n\r\n".AsMemory();
var shapedBuffer = TextShaper.Current.ShapeText(
text,
Typeface.Default.GlyphTypeface,
12,
CultureInfo.CurrentCulture, 0);
var options = new TextShaperOptions(Typeface.Default.GlyphTypeface, 12,0, CultureInfo.CurrentCulture);
var shapedBuffer = TextShaper.Current.ShapeText(text, options);
Assert.Equal(shapedBuffer.Text.Length, text.Length);
Assert.Equal(shapedBuffer.GlyphClusters.Count, text.Length);
@ -29,7 +25,21 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
Assert.Equal(1, shapedBuffer.GlyphClusters[2]);
}
}
[Fact]
public void Should_Apply_IncrementalTabWidth()
{
using (Start())
{
var text = "\t".AsMemory();
var options = new TextShaperOptions(Typeface.Default.GlyphTypeface, 12, 0, CultureInfo.CurrentCulture, 100);
var shapedBuffer = TextShaper.Current.ShapeText(text, options);
Assert.Equal(shapedBuffer.Length, text.Length);
Assert.Equal(100, shapedBuffer.GlyphAdvances[0]);
}
}
private static IDisposable Start()
{
var disposable = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface

9
tests/Avalonia.UnitTests/HarfBuzzTextShaperImpl.cs

@ -1,6 +1,5 @@
using System;
using System.Globalization;
using Avalonia.Media;
using Avalonia.Media.TextFormatting;
using Avalonia.Media.TextFormatting.Unicode;
using Avalonia.Platform;
@ -12,9 +11,13 @@ namespace Avalonia.UnitTests
{
public class HarfBuzzTextShaperImpl : ITextShaperImpl
{
public ShapedBuffer ShapeText(ReadOnlySlice<char> text, GlyphTypeface typeface, double fontRenderingEmSize,
CultureInfo culture, sbyte bidiLevel)
public ShapedBuffer ShapeText(ReadOnlySlice<char> text, TextShaperOptions options)
{
var typeface = options.Typeface;
var fontRenderingEmSize = options.FontRenderingEmSize;
var bidiLevel = options.BidLevel;
var culture = options.Culture;
using (var buffer = new Buffer())
{
buffer.AddUtf16(text.Buffer.Span, text.Start, text.Length);

11
tests/Avalonia.UnitTests/MockTextShaperImpl.cs

@ -1,6 +1,4 @@
using System.Globalization;
using Avalonia.Media;
using Avalonia.Media.TextFormatting;
using Avalonia.Media.TextFormatting;
using Avalonia.Media.TextFormatting.Unicode;
using Avalonia.Platform;
using Avalonia.Utilities;
@ -9,9 +7,12 @@ namespace Avalonia.UnitTests
{
public class MockTextShaperImpl : ITextShaperImpl
{
public ShapedBuffer ShapeText(ReadOnlySlice<char> text, GlyphTypeface typeface, double fontRenderingEmSize,
CultureInfo culture, sbyte bidiLevel)
public ShapedBuffer ShapeText(ReadOnlySlice<char> text, TextShaperOptions options)
{
var typeface = options.Typeface;
var fontRenderingEmSize = options.FontRenderingEmSize;
var bidiLevel = options.BidLevel;
var shapedBuffer = new ShapedBuffer(text, text.Length, typeface, fontRenderingEmSize, bidiLevel);
for (var i = 0; i < shapedBuffer.Length;)

Loading…
Cancel
Save