From 69f9e017f39e5e74432e05f108b6d77221868061 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Thu, 28 Apr 2022 20:52:23 +0200 Subject: [PATCH 1/6] Fix text selection Fix empty line RTL inital caret position --- src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs | 6 +++--- src/Avalonia.Base/Media/TextFormatting/TextLayout.cs | 6 +++--- src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs | 5 ----- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs index 7f0f204886..4205268bc6 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs @@ -528,7 +528,7 @@ namespace Avalonia.Media.TextFormatting /// Creates an empty text line. /// /// The empty text line. - public static TextLineImpl CreateEmptyTextLine(int firstTextSourceIndex, TextParagraphProperties paragraphProperties) + public static TextLineImpl CreateEmptyTextLine(int firstTextSourceIndex, double paragraphWidth, TextParagraphProperties paragraphProperties) { var flowDirection = paragraphProperties.FlowDirection; var properties = paragraphProperties.DefaultTextRunProperties; @@ -542,7 +542,7 @@ namespace Avalonia.Media.TextFormatting var textRuns = new List { new ShapedTextCharacters(shapedBuffer, properties) }; - return new TextLineImpl(textRuns, firstTextSourceIndex, 0, double.PositiveInfinity, paragraphProperties, flowDirection).FinalizeLine(); + return new TextLineImpl(textRuns, firstTextSourceIndex, 0, paragraphWidth, paragraphProperties, flowDirection).FinalizeLine(); } /// @@ -561,7 +561,7 @@ namespace Avalonia.Media.TextFormatting { if(textRuns.Count == 0) { - return CreateEmptyTextLine(firstTextSourceIndex, paragraphProperties); + return CreateEmptyTextLine(firstTextSourceIndex,paragraphWidth, paragraphProperties); } if (!TryMeasureLength(textRuns, paragraphWidth, out var measuredLength)) diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs b/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs index 0df608cb34..5d5d45db2d 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs @@ -410,7 +410,7 @@ namespace Avalonia.Media.TextFormatting { if (MathUtilities.IsZero(MaxWidth) || MathUtilities.IsZero(MaxHeight)) { - var textLine = TextFormatterImpl.CreateEmptyTextLine(0, _paragraphProperties); + var textLine = TextFormatterImpl.CreateEmptyTextLine(0, double.PositiveInfinity, _paragraphProperties); Bounds = new Rect(0,0,0, textLine.Height); @@ -434,7 +434,7 @@ namespace Avalonia.Media.TextFormatting { if(previousLine != null && previousLine.NewLineLength > 0) { - var emptyTextLine = TextFormatterImpl.CreateEmptyTextLine(_textSourceLength, _paragraphProperties); + var emptyTextLine = TextFormatterImpl.CreateEmptyTextLine(_textSourceLength, MaxWidth, _paragraphProperties); textLines.Add(emptyTextLine); @@ -483,7 +483,7 @@ namespace Avalonia.Media.TextFormatting //Make sure the TextLayout always contains at least on empty line if(textLines.Count == 0) { - var textLine = TextFormatterImpl.CreateEmptyTextLine(0, _paragraphProperties); + var textLine = TextFormatterImpl.CreateEmptyTextLine(0, MaxWidth, _paragraphProperties); textLines.Add(textLine); diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs index 6a704f6f3e..35ada3c7ee 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs @@ -418,11 +418,6 @@ namespace Avalonia.Media.TextFormatting continue; } - if(currentPosition + currentRun.TextSourceLength <= firstTextSourceCharacterIndex) - { - continue; - } - TextRun? nextRun = null; if (index + 1 < TextRuns.Count) From 8772a46bbaab12d3d77b76fd97eed4cffd11e365 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Fri, 29 Apr 2022 14:26:28 +0200 Subject: [PATCH 2/6] Fix scaled text measurements --- src/Avalonia.Controls/Presenters/TextPresenter.cs | 4 +++- src/Avalonia.Controls/TextBlock.cs | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index db1bbdbc6c..c3912077ee 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -532,7 +532,9 @@ namespace Avalonia.Controls.Presenters return finalSize; } - _constraint = new Size(finalSize.Width, Math.Ceiling(finalSize.Height)); + var textSize = PixelSize.FromSize(finalSize, 1); + + _constraint = new Size(textSize.Width, textSize.Height); _textLayout = null; diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs index c04a62008b..7427f21134 100644 --- a/src/Avalonia.Controls/TextBlock.cs +++ b/src/Avalonia.Controls/TextBlock.cs @@ -626,9 +626,9 @@ namespace Avalonia.Controls var padding = Padding; - var textSize = finalSize.Deflate(padding); + var textSize = PixelSize.FromSize(finalSize.Deflate(padding), 1); - _constraint = new Size(textSize.Width, Math.Ceiling(textSize.Height)); + _constraint = new Size(textSize.Width, textSize.Height); _textLayout = null; From 2425cbf7aa2504b96b6fe441ef9522de92bbc98b Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Fri, 29 Apr 2022 17:03:05 +0200 Subject: [PATCH 3/6] Use RenderScaling in the ArrangeOverride --- src/Avalonia.Controls/Presenters/TextPresenter.cs | 2 +- src/Avalonia.Controls/TextBlock.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index c3912077ee..eecf0d9101 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -532,7 +532,7 @@ namespace Avalonia.Controls.Presenters return finalSize; } - var textSize = PixelSize.FromSize(finalSize, 1); + var textSize = PixelSize.FromSize(finalSize, VisualRoot?.RenderScaling ?? 1); _constraint = new Size(textSize.Width, textSize.Height); diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs index 7427f21134..1087310841 100644 --- a/src/Avalonia.Controls/TextBlock.cs +++ b/src/Avalonia.Controls/TextBlock.cs @@ -626,7 +626,7 @@ namespace Avalonia.Controls var padding = Padding; - var textSize = PixelSize.FromSize(finalSize.Deflate(padding), 1); + var textSize = PixelSize.FromSize(finalSize.Deflate(padding), VisualRoot?.RenderScaling ?? 1); _constraint = new Size(textSize.Width, textSize.Height); From a056f0b654ae220ddbb58a2648fd286676769fef Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Mon, 2 May 2022 11:39:08 +0200 Subject: [PATCH 4/6] Second attempt to fix text measurements with scaling --- .../Presenters/TextPresenter.cs | 27 ++++++++++++------- src/Avalonia.Controls/TextBlock.cs | 19 +++++++------ 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index eecf0d9101..0785149a73 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -511,30 +511,39 @@ namespace Avalonia.Controls.Presenters InvalidateMeasure(); } - + + protected override Size MeasureOverride(Size availableSize) { + if (string.IsNullOrEmpty(Text)) + { + return new Size(); + } + _constraint = availableSize; - + _textLayout = null; - + InvalidateArrange(); - var measuredSize = PixelSize.FromSize(TextLayout.Bounds.Size, 1); - - return new Size(measuredSize.Width, measuredSize.Height); + var measuredSize = TextLayout.Bounds.Size; + + return measuredSize; } protected override Size ArrangeOverride(Size finalSize) { + if (finalSize.Width < TextLayout.Bounds.Width) + { + finalSize = finalSize.WithWidth(TextLayout.Bounds.Width); + } + if (MathUtilities.AreClose(_constraint.Width, finalSize.Width)) { return finalSize; } - var textSize = PixelSize.FromSize(finalSize, VisualRoot?.RenderScaling ?? 1); - - _constraint = new Size(textSize.Width, textSize.Height); + _constraint = new Size(finalSize.Width, double.PositiveInfinity); _textLayout = null; diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs index 1087310841..bbe6aeb7ee 100644 --- a/src/Avalonia.Controls/TextBlock.cs +++ b/src/Avalonia.Controls/TextBlock.cs @@ -604,7 +604,9 @@ namespace Avalonia.Controls return new Size(); } - var padding = Padding; + var scale = LayoutHelper.GetLayoutScale(this); + + var padding = LayoutHelper.RoundLayoutThickness(Padding, scale, scale); _constraint = availableSize.Deflate(padding); @@ -612,23 +614,24 @@ namespace Avalonia.Controls InvalidateArrange(); - var measuredSize = PixelSize.FromSize(TextLayout.Bounds.Size, 1); + var measuredSize = TextLayout.Bounds.Size.Inflate(padding); - return new Size(measuredSize.Width, measuredSize.Height).Inflate(padding); + return measuredSize; } protected override Size ArrangeOverride(Size finalSize) { + if(finalSize.Width < TextLayout.Bounds.Width) + { + finalSize = finalSize.WithWidth(TextLayout.Bounds.Width); + } + if (MathUtilities.AreClose(_constraint.Width, finalSize.Width)) { return finalSize; } - var padding = Padding; - - var textSize = PixelSize.FromSize(finalSize.Deflate(padding), VisualRoot?.RenderScaling ?? 1); - - _constraint = new Size(textSize.Width, textSize.Height); + _constraint = new Size(finalSize.Width, double.PositiveInfinity); _textLayout = null; From bdadb6a35111899747c353aa01a1ab334805a384 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Tue, 3 May 2022 12:04:26 +0200 Subject: [PATCH 5/6] Fix GetTextBounds for mixed runs --- .../Media/TextFormatting/TextLineImpl.cs | 47 ++++-------------- src/Skia/Avalonia.Skia/TextShaperImpl.cs | 7 +-- .../Media/TextShaperImpl.cs | 33 +++++------- ...estrictedHeight_VerticalAlign.expected.png | Bin 768 -> 752 bytes ...estrictedHeight_VerticalAlign.expected.png | Bin 532 -> 557 bytes 5 files changed, 24 insertions(+), 63 deletions(-) diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs index 35ada3c7ee..a518e2ffb8 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs @@ -536,6 +536,11 @@ namespace Avalonia.Media.TextFormatting endX += currentRun.Size.Width; } + if(currentPosition < firstTextSourceCharacterIndex) + { + startX += currentRun.Size.Width; + } + currentPosition += currentRun.TextSourceLength; break; @@ -590,10 +595,10 @@ namespace Avalonia.Media.TextFormatting public TextLineImpl FinalizeLine() { - BidiReorder(); - _textLineMetrics = CreateLineMetrics(); + BidiReorder(); + return this; } @@ -1068,41 +1073,11 @@ namespace Avalonia.Media.TextFormatting } } - switch (_paragraphProperties.FlowDirection) + if (index == _textRuns.Count - 1) { - case FlowDirection.LeftToRight: - { - if (index == _textRuns.Count - 1) - { - width = widthIncludingWhitespace + textRun.GlyphRun.Metrics.Width; - trailingWhitespaceLength = textRun.GlyphRun.Metrics.TrailingWhitespaceLength; - newLineLength = textRun.GlyphRun.Metrics.NewlineLength; - } - - break; - } - - case FlowDirection.RightToLeft: - { - if (index == _textRuns.Count - 1) - { - var firstRun = _textRuns[0]; - - if (firstRun is ShapedTextCharacters shapedTextCharacters) - { - var offset = shapedTextCharacters.GlyphRun.Metrics.WidthIncludingTrailingWhitespace - - shapedTextCharacters.GlyphRun.Metrics.Width; - - width = widthIncludingWhitespace + - textRun.GlyphRun.Metrics.WidthIncludingTrailingWhitespace - offset; - - trailingWhitespaceLength = shapedTextCharacters.GlyphRun.Metrics.TrailingWhitespaceLength; - newLineLength = shapedTextCharacters.GlyphRun.Metrics.NewlineLength; - } - } - - break; - } + width = widthIncludingWhitespace + textRun.GlyphRun.Metrics.Width; + trailingWhitespaceLength = textRun.GlyphRun.Metrics.TrailingWhitespaceLength; + newLineLength = textRun.GlyphRun.Metrics.NewlineLength; } widthIncludingWhitespace += textRun.GlyphRun.Metrics.WidthIncludingTrailingWhitespace; diff --git a/src/Skia/Avalonia.Skia/TextShaperImpl.cs b/src/Skia/Avalonia.Skia/TextShaperImpl.cs index ebaa247da8..908b0ffa47 100644 --- a/src/Skia/Avalonia.Skia/TextShaperImpl.cs +++ b/src/Skia/Avalonia.Skia/TextShaperImpl.cs @@ -27,7 +27,7 @@ namespace Avalonia.Skia buffer.GuessSegmentProperties(); - buffer.Direction = (bidiLevel & 1) == 0 ? Direction.LeftToRight : Direction.RightToLeft; + buffer.Direction = Direction.LeftToRight; //Always shape LeftToRight buffer.Language = new Language(culture ?? CultureInfo.CurrentCulture); @@ -35,11 +35,6 @@ namespace Avalonia.Skia font.Shape(buffer); - if (buffer.Direction == Direction.RightToLeft) - { - buffer.Reverse(); - } - font.GetScale(out var scaleX, out _); var textScale = fontRenderingEmSize / scaleX; diff --git a/src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs index 59027a663f..f4e4b00147 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.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; @@ -11,8 +10,7 @@ using GlyphInfo = HarfBuzzSharp.GlyphInfo; namespace Avalonia.Direct2D1.Media { - -internal class TextShaperImpl : ITextShaperImpl + internal class TextShaperImpl : ITextShaperImpl { public ShapedBuffer ShapeText(ReadOnlySlice text, TextShaperOptions options) { @@ -23,25 +21,20 @@ internal class TextShaperImpl : ITextShaperImpl 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); - + buffer.GuessSegmentProperties(); - buffer.Direction = (bidiLevel & 1) == 0 ? Direction.LeftToRight : Direction.RightToLeft; + buffer.Direction = Direction.LeftToRight; //Always shape LeftToRight - buffer.Language = new Language(culture ?? CultureInfo.CurrentCulture); + buffer.Language = new Language(culture ?? CultureInfo.CurrentCulture); var font = ((GlyphTypefaceImpl)typeface.PlatformImpl).Font; font.Shape(buffer); - if (buffer.Direction == Direction.RightToLeft) - { - buffer.Reverse(); - } - font.GetScale(out var scaleX, out _); var textScale = fontRenderingEmSize / scaleX; @@ -60,13 +53,13 @@ internal class TextShaperImpl : ITextShaperImpl var glyphIndex = (ushort)sourceInfo.Codepoint; - var glyphCluster = (int)sourceInfo.Cluster; + var glyphCluster = (int)(sourceInfo.Cluster); var glyphAdvance = GetGlyphAdvance(glyphPositions, i, textScale); var glyphOffset = GetGlyphOffset(glyphPositions, i, textScale); - if (glyphIndex == 0 && text[glyphCluster] == '\t') + if (glyphIndex == 0 && text.Buffer.Span[glyphCluster] == '\t') { glyphIndex = typeface.GetGlyph(' '); @@ -75,9 +68,7 @@ internal class TextShaperImpl : ITextShaperImpl 4 * typeface.GetGlyphAdvance(glyphIndex) * textScale; } - var targetInfo = - new Avalonia.Media.TextFormatting.GlyphInfo(glyphIndex, glyphCluster, glyphAdvance, - glyphOffset); + var targetInfo = new Avalonia.Media.TextFormatting.GlyphInfo(glyphIndex, glyphCluster, glyphAdvance, glyphOffset); shapedBuffer[i] = targetInfo; } @@ -91,7 +82,7 @@ internal class TextShaperImpl : ITextShaperImpl var length = buffer.Length; var glyphInfos = buffer.GetGlyphInfoSpan(); - + var second = glyphInfos[length - 1]; if (!new Codepoint((int)second.Codepoint).IsBreakChar) @@ -102,7 +93,7 @@ internal class TextShaperImpl : ITextShaperImpl if (length > 1 && glyphInfos[length - 2].Codepoint == '\r' && second.Codepoint == '\n') { var first = glyphInfos[length - 2]; - + first.Codepoint = '\u200C'; second.Codepoint = '\u200C'; second.Cluster = first.Cluster; @@ -113,7 +104,7 @@ internal class TextShaperImpl : ITextShaperImpl { *p = first; } - + fixed (GlyphInfo* p = &glyphInfos[length - 1]) { *p = second; @@ -148,7 +139,7 @@ internal class TextShaperImpl : ITextShaperImpl private static double GetGlyphAdvance(ReadOnlySpan glyphPositions, int index, double textScale) { // Depends on direction of layout - // advanceBuffer[index] = buffer.GlyphPositions[index].YAdvance * textScale; + // glyphPositions[index].YAdvance * textScale; return glyphPositions[index].XAdvance * textScale; } } diff --git a/tests/TestFiles/Direct2D1/Controls/TextBlock/RestrictedHeight_VerticalAlign.expected.png b/tests/TestFiles/Direct2D1/Controls/TextBlock/RestrictedHeight_VerticalAlign.expected.png index edd4dfd263d0aeddca97eb4f38b4a5310ef6441f..e8624fa457f37565fdc483c474424991e7b696d3 100644 GIT binary patch delta 663 zcmV;I0%-k!2Ji)tK~kDYL_t(|UhUgIa??N*$8nhhgpv-9Kst&PbZ~-{bWo>HI%eP$ zbliY0bTFi#WQHbHCZVECE@0mCYC&Ev)~@tc(Vy>U=9gVr&L&sF5? z2a=zgt*ZJWIdk3Ow4gWZj_kMw*zr#C&2{Yze1z^>+>Eo|l*ZA6u^yCDYTA#}F7b|b z1-+DUQlBp7XTR-n+9lr6_JMtu-Y0j%e~v?mF0?|+c*2;U(Dpd(9@K4;4-y?YmRFK4 z^g(@eT|_WHMd&L`zBFy;-KXYy|(KU{b8zDnNKxsvFW zH-0;ezHL90^u_#aD^}3IBtx9|jEVjQPyh0jnrD(F8Df4|Lt|DbXw1sJ6tu3b_2LwN zfr_3W`0X%Otk(2a znEu5s`c3DXRyVzJM1r%nkRJ0aMM(FjDeD5@R(^af#<|TF^PCdjI796AKN{2Wx1| x3I&Z>NnUc)osL*?lhFbeli&gb8GzoZ>JL{KmA-v_Vov}7002ovPDHLkV1g47Q*QtO delta 680 zcmV;Z0$2U;1%L*SK~kzoL_t(|UhUg4a??N*#&MYggpv-903AgNIyeCxbfl&a9W!tW zI&MH0Iv6M@nW3S|1S(3nfO#)Z%WR}I+O^(-y!!uUzG!9H8)yBZ?8;6DlfeQOe*gdg zXxpaWPS|ey#tfUx?*;5HyP&tZcTL!I`ujjZqdp26OL9YU+nOU6lCRd>je^FKJd`}O zW_MF^B>8f=*LCkC {7WCyrdau|9DDpz`!J7I0!x`G6xaq6RA@-#wV?8P7*0lB2 zcR0njpx4+}$;-od?LS_b&&a;ve=BL7*bO6(B(JSGl6vx=r1i#b7`Y?S$yCbPULeM6 zTHMz){gUg6L?5a-l^jR=yNWr7KJht^UmM12HvJ8fFHxkRw^Gof+f%VkP`9s=*e0mk z7vps`wh8=x#-cMc>d`3$^6eTg3VIo*Bf3vR=Kl|;! z(DHaSQP7@?)mjVRC24uQm~x;g)e2hQi1%9P=%2M8);uWsoOPb3`6l@l?GNK=&Er+A zpubDJ=7suZ&Sz^Ll=qV7ea`gjQ**ChgxBkvx2!pv=VH~fuM&*cZ0&rgMIUtbr>&cyZeJy_O~80f#WsdHG}g%XEHOVp z>R4%l)Unb8sbi%HQpZXYq>i0Rjt95J(bw-R-tWct3GwW!G(|iMC>jgkxg4sswAxF~ z1qq=Aoq5-UtN(5T)WaMaO`)LClwW{S>gC-`V7Q6^ O0000Wy2)Tif!}f*BA9YQBr<>Yv<0+ed$LhGhl$6qoFtRV$DAqv>tEFoHy;= zjj8AUHLmvgqjP}4f0`Ym-l@|ncRSzK*B<`-V%Iyxa-Mt3j*FJP@}0!LwOcIxMdrlE z`EBc_9=&?AZmz%QzU4W~PG3(r%l<3q)s%g)(bchAU-i5`|6g~~!uOv(cvj{7-f@I; zZO^|R^Na=Arn~kV?eHzSTz#h~bwk)M4ct2k#G&)%(W*Dsx)C6j$|eyBU2 z#3_f(+TH&xR=QqWGxcS(?x8Q~dmJTy>788NygzpSjqgWi)>y6GoVM2Iepc=R`^UZi z3We58xyrynyb(IzS%pj{N(kv(qntpW!+KP9r*m+Cc8U!>XmlI yb8njNTQ2ZS)p@fv+pJj4eL2tLs~9ojqI);H&U7`F+XGKFP=N&RV07srr_IdAW5%)1;Q!uDXlX#W9ggKvom4qEyL4~4P0&0{{ck2_jY zz?fNE$h%Z>(W=lXQ?j31ZCk(J|C5mA@slS#KgDbNF)^S4hwFN;%CwgrT&UI0`gre( zyBn{Z`^PAr`Jt|XYkILgL&iym%_{3BPpMbF-QIRwbLpF=FDusivZW`ln|z07Z}8Ve zAHMMX-Jp@P^FYYz`%&?e|M>j%d7pLG=tA@w(@X6Ni@OMP+s%{|8UZRef2 zOxJB?{S~_W*Ou5%Gj2?N7|#)W?H1GDRgZPpV+}8q9Cgq?Fkj{U;Tz2DaF{%vno$(gm^S9kO54e|f1`v3Z(cq0>#J*i*%C*Mn*p3gV8zGmi= zcRgFP)>dv@6<_N1-{NIR@src0`e9#J_nmMI=P`-C@@dwg Date: Tue, 3 May 2022 16:34:15 +0200 Subject: [PATCH 6/6] Fix GetTextBounds for some Bidi scenarios --- .../Media/TextFormatting/TextLineImpl.cs | 41 +++++++++++-------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs index a518e2ffb8..73ec055bbe 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs @@ -252,7 +252,7 @@ namespace Avalonia.Media.TextFormatting //Look at the left and right edge of the current run if (currentRun.IsLeftToRight) { - if (lastRun == null || lastRun.IsLeftToRight) + if (_flowDirection == FlowDirection.LeftToRight && (lastRun == null || lastRun.IsLeftToRight)) { if (characterIndex <= textRun.Text.Start) { @@ -455,7 +455,7 @@ namespace Avalonia.Media.TextFormatting } default: { - goto noop; + goto noop; } } @@ -536,7 +536,7 @@ namespace Avalonia.Media.TextFormatting endX += currentRun.Size.Width; } - if(currentPosition < firstTextSourceCharacterIndex) + if (currentPosition < firstTextSourceCharacterIndex) { startX += currentRun.Size.Width; } @@ -554,24 +554,29 @@ namespace Avalonia.Media.TextFormatting var width = endX - startX; - if (lastDirection == currentDirection && result.Count > 0 && MathUtilities.AreClose(currentRect.Right, startX)) + if (!MathUtilities.IsZero(width)) { - currentRect = currentRect.WithWidth(currentRect.Width + width); + if (lastDirection == currentDirection && result.Count > 0 && MathUtilities.AreClose(currentRect.Right, startX)) + { + currentRect = currentRect.WithWidth(currentRect.Width + width); - var textBounds = new TextBounds(currentRect, currentDirection); + var textBounds = new TextBounds(currentRect, currentDirection); - result[result.Count - 1] = textBounds; - } - else - { - currentRect = new Rect(startX, 0, width, Height); + result[result.Count - 1] = textBounds; + } + else + { + + currentRect = new Rect(startX, 0, width, Height); - result.Add(new TextBounds(currentRect, currentDirection)); + result.Add(new TextBounds(currentRect, currentDirection)); + + } } if (currentDirection == FlowDirection.LeftToRight) { - if (currentPosition >= firstTextSourceCharacterIndex + textLength) + if (currentPosition > firstTextSourceCharacterIndex + textLength) { break; } @@ -1026,7 +1031,7 @@ namespace Avalonia.Media.TextFormatting var glyphTypeface = _paragraphProperties.DefaultTextRunProperties.Typeface.GlyphTypeface; var fontRenderingEmSize = _paragraphProperties.DefaultTextRunProperties.FontRenderingEmSize; var scale = fontRenderingEmSize / glyphTypeface.DesignEmHeight; - + var width = 0d; var widthIncludingWhitespace = 0d; var trailingWhitespaceLength = 0; @@ -1036,8 +1041,8 @@ namespace Avalonia.Media.TextFormatting var lineGap = glyphTypeface.LineGap * scale; var height = descent - ascent + lineGap; - - var lineHeight = _paragraphProperties.LineHeight; + + var lineHeight = _paragraphProperties.LineHeight; for (var index = 0; index < _textRuns.Count; index++) { @@ -1136,10 +1141,10 @@ namespace Avalonia.Media.TextFormatting if (!double.IsNaN(lineHeight) && !MathUtilities.IsZero(lineHeight)) { - if(lineHeight > height) + if (lineHeight > height) { height = lineHeight; - } + } } return new TextLineMetrics(widthIncludingWhitespace > _paragraphWidth, height, newLineLength, start,