Browse Source

Merge pull request #3652 from Gillibald/fixes/TextBlock

TextBlock fixes
pull/3656/head
Dariusz Komosiński 7 years ago
committed by GitHub
parent
commit
6853c8e807
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 60
      src/Avalonia.Controls/TextBlock.cs
  2. 27
      src/Avalonia.Visuals/Media/TextFormatting/SimpleTextFormatter.cs
  3. 16
      src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs

60
src/Avalonia.Controls/TextBlock.cs

@ -20,6 +20,12 @@ namespace Avalonia.Controls
public static readonly StyledProperty<IBrush> BackgroundProperty = public static readonly StyledProperty<IBrush> BackgroundProperty =
Border.BackgroundProperty.AddOwner<TextBlock>(); Border.BackgroundProperty.AddOwner<TextBlock>();
/// <summary>
/// Defines the <see cref="Padding"/> property.
/// </summary>
public static readonly StyledProperty<Thickness> PaddingProperty =
Decorator.PaddingProperty.AddOwner<TextBlock>();
// TODO: Define these attached properties elsewhere (e.g. on a Text class) and AddOwner // TODO: Define these attached properties elsewhere (e.g. on a Text class) and AddOwner
// them into TextBlock. // them into TextBlock.
@ -29,7 +35,7 @@ namespace Avalonia.Controls
public static readonly AttachedProperty<FontFamily> FontFamilyProperty = public static readonly AttachedProperty<FontFamily> FontFamilyProperty =
AvaloniaProperty.RegisterAttached<TextBlock, Control, FontFamily>( AvaloniaProperty.RegisterAttached<TextBlock, Control, FontFamily>(
nameof(FontFamily), nameof(FontFamily),
defaultValue: FontFamily.Default, defaultValue: FontFamily.Default,
inherits: true); inherits: true);
/// <summary> /// <summary>
@ -110,20 +116,31 @@ namespace Avalonia.Controls
static TextBlock() static TextBlock()
{ {
ClipToBoundsProperty.OverrideDefaultValue<TextBlock>(true); ClipToBoundsProperty.OverrideDefaultValue<TextBlock>(true);
AffectsRender<TextBlock>( AffectsRender<TextBlock>(
BackgroundProperty, BackgroundProperty, ForegroundProperty, FontSizeProperty,
ForegroundProperty, FontWeightProperty, FontStyleProperty, TextWrappingProperty,
FontWeightProperty, TextTrimmingProperty, TextAlignmentProperty, FontFamilyProperty,
FontSizeProperty, TextDecorationsProperty, TextProperty, PaddingProperty);
FontStyleProperty);
AffectsMeasure<TextBlock>(
FontSizeProperty, FontWeightProperty, FontStyleProperty,
FontFamilyProperty, TextTrimmingProperty, TextProperty,
PaddingProperty);
Observable.Merge( Observable.Merge(
TextProperty.Changed, TextProperty.Changed,
ForegroundProperty.Changed,
TextAlignmentProperty.Changed, TextAlignmentProperty.Changed,
TextWrappingProperty.Changed,
TextTrimmingProperty.Changed,
FontSizeProperty.Changed, FontSizeProperty.Changed,
FontStyleProperty.Changed, FontStyleProperty.Changed,
FontWeightProperty.Changed FontWeightProperty.Changed,
).AddClassHandler<TextBlock>((x, _) => x.OnTextPropertiesChanged()); FontFamilyProperty.Changed,
TextDecorationsProperty.Changed,
PaddingProperty.Changed
).AddClassHandler<TextBlock>((x, _) => x.InvalidateTextLayout());
} }
/// <summary> /// <summary>
@ -145,6 +162,15 @@ namespace Avalonia.Controls
} }
} }
/// <summary>
/// Gets or sets the padding to place around the <see cref="Text"/>.
/// </summary>
public Thickness Padding
{
get { return GetValue(PaddingProperty); }
set { SetValue(PaddingProperty, value); }
}
/// <summary> /// <summary>
/// Gets or sets a brush used to paint the control's background. /// Gets or sets a brush used to paint the control's background.
/// </summary> /// </summary>
@ -363,7 +389,9 @@ namespace Avalonia.Controls
context.FillRectangle(background, new Rect(Bounds.Size)); 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));
} }
/// <summary> /// <summary>
@ -412,6 +440,10 @@ namespace Avalonia.Controls
return new Size(); return new Size();
} }
var padding = Padding;
availableSize = availableSize.Deflate(padding);
if (_constraint != availableSize) if (_constraint != availableSize)
{ {
InvalidateTextLayout(); InvalidateTextLayout();
@ -419,19 +451,17 @@ namespace Avalonia.Controls
_constraint = availableSize; _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) protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{ {
base.OnAttachedToLogicalTree(e); base.OnAttachedToLogicalTree(e);
InvalidateTextLayout();
InvalidateMeasure();
}
private void OnTextPropertiesChanged()
{
InvalidateTextLayout(); InvalidateTextLayout();
InvalidateMeasure(); InvalidateMeasure();
} }
} }

