diff --git a/src/Avalonia.Base/Controls/ResourceNodeExtensions.cs b/src/Avalonia.Base/Controls/ResourceNodeExtensions.cs
index 4b0bab0c92..8aed1545a5 100644
--- a/src/Avalonia.Base/Controls/ResourceNodeExtensions.cs
+++ b/src/Avalonia.Base/Controls/ResourceNodeExtensions.cs
@@ -138,18 +138,18 @@ namespace Avalonia.Controls
protected override void Initialize()
{
_target.ResourcesChanged += ResourcesChanged;
- if (_target is StyledElement themeStyleable)
+ if (_target is IThemeVariantHost themeVariantHost)
{
- themeStyleable.PropertyChanged += PropertyChanged;
+ themeVariantHost.ActualThemeVariantChanged += ActualThemeVariantChanged;
}
}
protected override void Deinitialize()
{
_target.ResourcesChanged -= ResourcesChanged;
- if (_target is StyledElement themeStyleable)
+ if (_target is IThemeVariantHost themeVariantHost)
{
- themeStyleable.PropertyChanged -= PropertyChanged;
+ themeVariantHost.ActualThemeVariantChanged -= ActualThemeVariantChanged;
}
}
@@ -163,18 +163,15 @@ namespace Avalonia.Controls
PublishNext(GetValue());
}
- private void PropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
+ private void ActualThemeVariantChanged(object? sender, EventArgs e)
{
- if (e.Property == StyledElement.ActualThemeVariantProperty)
- {
- PublishNext(GetValue());
- }
+ PublishNext(GetValue());
}
private object? GetValue()
{
- if (_target is not StyledElement themeStyleable
- || !_target.TryFindResource(_key, themeStyleable.ActualThemeVariant, out var value))
+ if (_target is not IThemeVariantHost themeVariantHost
+ || !_target.TryFindResource(_key, themeVariantHost.ActualThemeVariant, out var value))
{
value = _target.FindResource(_key) ?? AvaloniaProperty.UnsetValue;
}
@@ -236,9 +233,9 @@ namespace Avalonia.Controls
{
_owner.ResourcesChanged -= ResourcesChanged;
}
- if (_owner is StyledElement styleable)
+ if (_owner is IThemeVariantHost themeVariantHost)
{
- styleable.PropertyChanged += PropertyChanged;
+ themeVariantHost.ActualThemeVariantChanged += ActualThemeVariantChanged;
}
_owner = _target.Owner;
@@ -247,20 +244,18 @@ namespace Avalonia.Controls
{
_owner.ResourcesChanged += ResourcesChanged;
}
- if (_owner is StyledElement styleable2)
+ if (_owner is IThemeVariantHost themeVariantHost2)
{
- styleable2.PropertyChanged += PropertyChanged;
+ themeVariantHost2.ActualThemeVariantChanged -= ActualThemeVariantChanged;
}
+
PublishNext();
}
- private void PropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
+ private void ActualThemeVariantChanged(object? sender, EventArgs e)
{
- if (e.Property == StyledElement.ActualThemeVariantProperty)
- {
- PublishNext();
- }
+ PublishNext();
}
private void ResourcesChanged(object? sender, ResourcesChangedEventArgs e)
@@ -270,8 +265,8 @@ namespace Avalonia.Controls
private object? GetValue()
{
- if (!(_target.Owner is StyledElement themeStyleable)
- || !_target.Owner.TryFindResource(_key, themeStyleable.ActualThemeVariant, out var value))
+ if (!(_target.Owner is IThemeVariantHost themeVariantHost)
+ || !_target.Owner.TryFindResource(_key, themeVariantHost.ActualThemeVariant, out var value))
{
value = _target.Owner?.FindResource(_key) ?? AvaloniaProperty.UnsetValue;
}
diff --git a/src/Avalonia.Base/Layout/Layoutable.cs b/src/Avalonia.Base/Layout/Layoutable.cs
index 4a273b0291..ea84dc84bd 100644
--- a/src/Avalonia.Base/Layout/Layoutable.cs
+++ b/src/Avalonia.Base/Layout/Layoutable.cs
@@ -798,6 +798,12 @@ namespace Avalonia.Layout
InvalidateMeasure();
}
+ internal override void OnTemplatedParentControlThemeChanged()
+ {
+ base.OnTemplatedParentControlThemeChanged();
+ InvalidateMeasure();
+ }
+
///
/// Called when the layout manager raises a LayoutUpdated event.
///
diff --git a/src/Avalonia.Base/Media/FormattedText.cs b/src/Avalonia.Base/Media/FormattedText.cs
index 3b63a98720..d4640390d7 100644
--- a/src/Avalonia.Base/Media/FormattedText.cs
+++ b/src/Avalonia.Base/Media/FormattedText.cs
@@ -877,7 +877,7 @@ namespace Avalonia.Media
var lastRunProps = (GenericTextRunProperties)thatFormatRider.CurrentElement!;
- TextCollapsingProperties collapsingProperties = _that._trimming.CreateCollapsingProperties(new TextCollapsingCreateInfo(maxLineLength, lastRunProps));
+ TextCollapsingProperties collapsingProperties = _that._trimming.CreateCollapsingProperties(new TextCollapsingCreateInfo(maxLineLength, lastRunProps, paraProps.FlowDirection));
var collapsedLine = line.Collapse(collapsingProperties);
diff --git a/src/Avalonia.Base/Media/TextCollapsingCreateInfo.cs b/src/Avalonia.Base/Media/TextCollapsingCreateInfo.cs
index 40ba613717..9b7bf3f74c 100644
--- a/src/Avalonia.Base/Media/TextCollapsingCreateInfo.cs
+++ b/src/Avalonia.Base/Media/TextCollapsingCreateInfo.cs
@@ -6,11 +6,13 @@ namespace Avalonia.Media
{
public readonly double Width;
public readonly TextRunProperties TextRunProperties;
+ public readonly FlowDirection FlowDirection;
- public TextCollapsingCreateInfo(double width, TextRunProperties textRunProperties)
+ public TextCollapsingCreateInfo(double width, TextRunProperties textRunProperties, FlowDirection flowDirection)
{
Width = width;
TextRunProperties = textRunProperties;
+ FlowDirection = flowDirection;
}
}
}
diff --git a/src/Avalonia.Base/Media/TextFormatting/InterWordJustification.cs b/src/Avalonia.Base/Media/TextFormatting/InterWordJustification.cs
index 0d85f3e7c5..c1b9b77401 100644
--- a/src/Avalonia.Base/Media/TextFormatting/InterWordJustification.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/InterWordJustification.cs
@@ -27,16 +27,6 @@ namespace Avalonia.Media.TextFormatting
return;
}
- if (lineImpl.NewLineLength > 0)
- {
- return;
- }
-
- if (lineImpl.TextLineBreak is { TextEndOfLine: not null, IsSplit: false })
- {
- return;
- }
-
var breakOportunities = new Queue();
var currentPosition = textLine.FirstTextSourceIndex;
@@ -97,7 +87,8 @@ namespace Avalonia.Media.TextFormatting
continue;
}
- var glyphIndex = glyphRun.FindGlyphIndex(characterIndex);
+ var offset = Math.Max(0, currentPosition - glyphRun.Metrics.FirstCluster);
+ var glyphIndex = glyphRun.FindGlyphIndex(characterIndex - offset);
var glyphInfo = shapedBuffer.GlyphInfos[glyphIndex];
shapedBuffer.GlyphInfos[glyphIndex] = new GlyphInfo(glyphInfo.GlyphIndex,
diff --git a/src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs b/src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs
index 7f23ac98b4..568148e15c 100644
--- a/src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs
@@ -148,33 +148,38 @@ namespace Avalonia.Media.TextFormatting
internal SplitResult Split(int length)
{
- if (IsReversed)
+ var isReversed = IsReversed;
+
+ if (isReversed)
{
Reverse();
- }
+ length = Length - length;
+ }
#if DEBUG
- if(length == 0)
+ if (length == 0)
{
throw new ArgumentOutOfRangeException(nameof(length), "length must be greater than zero.");
}
-#endif
-
+#endif
var splitBuffer = ShapedBuffer.Split(length);
var first = new ShapedTextRun(splitBuffer.First, Properties);
- #if DEBUG
+#if DEBUG
if (first.Length != length)
{
throw new InvalidOperationException("Split length mismatch.");
}
-
#endif
-
var second = new ShapedTextRun(splitBuffer.Second!, Properties);
+ if (isReversed)
+ {
+ return new SplitResult(second, first);
+ }
+
return new SplitResult(first, second);
}
diff --git a/src/Avalonia.Base/Media/TextFormatting/TextCollapsingProperties.cs b/src/Avalonia.Base/Media/TextFormatting/TextCollapsingProperties.cs
index 72882df0b5..7cdf81ecc9 100644
--- a/src/Avalonia.Base/Media/TextFormatting/TextCollapsingProperties.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/TextCollapsingProperties.cs
@@ -1,4 +1,6 @@
-namespace Avalonia.Media.TextFormatting
+using System.Collections.Generic;
+
+namespace Avalonia.Media.TextFormatting
{
///
/// Properties of text collapsing.
@@ -15,6 +17,11 @@
///
public abstract TextRun Symbol { get; }
+ ///
+ /// Gets the flow direction that is used for collapsing.
+ ///
+ public abstract FlowDirection FlowDirection { get; }
+
///
/// Collapses given text line.
///
diff --git a/src/Avalonia.Base/Media/TextFormatting/TextEllipsisHelper.cs b/src/Avalonia.Base/Media/TextFormatting/TextEllipsisHelper.cs
index 4c93a1d851..6422f23dcd 100644
--- a/src/Avalonia.Base/Media/TextFormatting/TextEllipsisHelper.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/TextEllipsisHelper.cs
@@ -1,5 +1,4 @@
using System;
-using System.Collections.Generic;
using Avalonia.Media.TextFormatting.Unicode;
namespace Avalonia.Media.TextFormatting
@@ -28,97 +27,191 @@ namespace Avalonia.Media.TextFormatting
var availableWidth = properties.Width - shapedSymbol.Size.Width;
- while (runIndex < textRuns.Count)
+ if(properties.FlowDirection== FlowDirection.LeftToRight)
{
- var currentRun = textRuns[runIndex];
-
- switch (currentRun)
+ while (runIndex < textRuns.Count)
{
- case ShapedTextRun shapedRun:
- {
- currentWidth += shapedRun.Size.Width;
+ var currentRun = textRuns[runIndex];
- if (currentWidth > availableWidth)
+ switch (currentRun)
+ {
+ case ShapedTextRun shapedRun:
{
- if (shapedRun.TryMeasureCharacters(availableWidth, out var measuredLength))
+ currentWidth += shapedRun.Size.Width;
+
+ if (currentWidth > availableWidth)
{
- if (isWordEllipsis && measuredLength < textLine.Length)
+ if (shapedRun.TryMeasureCharacters(availableWidth, out var measuredLength))
{
- var currentBreakPosition = 0;
-
- var lineBreaker = new LineBreakEnumerator(currentRun.Text.Span);
-
- while (currentBreakPosition < measuredLength && lineBreaker.MoveNext(out var lineBreak))
+ if (isWordEllipsis && measuredLength < textLine.Length)
{
- var nextBreakPosition = lineBreak.PositionMeasure;
+ var currentBreakPosition = 0;
- if (nextBreakPosition == 0)
- {
- break;
- }
+ var lineBreaker = new LineBreakEnumerator(currentRun.Text.Span);
- if (nextBreakPosition >= measuredLength)
+ while (currentBreakPosition < measuredLength && lineBreaker.MoveNext(out var lineBreak))
{
- break;
+ var nextBreakPosition = lineBreak.PositionMeasure;
+
+ if (nextBreakPosition == 0)
+ {
+ break;
+ }
+
+ if (nextBreakPosition >= measuredLength)
+ {
+ break;
+ }
+
+ currentBreakPosition = nextBreakPosition;
}
- currentBreakPosition = nextBreakPosition;
+ measuredLength = currentBreakPosition;
}
-
- measuredLength = currentBreakPosition;
}
+
+ collapsedLength += measuredLength;
+
+ return CreateCollapsedRuns(textLine, collapsedLength, FlowDirection.LeftToRight, shapedSymbol);
}
- collapsedLength += measuredLength;
+ availableWidth -= shapedRun.Size.Width;
- return CreateCollapsedRuns(textRuns, collapsedLength, shapedSymbol);
+ break;
}
- availableWidth -= shapedRun.Size.Width;
+ case DrawableTextRun drawableRun:
+ {
+ //The whole run needs to fit into available space
+ if (currentWidth + drawableRun.Size.Width > availableWidth)
+ {
+ return CreateCollapsedRuns(textLine, collapsedLength, FlowDirection.LeftToRight, shapedSymbol);
+ }
- break;
- }
+ availableWidth -= drawableRun.Size.Width;
- case DrawableTextRun drawableRun:
- {
- //The whole run needs to fit into available space
- if (currentWidth + drawableRun.Size.Width > availableWidth)
- {
- return CreateCollapsedRuns(textRuns, collapsedLength, shapedSymbol);
+ break;
}
+ }
- availableWidth -= drawableRun.Size.Width;
+ collapsedLength += currentRun.Length;
- break;
- }
+ runIndex++;
}
+ }
+ else
+ {
+ runIndex = textRuns.Count - 1;
+
+ while (runIndex >= 0)
+ {
+ var currentRun = textRuns[runIndex];
- collapsedLength += currentRun.Length;
+ switch (currentRun)
+ {
+ case ShapedTextRun shapedRun:
+ {
+ currentWidth += shapedRun.Size.Width;
- runIndex++;
- }
+ if (currentWidth > availableWidth)
+ {
+ if (shapedRun.TryMeasureCharacters(availableWidth, out var measuredLength))
+ {
+ if (isWordEllipsis && measuredLength < textLine.Length)
+ {
+ var currentBreakPosition = 0;
+
+ var lineBreaker = new LineBreakEnumerator(currentRun.Text.Span);
+
+ while (currentBreakPosition < measuredLength && lineBreaker.MoveNext(out var lineBreak))
+ {
+ var nextBreakPosition = lineBreak.PositionMeasure;
+
+ if (nextBreakPosition == 0)
+ {
+ break;
+ }
+
+ if (nextBreakPosition >= measuredLength)
+ {
+ break;
+ }
+ currentBreakPosition = nextBreakPosition;
+ }
+
+ measuredLength = currentBreakPosition;
+ }
+ }
+
+ collapsedLength += measuredLength;
+
+ return CreateCollapsedRuns(textLine, collapsedLength, FlowDirection.RightToLeft, shapedSymbol);
+ }
+
+ availableWidth -= shapedRun.Size.Width;
+
+ break;
+ }
+
+ case DrawableTextRun drawableRun:
+ {
+ //The whole run needs to fit into available space
+ if (currentWidth + drawableRun.Size.Width > availableWidth)
+ {
+ return CreateCollapsedRuns(textLine, collapsedLength, FlowDirection.RightToLeft, shapedSymbol);
+ }
+
+ availableWidth -= drawableRun.Size.Width;
+
+ break;
+ }
+ }
+
+ collapsedLength += currentRun.Length;
+
+ runIndex--;
+ }
+ }
+
return null;
}
- private static TextRun[] CreateCollapsedRuns(IReadOnlyList textRuns, int collapsedLength,
- TextRun shapedSymbol)
+ private static TextRun[] CreateCollapsedRuns(TextLine textLine, int collapsedLength,
+ FlowDirection flowDirection, TextRun shapedSymbol)
{
+ var textRuns = textLine.TextRuns;
+
if (collapsedLength <= 0)
{
return new[] { shapedSymbol };
}
+ if(flowDirection == FlowDirection.RightToLeft)
+ {
+ collapsedLength = textLine.Length - collapsedLength;
+ }
+
var objectPool = FormattingObjectPool.Instance;
var (preSplitRuns, postSplitRuns) = TextFormatterImpl.SplitTextRuns(textRuns, collapsedLength, objectPool);
try
{
- var collapsedRuns = new TextRun[preSplitRuns.Count + 1];
- preSplitRuns.CopyTo(collapsedRuns);
- collapsedRuns[collapsedRuns.Length - 1] = shapedSymbol;
- return collapsedRuns;
+ if (flowDirection == FlowDirection.RightToLeft)
+ {
+ var collapsedRuns = new TextRun[postSplitRuns!.Count + 1];
+ postSplitRuns.CopyTo(collapsedRuns, 1);
+ collapsedRuns[0] = shapedSymbol;
+ return collapsedRuns;
+ }
+ else
+ {
+ var collapsedRuns = new TextRun[preSplitRuns!.Count + 1];
+ preSplitRuns.CopyTo(collapsedRuns);
+ collapsedRuns[collapsedRuns.Length - 1] = shapedSymbol;
+ return collapsedRuns;
+ }
}
finally
{
diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs b/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs
index 4dbc472133..a382416b8a 100644
--- a/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs
@@ -352,7 +352,7 @@ namespace Avalonia.Media.TextFormatting
var lastTrailingIndex = 0;
- if(_paragraphProperties.FlowDirection== FlowDirection.LeftToRight)
+ if (_paragraphProperties.FlowDirection == FlowDirection.LeftToRight)
{
lastTrailingIndex = textLine.FirstTextSourceIndex + textLine.Length;
@@ -377,7 +377,7 @@ namespace Avalonia.Media.TextFormatting
{
lastTrailingIndex += textEndOfLine.Length;
}
- }
+ }
var textPosition = characterHit.FirstCharacterIndex + characterHit.TrailingLength;
@@ -553,26 +553,18 @@ namespace Avalonia.Media.TextFormatting
if (_paragraphProperties.TextAlignment == TextAlignment.Justify)
{
- var whitespaceWidth = 0d;
+ var justificationWidth = MaxWidth;
- for (var i = 0; i < textLines.Count; i++)
+ if (_paragraphProperties.TextWrapping != TextWrapping.NoWrap)
{
- var line = textLines[i];
- var lineWhitespaceWidth = line.Width - line.WidthIncludingTrailingWhitespace;
-
- if (lineWhitespaceWidth > whitespaceWidth)
- {
- whitespaceWidth = lineWhitespaceWidth;
- }
+ justificationWidth = width;
}
- var justificationWidth = width - whitespaceWidth;
-
if (justificationWidth > 0)
{
var justificationProperties = new InterWordJustification(justificationWidth);
- for (var i = 0; i < textLines.Count - 1; i++)
+ for (var i = 0; i < textLines.Count; i++)
{
var line = textLines[i];
@@ -597,12 +589,13 @@ namespace Avalonia.Media.TextFormatting
/// The .
private TextCollapsingProperties? GetCollapsingProperties(double width)
{
- if(_textTrimming == TextTrimming.None)
+ if (_textTrimming == TextTrimming.None)
{
return null;
}
- return _textTrimming.CreateCollapsingProperties(new TextCollapsingCreateInfo(width, _paragraphProperties.DefaultTextRunProperties));
+ return _textTrimming.CreateCollapsingProperties(
+ new TextCollapsingCreateInfo(width, _paragraphProperties.DefaultTextRunProperties, _paragraphProperties.FlowDirection));
}
public void Dispose()
diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs b/src/Avalonia.Base/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs
index 2e85b1e187..a21a5d45e9 100644
--- a/src/Avalonia.Base/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs
@@ -19,11 +19,13 @@ namespace Avalonia.Media.TextFormatting
/// Length of leading prefix.
/// width in which collapsing is constrained to
/// text run properties of ellipsis symbol
+ /// the flow direction of the collapes line.
public TextLeadingPrefixCharacterEllipsis(
string ellipsis,
int prefixLength,
double width,
- TextRunProperties textRunProperties)
+ TextRunProperties textRunProperties,
+ FlowDirection flowDirection)
{
if (_prefixLength < 0)
{
@@ -33,6 +35,7 @@ namespace Avalonia.Media.TextFormatting
_prefixLength = prefixLength;
Width = width;
Symbol = new TextCharacters(ellipsis, textRunProperties);
+ FlowDirection = flowDirection;
}
///
@@ -41,6 +44,8 @@ namespace Avalonia.Media.TextFormatting
///
public override TextRun Symbol { get; }
+ public override FlowDirection FlowDirection { get; }
+
///
public override TextRun[]? Collapse(TextLine textLine)
{
diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs
index 187b3154ad..b3321d4d9f 100644
--- a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs
@@ -658,7 +658,7 @@ namespace Avalonia.Media.TextFormatting
currentX += drawableTextRun.Size.Width;
}
- if(lastRunIndex - 1 < 0)
+ if (lastRunIndex - 1 < 0)
{
break;
}
@@ -685,7 +685,7 @@ namespace Avalonia.Media.TextFormatting
directionalWidth -= drawableTextRun.Size.Width;
}
- if(firstRunIndex + 1 == _textRuns.Length)
+ if (firstRunIndex + 1 == _textRuns.Length)
{
break;
}
@@ -1097,7 +1097,7 @@ namespace Avalonia.Media.TextFormatting
var runWidth = endX - startX;
- return new TextRunBounds(new Rect(Start + startX, 0, runWidth, Height), currentPosition, characterLength, currentRun);
+ return new TextRunBounds(new Rect(startX, 0, runWidth, Height), currentPosition, characterLength, currentRun);
}
public override void Dispose()
@@ -1439,13 +1439,6 @@ namespace Avalonia.Media.TextFormatting
}
}
- if (index == lastRunIndex)
- {
- width = widthIncludingWhitespace + textRun.GlyphRun.Metrics.Width;
- trailingWhitespaceLength = textRun.GlyphRun.Metrics.TrailingWhitespaceLength;
- newLineLength += textRun.GlyphRun.Metrics.NewLineLength;
- }
-
widthIncludingWhitespace += textRun.Size.Width;
break;
@@ -1455,12 +1448,6 @@ namespace Avalonia.Media.TextFormatting
{
widthIncludingWhitespace += drawableTextRun.Size.Width;
- if (index == lastRunIndex)
- {
- width = widthIncludingWhitespace;
- trailingWhitespaceLength = 0;
- }
-
if (drawableTextRun.Size.Height > height)
{
height = drawableTextRun.Size.Height;
@@ -1476,6 +1463,32 @@ namespace Avalonia.Media.TextFormatting
}
}
+ width = widthIncludingWhitespace;
+
+ for (var i = _textRuns.Length - 1; i >= 0; i--)
+ {
+ var currentRun = _textRuns[i];
+
+ if(currentRun is ShapedTextRun shapedText)
+ {
+ var glyphRun = shapedText.GlyphRun;
+ var glyphRunMetrics = glyphRun.Metrics;
+
+ newLineLength += glyphRunMetrics.NewLineLength;
+
+ if (glyphRunMetrics.TrailingWhitespaceLength == 0)
+ {
+ break;
+ }
+
+ trailingWhitespaceLength += glyphRunMetrics.TrailingWhitespaceLength;
+
+ var whitespaceWidth = glyphRun.Size.Width - glyphRunMetrics.Width;
+
+ width -= whitespaceWidth;
+ }
+ }
+
var start = GetParagraphOffsetX(width, widthIncludingWhitespace);
if (!double.IsNaN(lineHeight) && !MathUtilities.IsZero(lineHeight))
@@ -1543,7 +1556,7 @@ namespace Avalonia.Media.TextFormatting
return Math.Max(0, start);
case TextAlignment.Right:
- return Math.Max(0, _paragraphWidth - widthIncludingTrailingWhitespace);
+ return Math.Max(0, _paragraphWidth - width);
default:
return 0;
diff --git a/src/Avalonia.Base/Media/TextFormatting/TextTrailingCharacterEllipsis.cs b/src/Avalonia.Base/Media/TextFormatting/TextTrailingCharacterEllipsis.cs
index ccae99cc75..8a6607bce2 100644
--- a/src/Avalonia.Base/Media/TextFormatting/TextTrailingCharacterEllipsis.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/TextTrailingCharacterEllipsis.cs
@@ -12,10 +12,13 @@
/// Text used as collapsing symbol.
/// Width in which collapsing is constrained to.
/// Text run properties of ellipsis symbol.
- public TextTrailingCharacterEllipsis(string ellipsis, double width, TextRunProperties textRunProperties)
+ /// The flow direction of the collapsed line.
+ public TextTrailingCharacterEllipsis(string ellipsis, double width,
+ TextRunProperties textRunProperties, FlowDirection flowDirection)
{
Width = width;
Symbol = new TextCharacters(ellipsis, textRunProperties);
+ FlowDirection = flowDirection;
}
///
@@ -24,6 +27,8 @@
///
public override TextRun Symbol { get; }
+ public override FlowDirection FlowDirection { get; }
+
///
public override TextRun[]? Collapse(TextLine textLine)
{
diff --git a/src/Avalonia.Base/Media/TextFormatting/TextTrailingWordEllipsis.cs b/src/Avalonia.Base/Media/TextFormatting/TextTrailingWordEllipsis.cs
index c622c76a60..5252766382 100644
--- a/src/Avalonia.Base/Media/TextFormatting/TextTrailingWordEllipsis.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/TextTrailingWordEllipsis.cs
@@ -12,14 +12,17 @@
/// Text used as collapsing symbol.
/// width in which collapsing is constrained to.
/// text run properties of ellipsis symbol.
+ /// flow direction of the collapsed line.
public TextTrailingWordEllipsis(
string ellipsis,
double width,
- TextRunProperties textRunProperties
+ TextRunProperties textRunProperties,
+ FlowDirection flowDirection
)
{
Width = width;
Symbol = new TextCharacters(ellipsis, textRunProperties);
+ FlowDirection = flowDirection;
}
///
@@ -28,6 +31,8 @@
///
public override TextRun Symbol { get; }
+ public override FlowDirection FlowDirection { get; }
+
///
public override TextRun[]? Collapse(TextLine textLine)
{
diff --git a/src/Avalonia.Base/Media/TextLeadingPrefixTrimming.cs b/src/Avalonia.Base/Media/TextLeadingPrefixTrimming.cs
index 7ba25eb005..19e6a70357 100644
--- a/src/Avalonia.Base/Media/TextLeadingPrefixTrimming.cs
+++ b/src/Avalonia.Base/Media/TextLeadingPrefixTrimming.cs
@@ -15,7 +15,7 @@ namespace Avalonia.Media
public override TextCollapsingProperties CreateCollapsingProperties(TextCollapsingCreateInfo createInfo)
{
- return new TextLeadingPrefixCharacterEllipsis(_ellipsis, _prefixLength, createInfo.Width, createInfo.TextRunProperties);
+ return new TextLeadingPrefixCharacterEllipsis(_ellipsis, _prefixLength, createInfo.Width, createInfo.TextRunProperties, createInfo.FlowDirection);
}
public override string ToString()
diff --git a/src/Avalonia.Base/Media/TextTrailingTrimming.cs b/src/Avalonia.Base/Media/TextTrailingTrimming.cs
index 2edbaabbc6..8a3c5aa397 100644
--- a/src/Avalonia.Base/Media/TextTrailingTrimming.cs
+++ b/src/Avalonia.Base/Media/TextTrailingTrimming.cs
@@ -17,10 +17,10 @@ namespace Avalonia.Media
{
if (_isWordBased)
{
- return new TextTrailingWordEllipsis(_ellipsis, createInfo.Width, createInfo.TextRunProperties);
+ return new TextTrailingWordEllipsis(_ellipsis, createInfo.Width, createInfo.TextRunProperties, createInfo.FlowDirection);
}
- return new TextTrailingCharacterEllipsis(_ellipsis, createInfo.Width, createInfo.TextRunProperties);
+ return new TextTrailingCharacterEllipsis(_ellipsis, createInfo.Width, createInfo.TextRunProperties, createInfo.FlowDirection);
}
public override string ToString()
diff --git a/src/Avalonia.Base/StyledElement.cs b/src/Avalonia.Base/StyledElement.cs
index 5b8dac2f53..39aaf55014 100644
--- a/src/Avalonia.Base/StyledElement.cs
+++ b/src/Avalonia.Base/StyledElement.cs
@@ -28,7 +28,7 @@ namespace Avalonia
public class StyledElement : Animatable,
IDataContextProvider,
ILogical,
- IResourceHost,
+ IThemeVariantHost,
IStyleHost,
IStyleable,
ISetLogicalParent,
@@ -75,23 +75,6 @@ namespace Avalonia
public static readonly StyledProperty ThemeProperty =
AvaloniaProperty.Register(nameof(Theme));
- ///
- /// Defines the property.
- ///
- public static readonly StyledProperty ActualThemeVariantProperty =
- AvaloniaProperty.Register(
- nameof(ThemeVariant),
- inherits: true,
- defaultValue: ThemeVariant.Light);
-
- ///
- /// Defines the RequestedThemeVariant property.
- ///
- public static readonly StyledProperty RequestedThemeVariantProperty =
- AvaloniaProperty.Register(
- nameof(ThemeVariant),
- defaultValue: ThemeVariant.Default);
-
private static readonly ControlTheme s_invalidTheme = new ControlTheme();
private int _initCount;
private string? _name;
@@ -160,6 +143,9 @@ namespace Avalonia
///
public event EventHandler? ResourcesChanged;
+ ///
+ public event EventHandler? ActualThemeVariantChanged;
+
///
/// Gets or sets the name of the styled element.
///
@@ -278,15 +264,6 @@ namespace Avalonia
set => SetValue(ThemeProperty, value);
}
- ///
- /// Gets the UI theme that is currently used by the element, which might be different than the .
- ///
- ///
- /// If current control is contained in the ThemeVariantScope, TopLevel or Application with non-default RequestedThemeVariant, that value will be returned.
- /// Otherwise, current OS theme variant is returned.
- ///
- public ThemeVariant ActualThemeVariant => GetValue(ActualThemeVariantProperty);
-
///
/// Gets the styled element's logical children.
///
@@ -325,6 +302,9 @@ namespace Avalonia
///
public StyledElement? Parent { get; private set; }
+ ///
+ public ThemeVariant ActualThemeVariant => GetValue(ThemeVariant.ActualThemeVariantProperty);
+
///
/// Gets the styled element's logical parent.
///
@@ -394,7 +374,7 @@ namespace Avalonia
///
public bool ApplyStyling()
{
- if (_initCount == 0 && (!_stylesApplied || !_themeApplied))
+ if (_initCount == 0 && (!_stylesApplied || !_themeApplied || !_templatedParentThemeApplied))
{
GetValueStore().BeginStyling();
@@ -644,13 +624,19 @@ namespace Avalonia
base.OnPropertyChanged(change);
if (change.Property == ThemeProperty)
+ {
OnControlThemeChanged();
- else if (change.Property == RequestedThemeVariantProperty)
+ }
+ else if (change.Property == ThemeVariant.RequestedThemeVariantProperty)
{
if (change.GetNewValue() is {} themeVariant && themeVariant != ThemeVariant.Default)
- SetValue(ActualThemeVariantProperty, themeVariant);
+ SetValue(ThemeVariant.ActualThemeVariantProperty, themeVariant);
else
- ClearValue(ActualThemeVariantProperty);
+ ClearValue(ThemeVariant.ActualThemeVariantProperty);
+ }
+ else if (change.Property == ThemeVariant.ActualThemeVariantProperty)
+ {
+ ActualThemeVariantChanged?.Invoke(this, EventArgs.Empty);
}
}
diff --git a/src/Avalonia.Base/Styling/IGlobalThemeVariantProvider.cs b/src/Avalonia.Base/Styling/IGlobalThemeVariantProvider.cs
deleted file mode 100644
index 2467d99b3b..0000000000
--- a/src/Avalonia.Base/Styling/IGlobalThemeVariantProvider.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using System;
-using Avalonia.Controls;
-using Avalonia.Metadata;
-
-namespace Avalonia.Styling;
-
-///
-/// Interface for an application host element with a root theme variant.
-///
-[Unstable]
-public interface IGlobalThemeVariantProvider : IResourceHost
-{
- ///
- /// Gets the UI theme variant that is used by the control (and its child elements) for resource determination.
- ///
- ThemeVariant ActualThemeVariant { get; }
-
- ///
- /// Raised when the theme variant is changed on the element or an ancestor of the element.
- ///
- event EventHandler? ActualThemeVariantChanged;
-}
diff --git a/src/Avalonia.Base/Styling/IThemeVariantHost.cs b/src/Avalonia.Base/Styling/IThemeVariantHost.cs
new file mode 100644
index 0000000000..01583148a8
--- /dev/null
+++ b/src/Avalonia.Base/Styling/IThemeVariantHost.cs
@@ -0,0 +1,26 @@
+using System;
+using Avalonia.Controls;
+using Avalonia.Metadata;
+
+namespace Avalonia.Styling;
+
+///
+/// Interface for the host element with a theme variant.
+///
+[Unstable]
+public interface IThemeVariantHost : IResourceHost
+{
+ ///
+ /// Gets the UI theme that is currently used by the element, which might be different than the RequestedThemeVariantProperty.
+ ///
+ ///
+ /// If current control is contained in the ThemeVariantScope, TopLevel or Application with non-default RequestedThemeVariant, that value will be returned.
+ /// Otherwise, current OS theme variant is returned.
+ ///
+ ThemeVariant ActualThemeVariant { get; }
+
+ ///
+ /// Raised when the theme variant is changed on the element or an ancestor of the element.
+ ///
+ event EventHandler? ActualThemeVariantChanged;
+}
diff --git a/src/Avalonia.Base/Styling/ThemeVariant.cs b/src/Avalonia.Base/Styling/ThemeVariant.cs
index 8218533f4f..389136b0f5 100644
--- a/src/Avalonia.Base/Styling/ThemeVariant.cs
+++ b/src/Avalonia.Base/Styling/ThemeVariant.cs
@@ -6,11 +6,26 @@ using Avalonia.Platform;
namespace Avalonia.Styling;
///
-/// Specifies a UI theme variant that should be used for the
+/// Specifies a UI theme variant that should be used for the Control and Application types.
///
[TypeConverter(typeof(ThemeVariantTypeConverter))]
public sealed record ThemeVariant
{
+ ///
+ /// Defines the ActualThemeVariant property.
+ ///
+ internal static readonly StyledProperty ActualThemeVariantProperty =
+ AvaloniaProperty.Register(
+ "ActualThemeVariant",
+ inherits: true);
+
+ ///
+ /// Defines the RequestedThemeVariant property.
+ ///
+ internal static readonly StyledProperty RequestedThemeVariantProperty =
+ AvaloniaProperty.Register(
+ "RequestedThemeVariant", defaultValue: Default);
+
///
/// Creates a new instance of the
///
diff --git a/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml b/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml
index dd8575c989..ca516c8918 100644
--- a/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml
+++ b/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml
@@ -1,6 +1,48 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
0.6
0.8
@@ -9,19 +51,6 @@
M515 93l930 931l-930 931l90 90l1022 -1021l-1022 -1021z
M109 486 19 576 1024 1581 2029 576 1939 486 1024 1401z
-
-
-
-
-
-
-
-
-
-
-
@@ -29,23 +58,10 @@
-
+
-
-
-
-
-
-
-
-
-
@@ -565,5 +581,6 @@
+
diff --git a/src/Avalonia.Controls/Application.cs b/src/Avalonia.Controls/Application.cs
index 6d3ba3cf8a..6d9a6bd493 100644
--- a/src/Avalonia.Controls/Application.cs
+++ b/src/Avalonia.Controls/Application.cs
@@ -29,7 +29,7 @@ namespace Avalonia
/// method.
/// - Tracks the lifetime of the application.
///
- public class Application : AvaloniaObject, IDataContextProvider, IGlobalDataTemplates, IGlobalStyles, IGlobalThemeVariantProvider, IApplicationPlatformEvents
+ public class Application : AvaloniaObject, IDataContextProvider, IGlobalDataTemplates, IGlobalStyles, IThemeVariantHost, IApplicationPlatformEvents
{
///
/// The application-global data templates.
@@ -50,13 +50,13 @@ namespace Avalonia
public static readonly StyledProperty