From e7e57d0e9136f0804f2ba57dd531f285026b169b Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 4 Mar 2019 13:30:20 +0100 Subject: [PATCH] Fix text rendering where TextAlignment != Left. The renderer was using the wrong bounds for text where `TextAlignment != Left` as seen in #1625. To fix this, change `FormattedTextImpl.Size` to `Bounds`, which returns a rect that takes into account the text alignment and similarly change `FormattedText.Measure` to return a `Rect` instead of a `Size. Fixes #1625. --- .../Presenters/TextPresenter.cs | 2 +- src/Avalonia.Controls/TextBlock.cs | 2 +- src/Avalonia.Visuals/Media/FormattedText.cs | 9 +++---- .../Platform/IFormattedTextImpl.cs | 5 ++-- .../Rendering/SceneGraph/TextNode.cs | 2 +- src/Skia/Avalonia.Skia/DrawingContextImpl.cs | 2 +- src/Skia/Avalonia.Skia/FormattedTextImpl.cs | 26 ++++++++++++++----- .../Media/DrawingContextImpl.cs | 2 +- .../Media/FormattedTextImpl.cs | 12 ++++++--- .../FullLayoutTests.cs | 2 +- .../Media/FormattedTextImplTests.cs | 4 +-- 11 files changed, 42 insertions(+), 26 deletions(-) diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index f73a335de5..00b2ed2149 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -275,7 +275,7 @@ namespace Avalonia.Controls.Presenters Typeface = new Typeface(FontFamily, FontSize, FontStyle, FontWeight), TextAlignment = TextAlignment, Constraint = availableSize, - }.Measure(); + }.Measure().Size; } } diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs index af7b0f835e..2ccab016f2 100644 --- a/src/Avalonia.Controls/TextBlock.cs +++ b/src/Avalonia.Controls/TextBlock.cs @@ -396,7 +396,7 @@ namespace Avalonia.Controls FormattedText.Constraint = Size.Infinity; } - return FormattedText.Measure(); + return FormattedText.Measure().Size; } return new Size(); diff --git a/src/Avalonia.Visuals/Media/FormattedText.cs b/src/Avalonia.Visuals/Media/FormattedText.cs index 0d456eb3b7..7abf8465a7 100644 --- a/src/Avalonia.Visuals/Media/FormattedText.cs +++ b/src/Avalonia.Visuals/Media/FormattedText.cs @@ -159,13 +159,10 @@ namespace Avalonia.Media } /// - /// Gets the size of the text, taking into account. + /// Gets the bounds of the text, taking into account. /// - /// The bounds box of the text. - public Size Measure() - { - return PlatformImpl.Size; - } + /// The bounds of the text. + public Rect Measure() => PlatformImpl.Bounds; private void Set(ref T field, T value) { diff --git a/src/Avalonia.Visuals/Platform/IFormattedTextImpl.cs b/src/Avalonia.Visuals/Platform/IFormattedTextImpl.cs index 2a4e9cde4f..2543e4f363 100644 --- a/src/Avalonia.Visuals/Platform/IFormattedTextImpl.cs +++ b/src/Avalonia.Visuals/Platform/IFormattedTextImpl.cs @@ -1,6 +1,7 @@ // Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. +using System; using System.Collections.Generic; using Avalonia.Media; @@ -17,9 +18,9 @@ namespace Avalonia.Platform Size Constraint { get; } /// - /// The measured size of the text. + /// The measured bounds of the text. /// - Size Size { get; } + Rect Bounds{ get; } /// /// Gets the text. diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/TextNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/TextNode.cs index c06d0d26b4..dec5c382c9 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/TextNode.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/TextNode.cs @@ -27,7 +27,7 @@ namespace Avalonia.Rendering.SceneGraph Point origin, IFormattedTextImpl text, IDictionary childScenes = null) - : base(new Rect(origin, text.Size), transform, null) + : base(text.Bounds, transform, null) { Transform = transform; Foreground = foreground?.ToImmutable(); diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs index 353354da5e..993d1eb256 100644 --- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs +++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs @@ -218,7 +218,7 @@ namespace Avalonia.Skia /// public void DrawText(IBrush foreground, Point origin, IFormattedTextImpl text) { - using (var paint = CreatePaint(foreground, text.Size)) + using (var paint = CreatePaint(foreground, text.Bounds.Size)) { var textImpl = (FormattedTextImpl) text; textImpl.Draw(this, Canvas, origin.ToSKPoint(), paint, _canTextUseLcdRendering); diff --git a/src/Skia/Avalonia.Skia/FormattedTextImpl.cs b/src/Skia/Avalonia.Skia/FormattedTextImpl.cs index 7e3f4aa40a..c83d5f26fb 100644 --- a/src/Skia/Avalonia.Skia/FormattedTextImpl.cs +++ b/src/Skia/Avalonia.Skia/FormattedTextImpl.cs @@ -89,7 +89,7 @@ namespace Avalonia.Skia public Size Constraint => _constraint; - public Size Size => _size; + public Rect Bounds => _bounds; public IEnumerable GetLines() { @@ -135,7 +135,7 @@ namespace Avalonia.Skia }; } - bool end = point.X > _size.Width || point.Y > _lines.Sum(l => l.Height); + bool end = point.X > _bounds.Width || point.Y > _lines.Sum(l => l.Height); return new TextHitTestResult() { @@ -323,7 +323,7 @@ namespace Avalonia.Skia private Size _constraint = new Size(double.PositiveInfinity, double.PositiveInfinity); private float _lineHeight = 0; private float _lineOffset = 0; - private Size _size; + private Rect _bounds; private List _skiaLines; private static void ApplyWrapperTo(ref SKPaint current, DrawingContextImpl.PaintWrapper wrapper, @@ -639,12 +639,26 @@ namespace Avalonia.Skia if (_skiaLines.Count == 0) { _lines.Add(new FormattedTextLine(0, _lineHeight)); - _size = new Size(0, _lineHeight); + _bounds = new Rect(0, 0, 0, _lineHeight); } else { var lastLine = _skiaLines[_skiaLines.Count - 1]; - _size = new Size(maxX, lastLine.Top + lastLine.Height); + _bounds = new Rect(0, 0, maxX, lastLine.Top + lastLine.Height); + + switch (_paint.TextAlign) + { + case SKTextAlign.Center: + _bounds = new Rect(Constraint).CenterRect(_bounds); + break; + case SKTextAlign.Right: + _bounds = new Rect( + Constraint.Width - _bounds.Width, + 0, + _bounds.Width, + _bounds.Height); + break; + } } } @@ -660,7 +674,7 @@ namespace Avalonia.Skia { double width = Constraint.Width > 0 && !double.IsPositiveInfinity(Constraint.Width) ? Constraint.Width : - _size.Width; + _bounds.Width; switch (align) { diff --git a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs index 5e150ff647..6123088d7e 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs @@ -274,7 +274,7 @@ namespace Avalonia.Direct2D1.Media { var impl = (FormattedTextImpl)text; - using (var brush = CreateBrush(foreground, impl.Size)) + using (var brush = CreateBrush(foreground, impl.Bounds.Size)) using (var renderer = new AvaloniaTextRenderer(this, _deviceContext, brush.PlatformBrush)) { if (brush.PlatformBrush != null) diff --git a/src/Windows/Avalonia.Direct2D1/Media/FormattedTextImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/FormattedTextImpl.cs index 7164ec7c0d..b3cc4c8e0d 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/FormattedTextImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/FormattedTextImpl.cs @@ -45,12 +45,12 @@ namespace Avalonia.Direct2D1.Media } } - Size = Measure(); + Bounds = Measure(); } public Size Constraint => new Size(TextLayout.MaxWidth, TextLayout.MaxHeight); - public Size Size { get; } + public Rect Bounds { get; } public string Text { get; } @@ -104,7 +104,7 @@ namespace Avalonia.Direct2D1.Media } } - private Size Measure() + private Rect Measure() { var metrics = TextLayout.Metrics; @@ -115,7 +115,11 @@ namespace Avalonia.Direct2D1.Media width = metrics.Width; } - return new Size(width, TextLayout.Metrics.Height); + return new Rect( + TextLayout.Metrics.Left, + TextLayout.Metrics.Top, + width, + TextLayout.Metrics.Height); } } } diff --git a/tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs b/tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs index ffdb146eec..6cf38b6121 100644 --- a/tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs +++ b/tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs @@ -146,7 +146,7 @@ namespace Avalonia.Layout.UnitTests public string Text { get; } - public Size Size => new Size(); + public Rect Bounds => Rect.Empty; public void Dispose() { diff --git a/tests/Avalonia.RenderTests/Media/FormattedTextImplTests.cs b/tests/Avalonia.RenderTests/Media/FormattedTextImplTests.cs index a8f9d42c1e..353123ab2a 100644 --- a/tests/Avalonia.RenderTests/Media/FormattedTextImplTests.cs +++ b/tests/Avalonia.RenderTests/Media/FormattedTextImplTests.cs @@ -100,7 +100,7 @@ namespace Avalonia.Direct2D1.RenderTests.Media public void Should_Measure_String_Correctly(string input, double fontSize, double expWidth, double expHeight) { var fmt = Create(input, fontSize); - var size = fmt.Size; + var size = fmt.Bounds.Size; Assert.Equal(expWidth, size.Width, 2); Assert.Equal(expHeight, size.Height, 2); @@ -265,4 +265,4 @@ namespace Avalonia.Direct2D1.RenderTests.Media } } } -} \ No newline at end of file +}