A cross-platform UI framework for .NET
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

215 lines
8.4 KiB

using Avalonia.Collections;
using Avalonia.Media.TextFormatting;
namespace Avalonia.Media
{
/// <summary>
/// Represents a text decoration, which is a visual ornamentation that is added to text (such as an underline).
/// </summary>
public class TextDecoration : AvaloniaObject
{
/// <summary>
/// Defines the <see cref="Location"/> property.
/// </summary>
public static readonly StyledProperty<TextDecorationLocation> LocationProperty =
AvaloniaProperty.Register<TextDecoration, TextDecorationLocation>(nameof(Location));
/// <summary>
/// Defines the <see cref="Stroke"/> property.
/// </summary>
public static readonly StyledProperty<IBrush> StrokeProperty =
AvaloniaProperty.Register<TextDecoration, IBrush>(nameof(Stroke));
/// <summary>
/// Defines the <see cref="StrokeThicknessUnit"/> property.
/// </summary>
public static readonly StyledProperty<TextDecorationUnit> StrokeThicknessUnitProperty =
AvaloniaProperty.Register<TextDecoration, TextDecorationUnit>(nameof(StrokeThicknessUnit));
/// <summary>
/// Defines the <see cref="StrokeDashArray"/> property.
/// </summary>
public static readonly StyledProperty<AvaloniaList<double>> StrokeDashArrayProperty =
AvaloniaProperty.Register<TextDecoration, AvaloniaList<double>>(nameof(StrokeDashArray));
/// <summary>
/// Defines the <see cref="StrokeDashOffset"/> property.
/// </summary>
public static readonly StyledProperty<double> StrokeDashOffsetProperty =
AvaloniaProperty.Register<TextDecoration, double>(nameof(StrokeDashOffset));
/// <summary>
/// Defines the <see cref="StrokeThickness"/> property.
/// </summary>
public static readonly StyledProperty<double> StrokeThicknessProperty =
AvaloniaProperty.Register<TextDecoration, double>(nameof(StrokeThickness), 1);
/// <summary>
/// Defines the <see cref="StrokeLineCap"/> property.
/// </summary>
public static readonly StyledProperty<PenLineCap> StrokeLineCapProperty =
AvaloniaProperty.Register<TextDecoration, PenLineCap>(nameof(StrokeLineCap));
/// <summary>
/// Defines the <see cref="StrokeOffset"/> property.
/// </summary>
public static readonly StyledProperty<double> StrokeOffsetProperty =
AvaloniaProperty.Register<TextDecoration, double>(nameof(StrokeOffset));
/// <summary>
/// Defines the <see cref="StrokeOffsetUnit"/> property.
/// </summary>
public static readonly StyledProperty<TextDecorationUnit> StrokeOffsetUnitProperty =
AvaloniaProperty.Register<TextDecoration, TextDecorationUnit>(nameof(StrokeOffsetUnit));
/// <summary>
/// Gets or sets the location.
/// </summary>
/// <value>
/// The location.
/// </value>
public TextDecorationLocation Location
{
get => GetValue(LocationProperty);
set => SetValue(LocationProperty, value);
}
/// <summary>
/// Gets or sets the <see cref="IBrush"/> that specifies how the <see cref="TextDecoration"/> is painted.
/// </summary>
public IBrush Stroke
{
get { return GetValue(StrokeProperty); }
set { SetValue(StrokeProperty, value); }
}
/// <summary>
/// Gets the units in which the thickness of the <see cref="TextDecoration"/> is expressed.
/// </summary>
public TextDecorationUnit StrokeThicknessUnit
{
get => GetValue(StrokeThicknessUnitProperty);
set => SetValue(StrokeThicknessUnitProperty, value);
}
/// <summary>
/// Gets or sets a collection of <see cref="double"/> values that indicate the pattern of dashes and gaps
/// that is used to draw the <see cref="TextDecoration"/>.
/// </summary>
public AvaloniaList<double> StrokeDashArray
{
get { return GetValue(StrokeDashArrayProperty); }
set { SetValue(StrokeDashArrayProperty, value); }
}
/// <summary>
/// Gets or sets a value that specifies the distance within the dash pattern where a dash begins.
/// </summary>
public double StrokeDashOffset
{
get { return GetValue(StrokeDashOffsetProperty); }
set { SetValue(StrokeDashOffsetProperty, value); }
}
/// <summary>
/// Gets or sets the thickness of the <see cref="TextDecoration"/>.
/// </summary>
public double StrokeThickness
{
get { return GetValue(StrokeThicknessProperty); }
set { SetValue(StrokeThicknessProperty, value); }
}
/// <summary>
/// Gets or sets a <see cref="PenLineCap"/> enumeration value that describes the shape at the ends of a line.
/// </summary>
public PenLineCap StrokeLineCap
{
get { return GetValue(StrokeLineCapProperty); }
set { SetValue(StrokeLineCapProperty, value); }
}
/// <summary>
/// The stroke's offset.
/// </summary>
/// <value>
/// The pen offset.
/// </value>
public double StrokeOffset
{
get => GetValue(StrokeOffsetProperty);
set => SetValue(StrokeOffsetProperty, value);
}
/// <summary>
/// Gets the units in which the <see cref="StrokeOffset"/> value is expressed.
/// </summary>
public TextDecorationUnit StrokeOffsetUnit
{
get => GetValue(StrokeOffsetUnitProperty);
set => SetValue(StrokeOffsetUnitProperty, value);
}
/// <summary>
/// Draws the <see cref="TextDecoration"/> at given origin.
/// </summary>
/// <param name="drawingContext">The drawing context.</param>
/// <param name="glyphRun">The decorated run.</param>
/// <param name="fontMetrics">The font metrics of the decorated run.</param>
/// <param name="defaultBrush">The default brush that is used to draw the decoration.</param>
internal void Draw(DrawingContext drawingContext, GlyphRun glyphRun, FontMetrics fontMetrics, IBrush defaultBrush)
{
var baselineOrigin = glyphRun.BaselineOrigin;
var thickness = StrokeThickness;
switch (StrokeThicknessUnit)
{
case TextDecorationUnit.FontRecommended:
switch (Location)
{
case TextDecorationLocation.Underline:
thickness = fontMetrics.UnderlineThickness;
break;
case TextDecorationLocation.Strikethrough:
thickness = fontMetrics.StrikethroughThickness;
break;
}
break;
case TextDecorationUnit.FontRenderingEmSize:
thickness = fontMetrics.FontRenderingEmSize * thickness;
break;
}
var origin = new Point();
switch (Location)
{
case TextDecorationLocation.Baseline:
origin += glyphRun.BaselineOrigin;
break;
case TextDecorationLocation.Strikethrough:
origin += new Point(baselineOrigin.X, baselineOrigin.Y + fontMetrics.StrikethroughPosition);
break;
case TextDecorationLocation.Underline:
origin += new Point(baselineOrigin.X, baselineOrigin.Y + fontMetrics.UnderlinePosition);
break;
}
switch (StrokeOffsetUnit)
{
case TextDecorationUnit.FontRenderingEmSize:
origin += new Point(0, StrokeOffset * fontMetrics.FontRenderingEmSize);
break;
case TextDecorationUnit.Pixel:
origin += new Point(0, StrokeOffset);
break;
}
var pen = new Pen(Stroke ?? defaultBrush, thickness,
new DashStyle(StrokeDashArray, StrokeDashOffset), StrokeLineCap);
drawingContext.DrawLine(pen, origin, origin + new Point(glyphRun.Metrics.Width, 0));
}
}
}