From 2d78458f7484d8a5f0b67aa5d30f4bfec784184b Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Tue, 10 Mar 2020 07:52:01 +0100 Subject: [PATCH 1/3] Add padding --- src/Avalonia.Controls/TextBlock.cs | 35 +++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs index 1655e22331..560c1952ac 100644 --- a/src/Avalonia.Controls/TextBlock.cs +++ b/src/Avalonia.Controls/TextBlock.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System.Reactive.Linq; +using Avalonia.Layout; using Avalonia.LogicalTree; using Avalonia.Media; using Avalonia.Media.TextFormatting; @@ -20,6 +21,12 @@ namespace Avalonia.Controls public static readonly StyledProperty BackgroundProperty = Border.BackgroundProperty.AddOwner(); + /// + /// Defines the property. + /// + public static readonly StyledProperty PaddingProperty = + Decorator.PaddingProperty.AddOwner(); + // TODO: Define these attached properties elsewhere (e.g. on a Text class) and AddOwner // them into TextBlock. @@ -29,7 +36,7 @@ namespace Avalonia.Controls public static readonly AttachedProperty FontFamilyProperty = AvaloniaProperty.RegisterAttached( nameof(FontFamily), - defaultValue: FontFamily.Default, + defaultValue: FontFamily.Default, inherits: true); /// @@ -110,16 +117,13 @@ namespace Avalonia.Controls static TextBlock() { ClipToBoundsProperty.OverrideDefaultValue(true); - AffectsRender( - BackgroundProperty, - ForegroundProperty, - FontWeightProperty, - FontSizeProperty, - FontStyleProperty); Observable.Merge( TextProperty.Changed, + ForegroundProperty.Changed, TextAlignmentProperty.Changed, + TextWrappingProperty.Changed, + TextTrimmingProperty.Changed, FontSizeProperty.Changed, FontStyleProperty.Changed, FontWeightProperty.Changed @@ -145,6 +149,15 @@ namespace Avalonia.Controls } } + /// + /// Gets or sets the padding to place around the . + /// + public Thickness Padding + { + get { return GetValue(PaddingProperty); } + set { SetValue(PaddingProperty, value); } + } + /// /// Gets or sets a brush used to paint the control's background. /// @@ -363,7 +376,9 @@ namespace Avalonia.Controls context.FillRectangle(background, new Rect(Bounds.Size)); } - TextLayout?.Draw(context.PlatformImpl, new Point()); + var padding = Padding; + + TextLayout?.Draw(context.PlatformImpl, new Point(padding.Left, padding.Top)); } /// @@ -412,6 +427,10 @@ namespace Avalonia.Controls return new Size(); } + var padding = Padding; + + availableSize = availableSize.Deflate(padding); + if (_constraint != availableSize) { InvalidateTextLayout(); From 6c948af49d1e8eefda98a1a9c3dec1ec17878582 Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Tue, 10 Mar 2020 09:28:24 +0100 Subject: [PATCH 2/3] Fix TextWrapping --- src/Avalonia.Controls/TextBlock.cs | 6 ++++- .../TextFormatting/SimpleTextFormatter.cs | 27 ++++++++++++------- .../Media/TextFormatting/TextLayout.cs | 15 +++++++---- 3 files changed, 33 insertions(+), 15 deletions(-) diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs index 560c1952ac..a7ec6457fa 100644 --- a/src/Avalonia.Controls/TextBlock.cs +++ b/src/Avalonia.Controls/TextBlock.cs @@ -118,6 +118,8 @@ namespace Avalonia.Controls { ClipToBoundsProperty.OverrideDefaultValue(true); + AffectsRender(BackgroundProperty); + Observable.Merge( TextProperty.Changed, ForegroundProperty.Changed, @@ -438,7 +440,9 @@ namespace Avalonia.Controls _constraint = availableSize; - return TextLayout?.Bounds.Size ?? Size.Empty; + var measuredSize = TextLayout?.Bounds.Size ?? Size.Empty; + + return measuredSize.Inflate(padding); } protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e) diff --git a/src/Avalonia.Visuals/Media/TextFormatting/SimpleTextFormatter.cs b/src/Avalonia.Visuals/Media/TextFormatting/SimpleTextFormatter.cs index e84242c628..30d513386e 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/SimpleTextFormatter.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/SimpleTextFormatter.cs @@ -201,18 +201,17 @@ namespace Avalonia.Media.TextFormatting var availableWidth = paragraphWidth; var currentWidth = 0.0; var runIndex = 0; + var length = 0; while (runIndex < textRuns.Count) { var currentRun = textRuns[runIndex]; - currentWidth += currentRun.GlyphRun.Bounds.Width; - - if (currentWidth > availableWidth) + if (currentWidth + currentRun.GlyphRun.Bounds.Width > availableWidth) { - var measuredLength = MeasureText(currentRun, paragraphWidth); + var measuredLength = MeasureText(currentRun, paragraphWidth - currentWidth); - if (measuredLength < text.End) + if (measuredLength < currentRun.Text.Length) { var currentBreakPosition = -1; @@ -241,15 +240,19 @@ namespace Avalonia.Media.TextFormatting } } - var splitResult = SplitTextRuns(textRuns, measuredLength); + length += measuredLength; + + var splitResult = SplitTextRuns(textRuns, length); var textLineMetrics = TextLineMetrics.Create(splitResult.First, paragraphWidth, paragraphProperties.TextAlignment); - return new SimpleTextLine(text.Take(measuredLength), splitResult.First, textLineMetrics); + return new SimpleTextLine(text.Take(length), splitResult.First, textLineMetrics); } - availableWidth -= currentRun.GlyphRun.Bounds.Width; + currentWidth += currentRun.GlyphRun.Bounds.Width; + + length += currentRun.GlyphRun.Characters.Length; runIndex++; } @@ -281,12 +284,18 @@ namespace Avalonia.Media.TextFormatting if (measuredWidth + advance > availableWidth) { + index--; break; } measuredWidth += advance; } + if(index < 0) + { + return 0; + } + var cluster = textRun.GlyphRun.GlyphClusters[index]; var characterHit = textRun.GlyphRun.FindNearestCharacterHit(cluster, out _); @@ -355,7 +364,7 @@ namespace Avalonia.Media.TextFormatting continue; } - var firstCount = currentRun.GlyphRun.Characters.Length > 1 ? i + 1 : i; + var firstCount = currentRun.GlyphRun.Characters.Length >= 1 ? i + 1 : i; var first = new ShapedTextRun[firstCount]; diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs index 0c9013e6f7..2ef7322eaa 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs @@ -233,6 +233,11 @@ namespace Avalonia.Media.TextFormatting var textLine = TextFormatter.Current.FormatLine(textSource, 0, MaxWidth, _paragraphProperties); + if (!double.IsPositiveInfinity(MaxHeight) && bottom + textLine.LineMetrics.Size.Height > MaxHeight) + { + break; + } + UpdateBounds(textLine, ref left, ref right, ref bottom); textLines.Add(textLine); @@ -253,17 +258,17 @@ namespace Avalonia.Media.TextFormatting { var emptyTextLine = CreateEmptyTextLine(currentPosition); + if (!double.IsPositiveInfinity(MaxHeight) && bottom + emptyTextLine.LineMetrics.Size.Height > MaxHeight) + { + break; + } + UpdateBounds(emptyTextLine, ref left, ref right, ref bottom); textLines.Add(emptyTextLine); break; } - - if (!double.IsPositiveInfinity(MaxHeight) && MaxHeight < Bounds.Height) - { - break; - } } Bounds = new Rect(left, 0, right, bottom); From 55b4a1930b4541273b2b37c928d42a5bbf9f2b63 Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Tue, 10 Mar 2020 15:19:11 +0100 Subject: [PATCH 3/3] Cleanup invalidation of measure and render --- src/Avalonia.Controls/TextBlock.cs | 25 ++++++++++++------- .../Media/TextFormatting/TextLayout.cs | 1 + 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs index a7ec6457fa..0278360ba5 100644 --- a/src/Avalonia.Controls/TextBlock.cs +++ b/src/Avalonia.Controls/TextBlock.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System.Reactive.Linq; -using Avalonia.Layout; using Avalonia.LogicalTree; using Avalonia.Media; using Avalonia.Media.TextFormatting; @@ -118,7 +117,16 @@ namespace Avalonia.Controls { ClipToBoundsProperty.OverrideDefaultValue(true); - AffectsRender(BackgroundProperty); + AffectsRender( + BackgroundProperty, ForegroundProperty, FontSizeProperty, + FontWeightProperty, FontStyleProperty, TextWrappingProperty, + TextTrimmingProperty, TextAlignmentProperty, FontFamilyProperty, + TextDecorationsProperty, TextProperty, PaddingProperty); + + AffectsMeasure( + FontSizeProperty, FontWeightProperty, FontStyleProperty, + FontFamilyProperty, TextTrimmingProperty, TextProperty, + PaddingProperty); Observable.Merge( TextProperty.Changed, @@ -128,8 +136,11 @@ namespace Avalonia.Controls TextTrimmingProperty.Changed, FontSizeProperty.Changed, FontStyleProperty.Changed, - FontWeightProperty.Changed - ).AddClassHandler((x, _) => x.OnTextPropertiesChanged()); + FontWeightProperty.Changed, + FontFamilyProperty.Changed, + TextDecorationsProperty.Changed, + PaddingProperty.Changed + ).AddClassHandler((x, _) => x.InvalidateTextLayout()); } /// @@ -448,13 +459,9 @@ namespace Avalonia.Controls protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e) { base.OnAttachedToLogicalTree(e); - InvalidateTextLayout(); - InvalidateMeasure(); - } - private void OnTextPropertiesChanged() - { InvalidateTextLayout(); + InvalidateMeasure(); } } diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs index 2ef7322eaa..3f0cf7c680 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs @@ -235,6 +235,7 @@ namespace Avalonia.Media.TextFormatting if (!double.IsPositiveInfinity(MaxHeight) && bottom + textLine.LineMetrics.Size.Height > MaxHeight) { + currentPosition = _text.Length; break; }