From 48177eddb70e8815c5e2d8f7ea2e625992e37d7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro?= Date: Sun, 31 May 2020 21:05:47 +0100 Subject: [PATCH] Added MaxLines property to TextBlock. --- src/Avalonia.Controls/TextBlock.cs | 22 ++++++++++++++++++- .../Media/TextFormatting/TextLayout.cs | 14 ++++++++++-- .../TextLayoutTests.cs | 21 ++++++++++++++++++ 3 files changed, 54 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs index e9867e4503..13bc4ed124 100644 --- a/src/Avalonia.Controls/TextBlock.cs +++ b/src/Avalonia.Controls/TextBlock.cs @@ -70,6 +70,14 @@ namespace Avalonia.Controls Brushes.Black, inherits: true); + /// + /// Defines the property. + /// + public static readonly StyledProperty MaxLinesProperty = + AvaloniaProperty.Register( + nameof(MaxLines), + validate: IsValidMaxLines); + /// /// Defines the property. /// @@ -222,6 +230,15 @@ namespace Avalonia.Controls set { SetValue(ForegroundProperty, value); } } + /// + /// Gets or sets the maximum number of text lines. + /// + public int MaxLines + { + get => GetValue(MaxLinesProperty); + set => SetValue(MaxLinesProperty, value); + } + /// /// Gets or sets the control's text wrapping mode. /// @@ -404,7 +421,8 @@ namespace Avalonia.Controls TextTrimming, TextDecorations, constraint.Width, - constraint.Height); + constraint.Height, + MaxLines); } /// @@ -451,5 +469,7 @@ namespace Avalonia.Controls InvalidateMeasure(); } + + private static bool IsValidMaxLines(int maxLines) => maxLines >= 0; } } diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs index 8d4475d1c3..720185a3ad 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs @@ -32,6 +32,7 @@ namespace Avalonia.Media.TextFormatting /// The text decorations. /// The maximum width. /// The maximum height. + /// The maximum number of text lines. /// The text style overrides. public TextLayout( string text, @@ -44,6 +45,7 @@ namespace Avalonia.Media.TextFormatting TextDecorationCollection textDecorations = null, double maxWidth = double.PositiveInfinity, double maxHeight = double.PositiveInfinity, + int maxLines = 0, IReadOnlyList textStyleOverrides = null) { _text = string.IsNullOrEmpty(text) ? @@ -59,6 +61,8 @@ namespace Avalonia.Media.TextFormatting MaxHeight = maxHeight; + MaxLines = maxLines; + UpdateLayout(); } @@ -73,6 +77,12 @@ namespace Avalonia.Media.TextFormatting /// public double MaxHeight { get; } + + /// + /// Gets the maximum number of text lines. + /// + public double MaxLines { get; } + /// /// Gets the text lines. /// @@ -192,7 +202,7 @@ namespace Avalonia.Media.TextFormatting var currentPosition = 0; - while (currentPosition < _text.Length) + while (currentPosition < _text.Length && (MaxLines == 0 || textLines.Count < MaxLines)) { int length; @@ -222,7 +232,7 @@ namespace Avalonia.Media.TextFormatting var remainingLength = length; - while (remainingLength > 0) + while (remainingLength > 0 && (MaxLines == 0 || textLines.Count < MaxLines)) { var textSlice = _text.AsSlice(currentPosition, remainingLength); diff --git a/tests/Avalonia.Skia.UnitTests/TextLayoutTests.cs b/tests/Avalonia.Skia.UnitTests/TextLayoutTests.cs index 0d9fd31e52..fb63df0407 100644 --- a/tests/Avalonia.Skia.UnitTests/TextLayoutTests.cs +++ b/tests/Avalonia.Skia.UnitTests/TextLayoutTests.cs @@ -506,6 +506,27 @@ namespace Avalonia.Skia.UnitTests } } + [InlineData("0123456789\r\n0123456789\r\n0123456789", 1, 1)] + [InlineData("0123456789\r\n0123456789\r\n0123456789", 2, 2)] + [InlineData("0123456789\r\n0123456789\r\n0123456789", 3, 3)] + [InlineData("0123456789\r\n0123456789\r\n0123456789", 4, 3)] + [Theory] + public void Should_Not_Exceed_MaxLines(string text, int maxLines, int expectedLines) + { + using (Start()) + { + var layout = new TextLayout( + text, + Typeface.Default, + 12, + Brushes.Black, + maxWidth: 50, + maxLines: maxLines); + + Assert.Equal(expectedLines, layout.TextLines.Count); + } + } + private const string Text = "日本でTest一番読まれている英字新聞・ジャパンタイムズが発信する国内外ニュースと、様々なジャンルの特集記事。"; [Fact(Skip= "Only used for profiling.")]