using System.Reactive.Linq; using Avalonia.LogicalTree; using Avalonia.Media; using Avalonia.Media.TextFormatting; using Avalonia.Metadata; 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 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 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 string _text; private TextLayout _textLayout; private Size _constraint; /// /// Initializes static members of the class. /// static TextBlock() { ClipToBoundsProperty.OverrideDefaultValue(true); AffectsRender(BackgroundProperty, ForegroundProperty, TextAlignmentProperty, TextDecorationsProperty); AffectsMeasure(FontSizeProperty, FontWeightProperty, FontStyleProperty, TextWrappingProperty, FontFamilyProperty, TextTrimmingProperty, TextProperty, PaddingProperty, LineHeightProperty, MaxLinesProperty); Observable.Merge(TextProperty.Changed, ForegroundProperty.Changed, TextAlignmentProperty.Changed, TextWrappingProperty.Changed, TextTrimmingProperty.Changed, FontSizeProperty.Changed, FontStyleProperty.Changed, FontWeightProperty.Changed, FontFamilyProperty.Changed, TextDecorationsProperty.Changed, PaddingProperty.Changed, MaxLinesProperty.Changed, LineHeightProperty.Changed ).AddClassHandler((x, _) => x.InvalidateTextLayout()); } /// /// Initializes a new instance of the class. /// public TextBlock() { _text = string.Empty; } /// /// 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. /// [Content] public string Text { get { return _text; } set { SetAndRaise(TextProperty, ref _text, value); } } /// /// 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 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 family. public static double GetFontSize(Control control) { return control.GetValue(FontSizeProperty); } /// /// Gets the value of the attached on a control. /// /// The control. /// The font family. public static FontStyle GetFontStyle(Control control) { return control.GetValue(FontStyleProperty); } /// /// Gets the value of the attached on a control. /// /// The control. /// The font family. public static FontWeight GetFontWeight(Control control) { return control.GetValue(FontWeightProperty); } /// /// 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. /// The font family. 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. /// The font family. 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. /// The font family. 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. /// The font family. 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. /// The font family. 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)); } if (TextLayout is null) { return; } var textAlignment = TextAlignment; var width = Bounds.Size.Width; var offsetX = 0.0; switch (textAlignment) { case TextAlignment.Center: offsetX = (width - TextLayout.Size.Width) / 2; break; case TextAlignment.Right: offsetX = width - TextLayout.Size.Width; break; } var padding = Padding; using (context.PushPostTransform(Matrix.CreateTranslation(padding.Left + offsetX, padding.Top))) { TextLayout.Draw(context); } } /// /// 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) { if (constraint == Size.Empty) { return null; } return new TextLayout( text ?? string.Empty, new Typeface(FontFamily, FontStyle, FontWeight), FontSize, Foreground, TextAlignment, TextWrapping, TextTrimming, TextDecorations, constraint.Width, constraint.Height, maxLines: MaxLines, lineHeight: LineHeight); } /// /// Invalidates . /// protected void InvalidateTextLayout() { _textLayout = null; } /// /// Measures the control. /// /// The available size for the control. /// The desired size. protected override Size MeasureOverride(Size availableSize) { if (string.IsNullOrEmpty(Text)) { return new Size(); } var padding = Padding; availableSize = availableSize.Deflate(padding); if (_constraint != availableSize) { _constraint = availableSize; InvalidateTextLayout(); } var measuredSize = TextLayout?.Size ?? Size.Empty; return measuredSize.Inflate(padding); } protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e) { base.OnAttachedToLogicalTree(e); InvalidateTextLayout(); InvalidateMeasure(); } private static bool IsValidMaxLines(int maxLines) => maxLines >= 0; private static bool IsValidLineHeight(double lineHeight) => double.IsNaN(lineHeight) || lineHeight > 0; } }