diff --git a/samples/ControlCatalog/Pages/GesturePage.cs b/samples/ControlCatalog/Pages/GesturePage.cs
index ee10f21317..0bb8f38219 100644
--- a/samples/ControlCatalog/Pages/GesturePage.cs
+++ b/samples/ControlCatalog/Pages/GesturePage.cs
@@ -6,6 +6,7 @@ using Avalonia.Input;
using Avalonia.LogicalTree;
using Avalonia.Markup.Xaml;
using Avalonia.Rendering.Composition;
+using Avalonia.Utilities;
namespace ControlCatalog.Pages
{
@@ -53,6 +54,7 @@ namespace ControlCatalog.Pages
{
_currentScale = 1;
compositionVisual.Scale = new Vector3(1,1,1);
+ compositionVisual.Offset = default;
image.InvalidateMeasure();
}
};
@@ -100,13 +102,19 @@ namespace ControlCatalog.Pages
{
InitComposition(control!);
- isZooming = true;
-
if(compositionVisual != null)
{
var scale = _currentScale * (float)e.Scale;
+ if (scale <= 1)
+ {
+ scale = 1;
+ compositionVisual.Offset = default;
+ }
+
compositionVisual.Scale = new(scale, scale, 1);
+
+ e.Handled = true;
}
});
@@ -114,8 +122,6 @@ namespace ControlCatalog.Pages
{
InitComposition(control!);
- isZooming = false;
-
if (compositionVisual != null)
{
_currentScale = compositionVisual.Scale.X;
@@ -126,11 +132,19 @@ namespace ControlCatalog.Pages
{
InitComposition(control!);
- if (compositionVisual != null && !isZooming)
+ if (compositionVisual != null && _currentScale != 1)
{
- currentOffset -= new Vector3((float)e.Delta.X, (float)e.Delta.Y, 0);
+ currentOffset += new Vector3((float)e.Delta.X, (float)e.Delta.Y, 0);
+
+ var currentSize = control.Bounds.Size * _currentScale;
+
+ currentOffset = new Vector3((float)MathUtilities.Clamp(currentOffset.X, 0, currentSize.Width - control.Bounds.Width),
+ (float)MathUtilities.Clamp(currentOffset.Y, 0, currentSize.Height - control.Bounds.Height),
+ 0);
- compositionVisual.Offset = currentOffset;
+ compositionVisual.Offset = currentOffset * -1;
+
+ e.Handled = true;
}
});
}
@@ -173,6 +187,8 @@ namespace ControlCatalog.Pages
if (ballCompositionVisual != null)
{
ballCompositionVisual.Offset = defaultOffset + new System.Numerics.Vector3((float)e.Delta.X * 0.4f, (float)e.Delta.Y * 0.4f, 0) * (inverse ? -1 : 1);
+
+ e.Handled = true;
}
});
@@ -187,11 +203,6 @@ namespace ControlCatalog.Pages
void InitComposition(Control control)
{
- if (ballCompositionVisual != null)
- {
- return;
- }
-
ballCompositionVisual = ElementComposition.GetElementVisual(ball);
if (ballCompositionVisual != null)
diff --git a/src/Avalonia.Base/Input/GestureRecognizers/PinchGestureRecognizer.cs b/src/Avalonia.Base/Input/GestureRecognizers/PinchGestureRecognizer.cs
index eea7c3b7d1..3b83d0cb87 100644
--- a/src/Avalonia.Base/Input/GestureRecognizers/PinchGestureRecognizer.cs
+++ b/src/Avalonia.Base/Input/GestureRecognizers/PinchGestureRecognizer.cs
@@ -57,7 +57,10 @@ namespace Avalonia.Input
var scale = distance / _initialDistance;
- _target?.RaiseEvent(new PinchEventArgs(scale, _origin));
+ var pinchEventArgs = new PinchEventArgs(scale, _origin);
+ _target?.RaiseEvent(pinchEventArgs);
+
+ e.Handled = pinchEventArgs.Handled;
}
}
}
diff --git a/src/Avalonia.Base/Input/GestureRecognizers/PullGestureRecognizer.cs b/src/Avalonia.Base/Input/GestureRecognizers/PullGestureRecognizer.cs
index 23bab13fc8..991694cc60 100644
--- a/src/Avalonia.Base/Input/GestureRecognizers/PullGestureRecognizer.cs
+++ b/src/Avalonia.Base/Input/GestureRecognizers/PullGestureRecognizer.cs
@@ -1,4 +1,5 @@
using System;
+using System.Diagnostics;
using Avalonia.Input.GestureRecognizers;
namespace Avalonia.Input
@@ -88,7 +89,10 @@ namespace Avalonia.Input
}
_pullInProgress = true;
- _target?.RaiseEvent(new PullGestureEventArgs(_gestureId, delta, PullDirection));
+ var pullEventArgs = new PullGestureEventArgs(_gestureId, delta, PullDirection);
+ _target?.RaiseEvent(pullEventArgs);
+
+ e.Handled = pullEventArgs.Handled;
}
}
diff --git a/src/Avalonia.Base/Media/Brush.cs b/src/Avalonia.Base/Media/Brush.cs
index b9a560ad8f..accabce145 100644
--- a/src/Avalonia.Base/Media/Brush.cs
+++ b/src/Avalonia.Base/Media/Brush.cs
@@ -11,7 +11,7 @@ namespace Avalonia.Media
/// Describes how an area is painted.
///
[TypeConverter(typeof(BrushConverter))]
- public abstract class Brush : Animatable
+ public abstract class Brush : Animatable, IBrush
{
///
/// Defines the property.
diff --git a/src/Avalonia.Base/Media/GlyphRun.cs b/src/Avalonia.Base/Media/GlyphRun.cs
index 65575617d0..0ec7152359 100644
--- a/src/Avalonia.Base/Media/GlyphRun.cs
+++ b/src/Avalonia.Base/Media/GlyphRun.cs
@@ -13,14 +13,22 @@ namespace Avalonia.Media
///
public sealed class GlyphRun : IDisposable
{
+ private readonly static IPlatformRenderInterface s_renderInterface;
+
private IRef? _platformImpl;
private double _fontRenderingEmSize;
private int _biDiLevel;
private GlyphRunMetrics? _glyphRunMetrics;
private ReadOnlyMemory _characters;
private IReadOnlyList _glyphInfos;
+ private Point? _baselineOrigin;
private bool _hasOneCharPerCluster; // if true, character index and cluster are similar
+ static GlyphRun()
+ {
+ s_renderInterface = AvaloniaLocator.Current.GetRequiredService();
+ }
+
///
/// Initializes a new instance of the class by specifying properties of the class.
///
@@ -28,15 +36,17 @@ namespace Avalonia.Media
/// The rendering em size.
/// The characters.
/// The glyph indices.
+ /// The baseline origin of the run.
/// The bidi level.
public GlyphRun(
IGlyphTypeface glyphTypeface,
double fontRenderingEmSize,
ReadOnlyMemory characters,
IReadOnlyList glyphIndices,
+ Point? baselineOrigin = null,
int biDiLevel = 0)
: this(glyphTypeface, fontRenderingEmSize, characters,
- CreateGlyphInfos(glyphIndices, fontRenderingEmSize, glyphTypeface), biDiLevel)
+ CreateGlyphInfos(glyphIndices, fontRenderingEmSize, glyphTypeface), baselineOrigin, biDiLevel)
{
_hasOneCharPerCluster = true;
}
@@ -48,12 +58,14 @@ namespace Avalonia.Media
/// The rendering em size.
/// The characters.
/// The list of glyphs used.
+ /// The baseline origin of the run.
/// The bidi level.
public GlyphRun(
IGlyphTypeface glyphTypeface,
double fontRenderingEmSize,
ReadOnlyMemory characters,
IReadOnlyList glyphInfos,
+ Point? baselineOrigin = null,
int biDiLevel = 0)
{
GlyphTypeface = glyphTypeface;
@@ -64,6 +76,8 @@ namespace Avalonia.Media
_glyphInfos = glyphInfos;
+ _baselineOrigin = baselineOrigin;
+
_biDiLevel = biDiLevel;
}
@@ -72,6 +86,7 @@ namespace Avalonia.Media
_glyphInfos = Array.Empty();
GlyphTypeface = Typeface.Default.GlyphTypeface;
_platformImpl = platformImpl;
+ _baselineOrigin = platformImpl.Item.BaselineOrigin;
}
private static IReadOnlyList CreateGlyphInfos(IReadOnlyList glyphIndices,
@@ -147,9 +162,13 @@ namespace Avalonia.Media
=> _glyphRunMetrics ??= CreateGlyphRunMetrics();
///
- /// Gets the baseline origin of the.
+ /// Gets or sets the baseline origin of the.
///
- public Point BaselineOrigin => PlatformImpl.Item.BaselineOrigin;
+ public Point BaselineOrigin
+ {
+ get => _baselineOrigin ?? default;
+ set => Set(ref _baselineOrigin, value);
+ }
///
/// Gets or sets the list of UTF16 code points that represent the Unicode content of the .
@@ -204,9 +223,7 @@ namespace Avalonia.Media
/// The geometry returned contains the combined geometry of all glyphs in the glyph run.
public Geometry BuildGeometry()
{
- var platformRenderInterface = AvaloniaLocator.Current.GetRequiredService();
-
- var geometryImpl = platformRenderInterface.BuildGlyphRunGeometry(this);
+ var geometryImpl = s_renderInterface.BuildGlyphRunGeometry(this);
return new PlatformGeometry(geometryImpl);
}
@@ -802,9 +819,11 @@ namespace Avalonia.Media
private IRef CreateGlyphRunImpl()
{
- var platformRenderInterface = AvaloniaLocator.Current.GetRequiredService();
-
- var platformImpl = platformRenderInterface.CreateGlyphRun(GlyphTypeface, FontRenderingEmSize, GlyphInfos);
+ var platformImpl = s_renderInterface.CreateGlyphRun(
+ GlyphTypeface,
+ FontRenderingEmSize,
+ GlyphInfos,
+ _baselineOrigin ?? new Point(0, -GlyphTypeface.Metrics.Ascent * Scale));
_platformImpl = RefCountable.Create(platformImpl);
diff --git a/src/Avalonia.Base/Media/TextFormatting/FormattingObjectPool.cs b/src/Avalonia.Base/Media/TextFormatting/FormattingObjectPool.cs
index cb8168e693..c7cd58eb6d 100644
--- a/src/Avalonia.Base/Media/TextFormatting/FormattingObjectPool.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/FormattingObjectPool.cs
@@ -93,16 +93,19 @@ namespace Avalonia.Media.TextFormatting
[Conditional("DEBUG")]
public void VerifyAllReturned()
{
- if (_pendingReturnCount > 0)
+ var pendingReturnCount = _pendingReturnCount;
+ _pendingReturnCount = 0;
+
+ if (pendingReturnCount > 0)
{
throw new InvalidOperationException(
- $"{_pendingReturnCount} RentedList<{typeof(T).Name} haven't been returned to the pool!");
+ $"{pendingReturnCount} RentedList<{typeof(T).Name}> haven't been returned to the pool!");
}
- if (_pendingReturnCount < 0)
+ if (pendingReturnCount < 0)
{
throw new InvalidOperationException(
- $"{-_pendingReturnCount} RentedList<{typeof(T).Name} extra lists have been returned to the pool!");
+ $"{-pendingReturnCount} RentedList<{typeof(T).Name}> extra lists have been returned to the pool!");
}
}
}
diff --git a/src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs b/src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs
index ac196bf7e0..7f23ac98b4 100644
--- a/src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs
@@ -185,7 +185,7 @@ namespace Avalonia.Media.TextFormatting
ShapedBuffer.FontRenderingEmSize,
Text,
ShapedBuffer,
- BidiLevel);
+ biDiLevel: BidiLevel);
}
public void Dispose()
diff --git a/src/Avalonia.Base/Media/TextFormatting/TextEllipsisHelper.cs b/src/Avalonia.Base/Media/TextFormatting/TextEllipsisHelper.cs
index 47973e37b5..4c93a1d851 100644
--- a/src/Avalonia.Base/Media/TextFormatting/TextEllipsisHelper.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/TextEllipsisHelper.cs
@@ -113,14 +113,18 @@ namespace Avalonia.Media.TextFormatting
var (preSplitRuns, postSplitRuns) = TextFormatterImpl.SplitTextRuns(textRuns, collapsedLength, objectPool);
- var collapsedRuns = new TextRun[preSplitRuns.Count + 1];
- preSplitRuns.CopyTo(collapsedRuns);
- collapsedRuns[collapsedRuns.Length - 1] = shapedSymbol;
-
- objectPool.TextRunLists.Return(ref preSplitRuns);
- objectPool.TextRunLists.Return(ref postSplitRuns);
-
- return collapsedRuns;
+ try
+ {
+ var collapsedRuns = new TextRun[preSplitRuns.Count + 1];
+ preSplitRuns.CopyTo(collapsedRuns);
+ collapsedRuns[collapsedRuns.Length - 1] = shapedSymbol;
+ return collapsedRuns;
+ }
+ finally
+ {
+ objectPool.TextRunLists.Return(ref preSplitRuns);
+ objectPool.TextRunLists.Return(ref postSplitRuns);
+ }
}
}
}
diff --git a/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs
index 7de842ab39..7505b9ccdd 100644
--- a/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs
@@ -32,58 +32,64 @@ namespace Avalonia.Media.TextFormatting
var fetchedRuns = FetchTextRuns(textSource, firstTextSourceIndex, objectPool,
out var textEndOfLine, out var textSourceLength);
- RentedList? shapedTextRuns;
+ RentedList? shapedTextRuns = null;
- if (previousLineBreak?.RemainingRuns is { } remainingRuns)
+ try
{
- resolvedFlowDirection = previousLineBreak.FlowDirection;
- textRuns = remainingRuns;
- nextLineBreak = previousLineBreak;
- shapedTextRuns = null;
- }
- else
- {
- shapedTextRuns = ShapeTextRuns(fetchedRuns, paragraphProperties, objectPool, fontManager, out resolvedFlowDirection);
- textRuns = shapedTextRuns;
-
- if (nextLineBreak == null && textEndOfLine != null)
+ if (previousLineBreak?.RemainingRuns is { } remainingRuns)
{
- nextLineBreak = new TextLineBreak(textEndOfLine, resolvedFlowDirection);
+ resolvedFlowDirection = previousLineBreak.FlowDirection;
+ textRuns = remainingRuns;
+ nextLineBreak = previousLineBreak;
+ shapedTextRuns = null;
}
- }
+ else
+ {
+ shapedTextRuns = ShapeTextRuns(fetchedRuns, paragraphProperties, objectPool, fontManager,
+ out resolvedFlowDirection);
+ textRuns = shapedTextRuns;
- TextLineImpl textLine;
+ if (nextLineBreak == null && textEndOfLine != null)
+ {
+ nextLineBreak = new TextLineBreak(textEndOfLine, resolvedFlowDirection);
+ }
+ }
- switch (textWrapping)
- {
- case TextWrapping.NoWrap:
+ TextLineImpl textLine;
+
+ switch (textWrapping)
{
- // perf note: if textRuns comes from remainingRuns above, it's very likely coming from this class
- // which already uses an array: ToArray() won't ever be called in this case
- var textRunArray = textRuns as TextRun[] ?? textRuns.ToArray();
+ case TextWrapping.NoWrap:
+ {
+ // perf note: if textRuns comes from remainingRuns above, it's very likely coming from this class
+ // which already uses an array: ToArray() won't ever be called in this case
+ var textRunArray = textRuns as TextRun[] ?? textRuns.ToArray();
- textLine = new TextLineImpl(textRunArray, firstTextSourceIndex, textSourceLength,
- paragraphWidth, paragraphProperties, resolvedFlowDirection, nextLineBreak);
+ textLine = new TextLineImpl(textRunArray, firstTextSourceIndex, textSourceLength,
+ paragraphWidth, paragraphProperties, resolvedFlowDirection, nextLineBreak);
- textLine.FinalizeLine();
+ textLine.FinalizeLine();
- break;
- }
- case TextWrapping.WrapWithOverflow:
- case TextWrapping.Wrap:
- {
- textLine = PerformTextWrapping(textRuns, firstTextSourceIndex, paragraphWidth,
- paragraphProperties, resolvedFlowDirection, nextLineBreak, objectPool, fontManager);
- break;
+ break;
+ }
+ case TextWrapping.WrapWithOverflow:
+ case TextWrapping.Wrap:
+ {
+ textLine = PerformTextWrapping(textRuns, firstTextSourceIndex, paragraphWidth,
+ paragraphProperties, resolvedFlowDirection, nextLineBreak, objectPool, fontManager);
+ break;
+ }
+ default:
+ throw new ArgumentOutOfRangeException(nameof(textWrapping));
}
- default:
- throw new ArgumentOutOfRangeException(nameof(textWrapping));
- }
-
- objectPool.TextRunLists.Return(ref shapedTextRuns);
- objectPool.TextRunLists.Return(ref fetchedRuns);
- return textLine;
+ return textLine;
+ }
+ finally
+ {
+ objectPool.TextRunLists.Return(ref shapedTextRuns);
+ objectPool.TextRunLists.Return(ref fetchedRuns);
+ }
}
///
@@ -224,23 +230,26 @@ namespace Avalonia.Media.TextFormatting
(resolvedEmbeddingLevel & 1) == 0 ? FlowDirection.LeftToRight : FlowDirection.RightToLeft;
var processedRuns = objectPool.TextRunLists.Rent();
+ var groupedRuns = objectPool.UnshapedTextRunLists.Rent();
- CoalesceLevels(textRuns, bidiAlgorithm.ResolvedLevels.Span, fontManager, processedRuns);
+ try
+ {
+ CoalesceLevels(textRuns, bidiAlgorithm.ResolvedLevels.Span, fontManager, processedRuns);
- bidiData.Reset();
- bidiAlgorithm.Reset();
+ bidiData.Reset();
+ bidiAlgorithm.Reset();
- var groupedRuns = objectPool.UnshapedTextRunLists.Rent();
- var textShaper = TextShaper.Current;
- for (var index = 0; index < processedRuns.Count; index++)
- {
- var currentRun = processedRuns[index];
+ var textShaper = TextShaper.Current;
- switch (currentRun)
+ for (var index = 0; index < processedRuns.Count; index++)
{
- case UnshapedTextRun shapeableRun:
+ var currentRun = processedRuns[index];
+
+ switch (currentRun)
{
+ case UnshapedTextRun shapeableRun:
+ {
groupedRuns.Clear();
groupedRuns.Add(shapeableRun);
@@ -277,17 +286,20 @@ namespace Avalonia.Media.TextFormatting
break;
}
- default:
+ default:
{
shapedRuns.Add(currentRun);
break;
}
+ }
}
}
-
- objectPool.TextRunLists.Return(ref processedRuns);
- objectPool.UnshapedTextRunLists.Return(ref groupedRuns);
+ finally
+ {
+ objectPool.TextRunLists.Return(ref processedRuns);
+ objectPool.UnshapedTextRunLists.Return(ref groupedRuns);
+ }
return shapedRuns;
}
@@ -805,25 +817,29 @@ namespace Avalonia.Media.TextFormatting
var (preSplitRuns, postSplitRuns) = SplitTextRuns(textRuns, measuredLength, objectPool);
- var textLineBreak = postSplitRuns?.Count > 0 ?
- new TextLineBreak(null, resolvedFlowDirection, postSplitRuns.ToArray()) :
- null;
-
- if (textLineBreak is null && currentLineBreak?.TextEndOfLine != null)
+ try
{
- textLineBreak = new TextLineBreak(currentLineBreak.TextEndOfLine, resolvedFlowDirection);
- }
+ var textLineBreak = postSplitRuns?.Count > 0 ?
+ new TextLineBreak(null, resolvedFlowDirection, postSplitRuns.ToArray()) :
+ null;
- var textLine = new TextLineImpl(preSplitRuns.ToArray(), firstTextSourceIndex, measuredLength,
- paragraphWidth, paragraphProperties, resolvedFlowDirection,
- textLineBreak);
-
- textLine.FinalizeLine();
+ if (textLineBreak is null && currentLineBreak?.TextEndOfLine != null)
+ {
+ textLineBreak = new TextLineBreak(currentLineBreak.TextEndOfLine, resolvedFlowDirection);
+ }
- objectPool.TextRunLists.Return(ref preSplitRuns);
- objectPool.TextRunLists.Return(ref postSplitRuns);
+ var textLine = new TextLineImpl(preSplitRuns.ToArray(), firstTextSourceIndex, measuredLength,
+ paragraphWidth, paragraphProperties, resolvedFlowDirection,
+ textLineBreak);
- return textLine;
+ textLine.FinalizeLine();
+ return textLine;
+ }
+ finally
+ {
+ objectPool.TextRunLists.Return(ref preSplitRuns);
+ objectPool.TextRunLists.Return(ref postSplitRuns);
+ }
}
private struct TextRunEnumerator
diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs b/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs
index bb58e0d692..4923cdbe32 100644
--- a/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs
@@ -441,128 +441,133 @@ namespace Avalonia.Media.TextFormatting
var textLines = objectPool.TextLines.Rent();
- double left = double.PositiveInfinity, width = 0.0, height = 0.0;
-
- _textSourceLength = 0;
+ try
+ {
+ double left = double.PositiveInfinity, width = 0.0, height = 0.0;
- TextLine? previousLine = null;
+ _textSourceLength = 0;
- var textFormatter = TextFormatter.Current;
+ TextLine? previousLine = null;
- while (true)
- {
- var textLine = textFormatter.FormatLine(_textSource, _textSourceLength, MaxWidth, _paragraphProperties,
- previousLine?.TextLineBreak);
+ var textFormatter = TextFormatter.Current;
- if (textLine.Length == 0)
+ while (true)
{
- if (previousLine != null && previousLine.NewLineLength > 0)
+ var textLine = textFormatter.FormatLine(_textSource, _textSourceLength, MaxWidth,
+ _paragraphProperties, previousLine?.TextLineBreak);
+
+ if (textLine.Length == 0)
{
- var emptyTextLine = TextFormatterImpl.CreateEmptyTextLine(_textSourceLength, MaxWidth,
- _paragraphProperties, fontManager);
+ if (previousLine != null && previousLine.NewLineLength > 0)
+ {
+ var emptyTextLine = TextFormatterImpl.CreateEmptyTextLine(_textSourceLength, MaxWidth,
+ _paragraphProperties, fontManager);
- textLines.Add(emptyTextLine);
+ textLines.Add(emptyTextLine);
- UpdateBounds(emptyTextLine, ref left, ref width, ref height);
- }
+ UpdateBounds(emptyTextLine, ref left, ref width, ref height);
+ }
- break;
- }
+ break;
+ }
- _textSourceLength += textLine.Length;
+ _textSourceLength += textLine.Length;
- //Fulfill max height constraint
- if (textLines.Count > 0 && !double.IsPositiveInfinity(MaxHeight) && height + textLine.Height > MaxHeight)
- {
- if (previousLine?.TextLineBreak != null && _textTrimming != TextTrimming.None)
+ //Fulfill max height constraint
+ if (textLines.Count > 0 && !double.IsPositiveInfinity(MaxHeight)
+ && height + textLine.Height > MaxHeight)
{
- var collapsedLine =
- previousLine.Collapse(GetCollapsingProperties(MaxWidth));
+ if (previousLine?.TextLineBreak != null && _textTrimming != TextTrimming.None)
+ {
+ var collapsedLine =
+ previousLine.Collapse(GetCollapsingProperties(MaxWidth));
- textLines[textLines.Count - 1] = collapsedLine;
- }
+ textLines[textLines.Count - 1] = collapsedLine;
+ }
- break;
- }
+ break;
+ }
- var hasOverflowed = textLine.HasOverflowed;
+ var hasOverflowed = textLine.HasOverflowed;
- if (hasOverflowed && _textTrimming != TextTrimming.None)
- {
- textLine = textLine.Collapse(GetCollapsingProperties(MaxWidth));
- }
+ if (hasOverflowed && _textTrimming != TextTrimming.None)
+ {
+ textLine = textLine.Collapse(GetCollapsingProperties(MaxWidth));
+ }
- textLines.Add(textLine);
+ textLines.Add(textLine);
- UpdateBounds(textLine, ref left, ref width, ref height);
+ UpdateBounds(textLine, ref left, ref width, ref height);
- previousLine = textLine;
+ previousLine = textLine;
- //Fulfill max lines constraint
- if (MaxLines > 0 && textLines.Count >= MaxLines)
- {
- if(textLine.TextLineBreak?.RemainingRuns is not null)
+ //Fulfill max lines constraint
+ if (MaxLines > 0 && textLines.Count >= MaxLines)
{
- textLines[textLines.Count - 1] = textLine.Collapse(GetCollapsingProperties(width));
+ if (textLine.TextLineBreak?.RemainingRuns is not null)
+ {
+ textLines[textLines.Count - 1] = textLine.Collapse(GetCollapsingProperties(width));
+ }
+
+ break;
}
- break;
+ if (textLine.TextLineBreak?.TextEndOfLine is TextEndOfParagraph)
+ {
+ break;
+ }
}
- if (textLine.TextLineBreak?.TextEndOfLine is TextEndOfParagraph)
+ //Make sure the TextLayout always contains at least on empty line
+ if (textLines.Count == 0)
{
- break;
- }
- }
+ var textLine =
+ TextFormatterImpl.CreateEmptyTextLine(0, MaxWidth, _paragraphProperties, fontManager);
- //Make sure the TextLayout always contains at least on empty line
- if (textLines.Count == 0)
- {
- var textLine = TextFormatterImpl.CreateEmptyTextLine(0, MaxWidth, _paragraphProperties, fontManager);
-
- textLines.Add(textLine);
-
- UpdateBounds(textLine, ref left, ref width, ref height);
- }
+ textLines.Add(textLine);
- Bounds = new Rect(left, 0, width, height);
+ UpdateBounds(textLine, ref left, ref width, ref height);
+ }
- if (_paragraphProperties.TextAlignment == TextAlignment.Justify)
- {
- var whitespaceWidth = 0d;
+ Bounds = new Rect(left, 0, width, height);
- for (var i = 0; i < textLines.Count; i++)
+ if (_paragraphProperties.TextAlignment == TextAlignment.Justify)
{
- var line = textLines[i];
- var lineWhitespaceWidth = line.Width - line.WidthIncludingTrailingWhitespace;
+ var whitespaceWidth = 0d;
- if (lineWhitespaceWidth > whitespaceWidth)
+ for (var i = 0; i < textLines.Count; i++)
{
- whitespaceWidth = lineWhitespaceWidth;
- }
- }
+ var line = textLines[i];
+ var lineWhitespaceWidth = line.Width - line.WidthIncludingTrailingWhitespace;
- var justificationWidth = width - whitespaceWidth;
+ if (lineWhitespaceWidth > whitespaceWidth)
+ {
+ whitespaceWidth = lineWhitespaceWidth;
+ }
+ }
- if (justificationWidth > 0)
- {
- var justificationProperties = new InterWordJustification(justificationWidth);
+ var justificationWidth = width - whitespaceWidth;
- for (var i = 0; i < textLines.Count - 1; i++)
+ if (justificationWidth > 0)
{
- var line = textLines[i];
+ var justificationProperties = new InterWordJustification(justificationWidth);
- line.Justify(justificationProperties);
+ for (var i = 0; i < textLines.Count - 1; i++)
+ {
+ var line = textLines[i];
+
+ line.Justify(justificationProperties);
+ }
}
}
- }
- var result = textLines.ToArray();
-
- objectPool.TextLines.Return(ref textLines);
- objectPool.VerifyAllReturned();
-
- return result;
+ return textLines.ToArray();
+ }
+ finally
+ {
+ objectPool.TextLines.Return(ref textLines);
+ objectPool.VerifyAllReturned();
+ }
}
///
diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs b/src/Avalonia.Base/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs
index 0d777ad043..2e85b1e187 100644
--- a/src/Avalonia.Base/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs
+++ b/src/Avalonia.Base/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs
@@ -86,7 +86,6 @@ namespace Avalonia.Media.TextFormatting
RentedList? rentedPreSplitRuns = null;
RentedList? rentedPostSplitRuns = null;
- TextRun[]? results;
try
{
@@ -113,9 +112,7 @@ namespace Avalonia.Media.TextFormatting
if (measuredLength <= _prefixLength || effectivePostSplitRuns is null)
{
- results = collapsedRuns.ToArray();
- objectPool.TextRunLists.Return(ref collapsedRuns);
- return results;
+ return collapsedRuns.ToArray();
}
var availableSuffixWidth = availableWidth;
@@ -157,16 +154,15 @@ namespace Avalonia.Media.TextFormatting
}
}
}
+
+ return collapsedRuns.ToArray();
}
finally
{
objectPool.TextRunLists.Return(ref rentedPreSplitRuns);
objectPool.TextRunLists.Return(ref rentedPostSplitRuns);
+ objectPool.TextRunLists.Return(ref collapsedRuns);
}
-
- results = collapsedRuns.ToArray();
- objectPool.TextRunLists.Return(ref collapsedRuns);
- return results;
}
return new TextRun[] { shapedSymbol };
diff --git a/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs b/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs
index e2160f21d2..41e792d58e 100644
--- a/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs
+++ b/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs
@@ -168,8 +168,9 @@ namespace Avalonia.Platform
/// The glyph typeface.
/// The font rendering em size.
/// The list of glyphs.
+ /// The baseline origin of the run. Can be null.
/// An .
- IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, IReadOnlyList glyphInfos);
+ IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, IReadOnlyList glyphInfos, Point baselineOrigin);
///
/// Creates a backend-specific object using a low-level API graphics context
diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs
index fc88055e01..3bd566c622 100644
--- a/src/Avalonia.Controls/TopLevel.cs
+++ b/src/Avalonia.Controls/TopLevel.cs
@@ -581,12 +581,21 @@ namespace Avalonia.Controls
/// The event args.
private void HandleInput(RawInputEventArgs e)
{
- if (e is RawPointerEventArgs pointerArgs)
+ if (PlatformImpl != null)
{
- pointerArgs.InputHitTestResult = this.InputHitTest(pointerArgs.Position);
- }
+ if (e is RawPointerEventArgs pointerArgs)
+ {
+ pointerArgs.InputHitTestResult = this.InputHitTest(pointerArgs.Position);
+ }
- _inputManager?.ProcessInput(e);
+ _inputManager?.ProcessInput(e);
+ }
+ else
+ {
+ Logger.TryGet(LogEventLevel.Warning, LogArea.Control)?.Log(
+ this,
+ "PlatformImpl is null, couldn't handle input.");
+ }
}
private void SceneInvalidated(object? sender, SceneInvalidatedEventArgs e)
diff --git a/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs b/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs
index 572ff1c876..514d3b3e07 100644
--- a/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs
+++ b/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs
@@ -120,7 +120,11 @@ namespace Avalonia.Headless
return new HeadlessGeometryStub(new Rect(glyphRun.Size));
}
- public IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, IReadOnlyList glyphInfos)
+ public IGlyphRunImpl CreateGlyphRun(
+ IGlyphTypeface glyphTypeface,
+ double fontRenderingEmSize,
+ IReadOnlyList glyphInfos,
+ Point baselineOrigin)
{
return new HeadlessGlyphRunStub();
}
diff --git a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs
index 6630f0707e..b4297a7c33 100644
--- a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs
+++ b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs
@@ -201,7 +201,11 @@ namespace Avalonia.Skia
return new WriteableBitmapImpl(size, dpi, format, alphaFormat);
}
- public IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, IReadOnlyList glyphInfos)
+ public IGlyphRunImpl CreateGlyphRun(
+ IGlyphTypeface glyphTypeface,
+ double fontRenderingEmSize,
+ IReadOnlyList glyphInfos,
+ Point baselineOrigin)
{
if (glyphTypeface == null)
{
@@ -252,7 +256,6 @@ namespace Avalonia.Skia
var scale = fontRenderingEmSize / glyphTypeface.Metrics.DesignEmHeight;
var height = glyphTypeface.Metrics.LineSpacing * scale;
- var baselineOrigin = new Point(0, -glyphTypeface.Metrics.Ascent * scale);
return new GlyphRunImpl(builder.Build(), new Size(width, height), baselineOrigin);
}
diff --git a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
index 461950b728..fbf8097ece 100644
--- a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
+++ b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
@@ -158,7 +158,8 @@ namespace Avalonia.Direct2D1
public IGeometryImpl CreateGeometryGroup(FillRule fillRule, IReadOnlyList children) => new GeometryGroupImpl(fillRule, children);
public IGeometryImpl CreateCombinedGeometry(GeometryCombineMode combineMode, Geometry g1, Geometry g2) => new CombinedGeometryImpl(combineMode, g1, g2);
- public IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, IReadOnlyList glyphInfos)
+ public IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize,
+ IReadOnlyList glyphInfos, Point baselineOrigin)
{
var glyphTypefaceImpl = (GlyphTypefaceImpl)glyphTypeface;
@@ -207,7 +208,6 @@ namespace Avalonia.Direct2D1
var scale = fontRenderingEmSize / glyphTypeface.Metrics.DesignEmHeight;
var height = glyphTypeface.Metrics.LineSpacing * scale;
- var baselineOrigin = new Point(0, -glyphTypeface.Metrics.Ascent * scale);
return new GlyphRunImpl(run, new Size(width, height), baselineOrigin);
}
@@ -257,7 +257,7 @@ namespace Avalonia.Direct2D1
sink.Close();
}
- var (baselineOriginX, baselineOriginY) = glyphRun.BaselineOrigin;
+ var (baselineOriginX, baselineOriginY) = glyphRun.PlatformImpl.Item.BaselineOrigin;
var transformedGeometry = new SharpDX.Direct2D1.TransformedGeometry(
Direct2D1Factory,
diff --git a/tests/Avalonia.Base.UnitTests/Media/GlyphRunTests.cs b/tests/Avalonia.Base.UnitTests/Media/GlyphRunTests.cs
index a05bfbea4c..43feb75c08 100644
--- a/tests/Avalonia.Base.UnitTests/Media/GlyphRunTests.cs
+++ b/tests/Avalonia.Base.UnitTests/Media/GlyphRunTests.cs
@@ -188,7 +188,7 @@ namespace Avalonia.Base.UnitTests.Media
glyphInfos[i] = new GlyphInfo(0, glyphClusters[i], glyphAdvances[i]);
}
- return new GlyphRun(new MockGlyphTypeface(), 10, new string('a', count).AsMemory(), glyphInfos, bidiLevel);
+ return new GlyphRun(new MockGlyphTypeface(), 10, new string('a', count).AsMemory(), glyphInfos, biDiLevel: bidiLevel);
}
}
}
diff --git a/tests/Avalonia.Base.UnitTests/VisualTree/MockRenderInterface.cs b/tests/Avalonia.Base.UnitTests/VisualTree/MockRenderInterface.cs
index ee501a86c1..76c7fe97fc 100644
--- a/tests/Avalonia.Base.UnitTests/VisualTree/MockRenderInterface.cs
+++ b/tests/Avalonia.Base.UnitTests/VisualTree/MockRenderInterface.cs
@@ -77,7 +77,8 @@ namespace Avalonia.Base.UnitTests.VisualTree
throw new NotImplementedException();
}
- public IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, IReadOnlyList glyphInfos)
+ public IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize,
+ IReadOnlyList glyphInfos, Point baselineOrigin)
{
throw new NotImplementedException();
}
diff --git a/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs b/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs
index 9b148c798b..37b79855db 100644
--- a/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs
+++ b/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs
@@ -123,7 +123,8 @@ namespace Avalonia.Benchmarks
return new MockStreamGeometryImpl();
}
- public IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, IReadOnlyList glyphInfos)
+ public IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize,
+ IReadOnlyList glyphInfos, Point baselineOrigin)
{
return new MockGlyphRun(glyphInfos);
}
diff --git a/tests/Avalonia.Skia.UnitTests/Media/GlyphRunTests.cs b/tests/Avalonia.Skia.UnitTests/Media/GlyphRunTests.cs
index 59c7ac3786..bfe03030c6 100644
--- a/tests/Avalonia.Skia.UnitTests/Media/GlyphRunTests.cs
+++ b/tests/Avalonia.Skia.UnitTests/Media/GlyphRunTests.cs
@@ -217,7 +217,7 @@ namespace Avalonia.Skia.UnitTests.Media
shapedBuffer.FontRenderingEmSize,
shapedBuffer.Text,
shapedBuffer.GlyphInfos,
- shapedBuffer.BidiLevel);
+ biDiLevel: shapedBuffer.BidiLevel);
if(shapedBuffer.BidiLevel == 1)
{
diff --git a/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs b/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs
index 30f949ccb8..93073faefb 100644
--- a/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs
+++ b/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs
@@ -149,7 +149,8 @@ namespace Avalonia.UnitTests
throw new NotImplementedException();
}
- public IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, IReadOnlyList glyphInfos)
+ public IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize,
+ IReadOnlyList glyphInfos, Point baselineOrigin)
{
return new MockGlyphRun(glyphInfos);
}