diff --git a/src/Avalonia.Base/Media/TextFormatting/FormattedTextSource.cs b/src/Avalonia.Base/Media/TextFormatting/FormattedTextSource.cs
index 2f8c4ad263..a4fc1edd14 100644
--- a/src/Avalonia.Base/Media/TextFormatting/FormattedTextSource.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/FormattedTextSource.cs
@@ -48,7 +48,7 @@ namespace Avalonia.Media.TextFormatting
///
/// The created text style run.
///
- private static ValueSpan CreateTextStyleRun(ReadOnlySpan text, int firstTextSourceIndex,
+ internal static ValueSpan CreateTextStyleRun(ReadOnlySpan text, int firstTextSourceIndex,
TextRunProperties defaultProperties, IReadOnlyList>? textModifier)
{
if (textModifier == null || textModifier.Count == 0)
diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs
index fed0f2a56c..9a89ccaf28 100644
--- a/src/Avalonia.Controls/Presenters/TextPresenter.cs
+++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs
@@ -105,7 +105,7 @@ namespace Avalonia.Controls.Presenters
static TextPresenter()
{
- AffectsRender(CaretBrushProperty, SelectionBrushProperty, TextElement.ForegroundProperty);
+ AffectsRender(CaretBrushProperty, SelectionBrushProperty, SelectionForegroundBrushProperty, TextElement.ForegroundProperty);
}
public TextPresenter() { }
diff --git a/src/Avalonia.Controls/SelectableTextBlock.cs b/src/Avalonia.Controls/SelectableTextBlock.cs
index 4fa8ea989f..143ac29c02 100644
--- a/src/Avalonia.Controls/SelectableTextBlock.cs
+++ b/src/Avalonia.Controls/SelectableTextBlock.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Data;
using System.Linq;
using Avalonia.Controls.Documents;
using Avalonia.Controls.Utils;
@@ -32,6 +33,9 @@ namespace Avalonia.Controls
public static readonly StyledProperty SelectionBrushProperty =
TextBox.SelectionBrushProperty.AddOwner();
+ public static readonly StyledProperty SelectionForegroundBrushProperty =
+ TextBox.SelectionForegroundBrushProperty.AddOwner();
+
public static readonly DirectProperty CanCopyProperty =
TextBox.CanCopyProperty.AddOwner(o => o.CanCopy);
@@ -63,6 +67,15 @@ namespace Avalonia.Controls
set => SetValue(SelectionBrushProperty, value);
}
+ ///
+ /// Gets or sets a brush that is used for the foreground of selected text
+ ///
+ public IBrush? SelectionForegroundBrush
+ {
+ get => GetValue(SelectionForegroundBrushProperty);
+ set => SetValue(SelectionForegroundBrushProperty, value);
+ }
+
///
/// Gets or sets a character index for the beginning of the current selection.
///
@@ -167,6 +180,58 @@ namespace Avalonia.Controls
UpdateCommandStates();
}
+ protected override TextLayout CreateTextLayout(string? text)
+ {
+ var typeface = new Typeface(FontFamily, FontStyle, FontWeight, FontStretch);
+
+ var defaultProperties = new GenericTextRunProperties(
+ typeface,
+ FontSize,
+ TextDecorations,
+ Foreground);
+
+ var paragraphProperties = new GenericTextParagraphProperties(FlowDirection, TextAlignment, true, false,
+ defaultProperties, TextWrapping, LineHeight, 0, LetterSpacing)
+ {
+ LineSpacing = LineSpacing
+ };
+
+ IReadOnlyList>? textStyleOverrides = null;
+ var selectionStart = SelectionStart;
+ var selectionEnd = SelectionEnd;
+ var start = Math.Min(selectionStart, selectionEnd);
+ var length = Math.Max(selectionStart, selectionEnd) - start;
+
+ if (length > 0 && SelectionForegroundBrush != null)
+ {
+ textStyleOverrides = new[]
+ {
+ new ValueSpan(start, length,
+ new GenericTextRunProperties(typeface, FontSize,
+ foregroundBrush: SelectionForegroundBrush))
+ };
+ }
+
+ ITextSource textSource;
+
+ if (_textRuns != null)
+ {
+ textSource = new InlinesTextSource(_textRuns, textStyleOverrides);
+ }
+ else
+ {
+ textSource = new FormattedTextSource(text ?? "", defaultProperties, textStyleOverrides);
+ }
+
+ return new TextLayout(
+ textSource,
+ paragraphProperties,
+ TextTrimming,
+ _constraint.Width,
+ _constraint.Height,
+ MaxLines);
+ }
+
protected override void RenderTextLayout(DrawingContext context, Point origin)
{
var selectionStart = SelectionStart;
@@ -220,10 +285,17 @@ namespace Avalonia.Controls
{
base.OnPropertyChanged(change);
- if (change.Property == SelectionStartProperty || change.Property == SelectionEndProperty)
+ if (change.Property == SelectionStartProperty ||
+ change.Property == SelectionEndProperty)
{
RaisePropertyChanged(SelectedTextProperty, "", "");
UpdateCommandStates();
+ InvalidateTextLayout();
+ }
+
+ if(change.Property == SelectionForegroundBrushProperty)
+ {
+ InvalidateTextLayout();
}
}
diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs
index 1065bedc4b..cbda1dfbe6 100644
--- a/src/Avalonia.Controls/TextBlock.cs
+++ b/src/Avalonia.Controls/TextBlock.cs
@@ -7,6 +7,7 @@ using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.Media.TextFormatting;
using Avalonia.Metadata;
+using Avalonia.Utilities;
namespace Avalonia.Controls
{
@@ -155,8 +156,8 @@ namespace Avalonia.Controls
nameof(Inlines), t => t.Inlines, (t, v) => t.Inlines = v);
private TextLayout? _textLayout;
- private Size _constraint;
- private IReadOnlyList? _textRuns;
+ protected Size _constraint;
+ protected IReadOnlyList? _textRuns;
private InlineCollection? _inlines;
///
@@ -846,7 +847,7 @@ namespace Avalonia.Controls
InvalidateTextLayout();
}
- private readonly record struct SimpleTextSource : ITextSource
+ protected readonly record struct SimpleTextSource : ITextSource
{
private readonly string _text;
private readonly TextRunProperties _defaultProperties;
@@ -875,13 +876,17 @@ namespace Avalonia.Controls
}
}
- private readonly struct InlinesTextSource : ITextSource
+#pragma warning disable CA1815 // Equals und Gleichheitsoperator für Werttypen außer Kraft setzen
+ protected readonly struct InlinesTextSource : ITextSource
+#pragma warning restore CA1815 // Equals und Gleichheitsoperator für Werttypen außer Kraft setzen
{
private readonly IReadOnlyList _textRuns;
+ private readonly IReadOnlyList>? _textModifier;
- public InlinesTextSource(IReadOnlyList textRuns)
+ public InlinesTextSource(IReadOnlyList textRuns, IReadOnlyList>? textModifier = null)
{
_textRuns = textRuns;
+ _textModifier = textModifier;
}
public IReadOnlyList TextRuns => _textRuns;
@@ -904,11 +909,13 @@ namespace Avalonia.Controls
continue;
}
- if (textRun is TextCharacters)
+ if (textRun is TextCharacters textCharacters)
{
var skip = Math.Max(0, textSourceIndex - currentPosition);
- return new TextCharacters(textRun.Text.Slice(skip), textRun.Properties!);
+ var textStyleRun = FormattedTextSource.CreateTextStyleRun(textRun.Text.Slice(skip).Span, textSourceIndex, textCharacters.Properties, _textModifier);
+
+ return new TextCharacters(textRun.Text.Slice(skip, textStyleRun.Length), textStyleRun.Value);
}
return textRun;
@@ -916,6 +923,6 @@ namespace Avalonia.Controls
return new TextEndOfParagraph();
}
- }
+ }
}
}