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;
}
}