using System;
using System.Collections.Generic;
using System.Text;
using Avalonia.Automation.Peers;
using Avalonia.Controls.Documents;
using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.Media.TextFormatting;
using Avalonia.Metadata;
using Avalonia.Utilities;
namespace Avalonia.Controls
{
///
/// A control that displays a block of text.
///
public class TextBlock : Control
{
///
/// Defines the property.
///
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.
///
/// Defines the property.
///
public static readonly AttachedProperty FontFamilyProperty =
AvaloniaProperty.RegisterAttached(
nameof(FontFamily),
defaultValue: FontFamily.Default,
inherits: true);
///
/// Defines the property.
///
public static readonly AttachedProperty FontSizeProperty =
AvaloniaProperty.RegisterAttached(
nameof(FontSize),
defaultValue: 12,
inherits: true);
///
/// Defines the property.
///
public static readonly AttachedProperty FontStyleProperty =
AvaloniaProperty.RegisterAttached(
nameof(FontStyle),
inherits: true);
///
/// Defines the property.
///
public static readonly AttachedProperty FontWeightProperty =
AvaloniaProperty.RegisterAttached(
nameof(FontWeight),
inherits: true,
defaultValue: FontWeight.Normal);
///
/// Defines the property.
///
public static readonly AttachedProperty FontStretchProperty =
AvaloniaProperty.RegisterAttached(
nameof(FontStretch),
inherits: true,
defaultValue: FontStretch.Normal);
///
/// Defines the property.
///
public static readonly AttachedProperty ForegroundProperty =
AvaloniaProperty.RegisterAttached(
nameof(Foreground),
Brushes.Black,
inherits: true);
///
/// Defines the property.
///
public static readonly StyledProperty LineHeightProperty =
AvaloniaProperty.Register(
nameof(LineHeight),
double.NaN,
validate: IsValidLineHeight);
///
/// Defines the property.
///
public static readonly StyledProperty MaxLinesProperty =
AvaloniaProperty.Register(
nameof(MaxLines),
validate: IsValidMaxLines);
///
/// Defines the property.
///
public static readonly DirectProperty TextProperty =
AvaloniaProperty.RegisterDirect(
nameof(Text),
o => o.Text,
(o, v) => o.Text = v);
///
/// Defines the property.
///
public static readonly DirectProperty InlinesProperty =
AvaloniaProperty.RegisterDirect(
nameof(Inlines),
o => o.Inlines);
///
/// Defines the property.
///
public static readonly StyledProperty TextAlignmentProperty =
AvaloniaProperty.Register(nameof(TextAlignment));
///
/// Defines the property.
///
public static readonly StyledProperty TextWrappingProperty =
AvaloniaProperty.Register(nameof(TextWrapping));
///
/// Defines the property.
///
public static readonly StyledProperty TextTrimmingProperty =
AvaloniaProperty.Register(nameof(TextTrimming));
///
/// Defines the property.
///
public static readonly StyledProperty TextDecorationsProperty =
AvaloniaProperty.Register(nameof(TextDecorations));
private TextLayout? _textLayout;
private Size _constraint;
///
/// Initializes static members of the class.
///
static TextBlock()
{
ClipToBoundsProperty.OverrideDefaultValue(true);
AffectsRender(BackgroundProperty, ForegroundProperty);
}
///
/// Initializes a new instance of the class.
///
public TextBlock()
{
Inlines = new InlineCollection(this);
Inlines.Invalidated += InlinesChanged;
}
///
/// Gets the used to render the text.
///
public TextLayout TextLayout
{
get
{
return _textLayout ?? (_textLayout = CreateTextLayout(_constraint, Text));
}
}
///
/// 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.
///
public IBrush? Background
{
get { return GetValue(BackgroundProperty); }
set { SetValue(BackgroundProperty, value); }
}
///
/// Gets or sets the text.
///
public string? Text
{
get => Inlines.Text;
set
{
var old = Text;
if (value == old)
{
return;
}
Inlines.Text = value;
RaisePropertyChanged(TextProperty, old, value);
}
}
///
/// Gets or sets the inlines.
///
[Content]
public InlineCollection Inlines { get; }
///
/// Gets or sets the font family.
///
public FontFamily FontFamily
{
get { return GetValue(FontFamilyProperty); }
set { SetValue(FontFamilyProperty, value); }
}
///
/// Gets or sets the font size.
///
public double FontSize
{
get { return GetValue(FontSizeProperty); }
set { SetValue(FontSizeProperty, value); }
}
///
/// Gets or sets the font style.
///
public FontStyle FontStyle
{
get { return GetValue(FontStyleProperty); }
set { SetValue(FontStyleProperty, value); }
}
///
/// Gets or sets the font weight.
///
public FontWeight FontWeight
{
get { return GetValue(FontWeightProperty); }
set { SetValue(FontWeightProperty, value); }
}
///
/// Gets or sets the font stretch.
///
public FontStretch FontStretch
{
get { return GetValue(FontStretchProperty); }
set { SetValue(FontStretchProperty, value); }
}
///
/// Gets or sets a brush used to paint the text.
///
public IBrush? Foreground
{
get { return GetValue(ForegroundProperty); }
set { SetValue(ForegroundProperty, value); }
}
///
/// Gets or sets the height of each line of content.
///
public double LineHeight
{
get => GetValue(LineHeightProperty);
set => SetValue(LineHeightProperty, 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.
///
public TextWrapping TextWrapping
{
get { return GetValue(TextWrappingProperty); }
set { SetValue(TextWrappingProperty, value); }
}
///
/// Gets or sets the control's text trimming mode.
///
public TextTrimming TextTrimming
{
get { return GetValue(TextTrimmingProperty); }
set { SetValue(TextTrimmingProperty, value); }
}
///
/// Gets or sets the text alignment.
///
public TextAlignment TextAlignment
{
get { return GetValue(TextAlignmentProperty); }
set { SetValue(TextAlignmentProperty, value); }
}
///
/// Gets or sets the text decorations.
///
public TextDecorationCollection? TextDecorations
{
get => GetValue(TextDecorationsProperty);
set => SetValue(TextDecorationsProperty, value);
}
///
/// Gets the value of the attached on a control.
///
/// The control.
/// The font family.
public static FontFamily GetFontFamily(Control control)
{
return control.GetValue(FontFamilyProperty);
}
///
/// Gets the value of the attached on a control.
///
/// The control.
/// The font size.
public static double GetFontSize(Control control)
{
return control.GetValue(FontSizeProperty);
}
///
/// Gets the value of the attached on a control.
///
/// The control.
/// The font style.
public static FontStyle GetFontStyle(Control control)
{
return control.GetValue(FontStyleProperty);
}
///
/// Gets the value of the attached on a control.
///
/// The control.
/// The font weight.
public static FontWeight GetFontWeight(Control control)
{
return control.GetValue(FontWeightProperty);
}
///
/// Gets the value of the attached on a control.
///
/// The control.
/// The font stretch.
public static FontStretch GetFontStretch(Control control)
{
return control.GetValue(FontStretchProperty);
}
///
/// Gets the value of the attached on a control.
///
/// The control.
/// The foreground.
public static IBrush? GetForeground(Control control)
{
return control.GetValue(ForegroundProperty);
}
///
/// Sets the value of the attached on a control.
///
/// The control.
/// The property value to set.
public static void SetFontFamily(Control control, FontFamily value)
{
control.SetValue(FontFamilyProperty, value);
}
///
/// Sets the value of the attached on a control.
///
/// The control.
/// The property value to set.
public static void SetFontSize(Control control, double value)
{
control.SetValue(FontSizeProperty, value);
}
///
/// Sets the value of the attached on a control.
///
/// The control.
/// The property value to set.
public static void SetFontStyle(Control control, FontStyle value)
{
control.SetValue(FontStyleProperty, value);
}
///
/// Sets the value of the attached on a control.
///
/// The control.
/// The property value to set.
public static void SetFontWeight(Control control, FontWeight value)
{
control.SetValue(FontWeightProperty, value);
}
///
/// Sets the value of the attached on a control.
///
/// The control.
/// The property value to set.
public static void SetFontStretch(Control control, FontStretch value)
{
control.SetValue(FontStretchProperty, value);
}
///
/// Sets the value of the attached on a control.
///
/// The control.
/// The property value to set.
public static void SetForeground(Control control, IBrush? value)
{
control.SetValue(ForegroundProperty, value);
}
///
/// Renders the to a drawing context.
///
/// The drawing context.
public override void Render(DrawingContext context)
{
var background = Background;
if (background != null)
{
context.FillRectangle(background, new Rect(Bounds.Size));
}
var padding = Padding;
var top = padding.Top;
var textHeight = TextLayout.Bounds.Height;
if (Bounds.Height < textHeight)
{
switch (VerticalAlignment)
{
case VerticalAlignment.Center:
top += (Bounds.Height - textHeight) / 2;
break;
case VerticalAlignment.Bottom:
top += (Bounds.Height - textHeight);
break;
}
}
TextLayout.Draw(context, new Point(padding.Left, top));
}
///
/// Creates the used to render the text.
///
/// The constraint of the text.
/// The text to format.
/// A object.
protected virtual TextLayout CreateTextLayout(Size constraint, string? text)
{
List>? textStyleOverrides = null;
if (Inlines.HasComplexContent)
{
textStyleOverrides = new List>(Inlines.Count);
var textPosition = 0;
var stringBuilder = new StringBuilder();
foreach (var inline in Inlines)
{
textPosition += inline.BuildRun(stringBuilder, textStyleOverrides, textPosition);
}
text = stringBuilder.ToString();
}
return new TextLayout(
text ?? string.Empty,
new Typeface(FontFamily, FontStyle, FontWeight, FontStretch),
FontSize,
Foreground ?? Brushes.Transparent,
TextAlignment,
TextWrapping,
TextTrimming,
TextDecorations,
FlowDirection,
constraint.Width,
constraint.Height,
maxLines: MaxLines,
lineHeight: LineHeight,
textStyleOverrides: textStyleOverrides);
}
///
/// Invalidates .
///
protected void InvalidateTextLayout()
{
_textLayout = null;
InvalidateMeasure();
}
protected override Size MeasureOverride(Size availableSize)
{
if (!Inlines.HasComplexContent && string.IsNullOrEmpty(Text))
{
return new Size();
}
var padding = Padding;
_constraint = availableSize.Deflate(padding);
_textLayout = null;
InvalidateArrange();
var measuredSize = PixelSize.FromSize(TextLayout.Bounds.Size, 1);
return new Size(measuredSize.Width, measuredSize.Height).Inflate(padding);
}
protected override Size ArrangeOverride(Size finalSize)
{
if (MathUtilities.AreClose(_constraint.Width, finalSize.Width))
{
return finalSize;
}
_constraint = finalSize;
_textLayout = null;
return finalSize;
}
protected override AutomationPeer OnCreateAutomationPeer()
{
return new TextBlockAutomationPeer(this);
}
private static bool IsValidMaxLines(int maxLines) => maxLines >= 0;
private static bool IsValidLineHeight(double lineHeight) => double.IsNaN(lineHeight) || lineHeight > 0;
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
switch (change.Property.Name)
{
case nameof (FontSize):
case nameof (FontWeight):
case nameof (FontStyle):
case nameof (FontFamily):
case nameof (TextWrapping):
case nameof (TextTrimming):
case nameof (TextAlignment):
case nameof (FlowDirection):
case nameof (Padding):
case nameof (LineHeight):
case nameof (MaxLines):
case nameof (Text):
case nameof (TextDecorations):
case nameof (Foreground):
{
InvalidateTextLayout();
break;
}
}
}
private void InlinesChanged(object? sender, EventArgs e)
{
InvalidateTextLayout();
}
}
}