csharpc-sharpdotnetxamlavaloniauicross-platformcross-platform-xamlavaloniaguimulti-platformuser-interfacedotnetcore
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.
140 lines
4.3 KiB
140 lines
4.3 KiB
using System;
|
|
using Avalonia.Collections;
|
|
using Avalonia.Metadata;
|
|
using Avalonia.Platform;
|
|
using Avalonia.Visuals.Platform;
|
|
|
|
namespace Avalonia.Media
|
|
{
|
|
public class PathGeometry : StreamGeometry
|
|
{
|
|
/// <summary>
|
|
/// Defines the <see cref="Figures"/> property.
|
|
/// </summary>
|
|
public static readonly DirectProperty<PathGeometry, PathFigures?> FiguresProperty =
|
|
AvaloniaProperty.RegisterDirect<PathGeometry, PathFigures?>(nameof(Figures), g => g.Figures, (g, f) => g.Figures = f);
|
|
|
|
/// <summary>
|
|
/// Defines the <see cref="FillRule"/> property.
|
|
/// </summary>
|
|
public static readonly StyledProperty<FillRule> FillRuleProperty =
|
|
AvaloniaProperty.Register<PathGeometry, FillRule>(nameof(FillRule));
|
|
|
|
private PathFigures? _figures;
|
|
private IDisposable? _figuresObserver;
|
|
private IDisposable? _figuresPropertiesObserver;
|
|
|
|
static PathGeometry()
|
|
{
|
|
FiguresProperty.Changed.AddClassHandler<PathGeometry>((s, e) =>
|
|
s.OnFiguresChanged(e.NewValue as PathFigures));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="PathGeometry"/> class.
|
|
/// </summary>
|
|
public PathGeometry()
|
|
{
|
|
_figures = new PathFigures();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parses the specified path data to a <see cref="PathGeometry"/>.
|
|
/// </summary>
|
|
/// <param name="pathData">The s.</param>
|
|
/// <returns></returns>
|
|
public static new PathGeometry Parse(string pathData)
|
|
{
|
|
var pathGeometry = new PathGeometry();
|
|
|
|
using (var context = new PathGeometryContext(pathGeometry))
|
|
using (var parser = new PathMarkupParser(context))
|
|
{
|
|
parser.Parse(pathData);
|
|
}
|
|
|
|
return pathGeometry;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the figures.
|
|
/// </summary>
|
|
/// <value>
|
|
/// The figures.
|
|
/// </value>
|
|
[Content]
|
|
public PathFigures? Figures
|
|
{
|
|
get { return _figures; }
|
|
set { SetAndRaise(FiguresProperty, ref _figures, value); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the fill rule.
|
|
/// </summary>
|
|
/// <value>
|
|
/// The fill rule.
|
|
/// </value>
|
|
public FillRule FillRule
|
|
{
|
|
get { return GetValue(FillRuleProperty); }
|
|
set { SetValue(FillRuleProperty, value); }
|
|
}
|
|
|
|
protected override IGeometryImpl? CreateDefiningGeometry()
|
|
{
|
|
var figures = Figures;
|
|
|
|
if (figures is null)
|
|
return null;
|
|
|
|
var factory = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>() ??
|
|
throw new InvalidOperationException("Unable to locate IPlatformRenderInterface.");
|
|
var geometry = factory.CreateStreamGeometry();
|
|
|
|
using (var ctx = new StreamGeometryContext(geometry.Open()))
|
|
{
|
|
ctx.SetFillRule(FillRule);
|
|
foreach (var f in figures)
|
|
{
|
|
f.ApplyTo(ctx);
|
|
}
|
|
}
|
|
|
|
return geometry;
|
|
}
|
|
|
|
private void OnFiguresChanged(PathFigures? figures)
|
|
{
|
|
_figuresObserver?.Dispose();
|
|
_figuresPropertiesObserver?.Dispose();
|
|
|
|
_figuresObserver = figures?.ForEachItem(
|
|
s =>
|
|
{
|
|
s.SegmentsInvalidated += InvalidateGeometryFromSegments;
|
|
InvalidateGeometry();
|
|
},
|
|
s =>
|
|
{
|
|
s.SegmentsInvalidated -= InvalidateGeometryFromSegments;
|
|
InvalidateGeometry();
|
|
},
|
|
InvalidateGeometry);
|
|
|
|
_figuresPropertiesObserver = figures?.TrackItemPropertyChanged(_ => InvalidateGeometry());
|
|
|
|
}
|
|
|
|
private void InvalidateGeometryFromSegments(object? _, EventArgs __)
|
|
{
|
|
InvalidateGeometry();
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
var figuresString = _figures is not null ? string.Join(" ", _figures) : string.Empty;
|
|
return $"{(FillRule != FillRule.EvenOdd ? "F1 " : "")}{figuresString}";
|
|
}
|
|
}
|
|
}
|
|
|