27
src/Avalonia.Visuals/Media/TextFormatting/SimpleTextFormatter.cs

@ -201,18 +201,17 @@ namespace Avalonia.Media.TextFormatting
var availableWidth = paragraphWidth; var availableWidth = paragraphWidth;
var currentWidth = 0.0; var currentWidth = 0.0;
var runIndex = 0; var runIndex = 0;
var length = 0;
while (runIndex < textRuns.Count) while (runIndex < textRuns.Count)
{ {
var currentRun = textRuns[runIndex]; var currentRun = textRuns[runIndex];
currentWidth += currentRun.GlyphRun.Bounds.Width; if (currentWidth + currentRun.GlyphRun.Bounds.Width > availableWidth)
if (currentWidth > availableWidth)
{ {
var measuredLength = MeasureText(currentRun, paragraphWidth); var measuredLength = MeasureText(currentRun, paragraphWidth - currentWidth);
if (measuredLength < text.End) if (measuredLength < currentRun.Text.Length)
{ {
var currentBreakPosition = -1; 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 = var textLineMetrics =
TextLineMetrics.Create(splitResult.First, paragraphWidth, paragraphProperties.TextAlignment); 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++; runIndex++;
} }
@ -281,12 +284,18 @@ namespace Avalonia.Media.TextFormatting
if (measuredWidth + advance > availableWidth) if (measuredWidth + advance > availableWidth)
{ {
index--;
break; break;
} }
measuredWidth += advance; measuredWidth += advance;
} }
if(index < 0)
{
return 0;
}
var cluster = textRun.GlyphRun.GlyphClusters[index]; var cluster = textRun.GlyphRun.GlyphClusters[index];
var characterHit = textRun.GlyphRun.FindNearestCharacterHit(cluster, out _); var characterHit = textRun.GlyphRun.FindNearestCharacterHit(cluster, out _);
@ -355,7 +364,7 @@ namespace Avalonia.Media.TextFormatting
continue; 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]; var first = new ShapedTextRun[firstCount];

16
src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs

@ -233,6 +233,12 @@ namespace Avalonia.Media.TextFormatting
var textLine = TextFormatter.Current.FormatLine(textSource, 0, MaxWidth, _paragraphProperties); var textLine = TextFormatter.Current.FormatLine(textSource, 0, MaxWidth, _paragraphProperties);
if (!double.IsPositiveInfinity(MaxHeight) && bottom + textLine.LineMetrics.Size.Height > MaxHeight)
{
currentPosition = _text.Length;
break;
}
UpdateBounds(textLine, ref left, ref right, ref bottom); UpdateBounds(textLine, ref left, ref right, ref bottom);
textLines.Add(textLine); textLines.Add(textLine);
@ -253,17 +259,17 @@ namespace Avalonia.Media.TextFormatting
{ {
var emptyTextLine = CreateEmptyTextLine(currentPosition); var emptyTextLine = CreateEmptyTextLine(currentPosition);
if (!double.IsPositiveInfinity(MaxHeight) && bottom + emptyTextLine.LineMetrics.Size.Height > MaxHeight)
{
break;
}
UpdateBounds(emptyTextLine, ref left, ref right, ref bottom); UpdateBounds(emptyTextLine, ref left, ref right, ref bottom);
textLines.Add(emptyTextLine); textLines.Add(emptyTextLine);
break; break;
} }
if (!double.IsPositiveInfinity(MaxHeight) && MaxHeight < Bounds.Height)
{
break;
}
} }
Bounds = new Rect(left, 0, right, bottom); Bounds = new Rect(left, 0, right, bottom);

Loading…
Cancel
Save