Browse Source

Merge pull request #5042 from AvaloniaUI/update_geometry_segments_onpropchange

Update Path from PathSegments
pull/5075/head
Dan Walmsley 6 years ago
committed by GitHub
parent
commit
3ab8222475
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 72
      src/Avalonia.Visuals/Media/PathFigure.cs
  2. 20
      src/Avalonia.Visuals/Media/PathGeometry.cs
  3. 34
      tests/Avalonia.Visuals.UnitTests/Media/PathSegmentTests.cs

72
src/Avalonia.Visuals/Media/PathFigure.cs

@ -1,3 +1,7 @@
#nullable enable
using System;
using System.Linq;
using Avalonia.Collections;
using Avalonia.Metadata;
namespace Avalonia.Media
@ -8,22 +12,36 @@ namespace Avalonia.Media
/// Defines the <see cref="IsClosed"/> property.
/// </summary>
public static readonly StyledProperty<bool> IsClosedProperty
= AvaloniaProperty.Register<PathFigure, bool>(nameof(IsClosed), true);
= AvaloniaProperty.Register<PathFigure, bool>(nameof(IsClosed), true);
/// <summary>
/// Defines the <see cref="IsFilled"/> property.
/// </summary>
public static readonly StyledProperty<bool> IsFilledProperty
= AvaloniaProperty.Register<PathFigure, bool>(nameof(IsFilled), true);
= AvaloniaProperty.Register<PathFigure, bool>(nameof(IsFilled), true);
/// <summary>
/// Defines the <see cref="Segments"/> property.
/// </summary>
public static readonly DirectProperty<PathFigure, PathSegments> SegmentsProperty
= AvaloniaProperty.RegisterDirect<PathFigure, PathSegments>(nameof(Segments), f => f.Segments, (f, s) => f.Segments = s);
public static readonly DirectProperty<PathFigure, PathSegments?> SegmentsProperty
= AvaloniaProperty.RegisterDirect<PathFigure, PathSegments?>(
nameof(Segments),
f => f.Segments,
(f, s) => f.Segments = s);
/// <summary>
/// Defines the <see cref="StartPoint"/> property.
/// </summary>
public static readonly StyledProperty<Point> StartPointProperty
= AvaloniaProperty.Register<PathFigure, Point>(nameof(StartPoint));
= AvaloniaProperty.Register<PathFigure, Point>(nameof(StartPoint));
internal event EventHandler? SegmentsInvalidated;
private PathSegments? _segments;
private IDisposable? _segmentsDisposable;
private IDisposable? _segmentsPropertiesDisposable;
/// <summary>
/// Initializes a new instance of the <see cref="PathFigure"/> class.
@ -33,6 +51,31 @@ namespace Avalonia.Media
Segments = new PathSegments();
}
static PathFigure()
{
SegmentsProperty.Changed.AddClassHandler<PathFigure>(
(s, e) =>
s.OnSegmentsChanged());
}
private void OnSegmentsChanged()
{
_segmentsDisposable?.Dispose();
_segmentsPropertiesDisposable?.Dispose();
_segmentsDisposable = _segments?.ForEachItem(
_ => InvalidateSegments(),
_ => InvalidateSegments(),
InvalidateSegments);
_segmentsPropertiesDisposable = _segments?.TrackItemPropertyChanged(_ => InvalidateSegments());
}
private void InvalidateSegments()
{
SegmentsInvalidated?.Invoke(this, EventArgs.Empty);
}
/// <summary>
/// Gets or sets a value indicating whether this instance is closed.
/// </summary>
@ -64,7 +107,7 @@ namespace Avalonia.Media
/// The segments.
/// </value>
[Content]
public PathSegments Segments
public PathSegments? Segments
{
get { return _segments; }
set { SetAndRaise(SegmentsProperty, ref _segments, value); }
@ -81,22 +124,23 @@ namespace Avalonia.Media
get { return GetValue(StartPointProperty); }
set { SetValue(StartPointProperty, value); }
}
public override string ToString()
=> $"M {StartPoint} {string.Join(" ", _segments ?? Enumerable.Empty<PathSegment>())}{(IsClosed ? "Z" : "")}";
internal void ApplyTo(StreamGeometryContext ctx)
{
ctx.BeginFigure(StartPoint, IsFilled);
foreach (var segment in Segments)
if (Segments != null)
{
segment.ApplyTo(ctx);
foreach (var segment in Segments)
{
segment.ApplyTo(ctx);
}
}
ctx.EndFigure(IsClosed);
}
private PathSegments _segments;
public override string ToString()
=> $"M {StartPoint} {string.Join(" ", _segments)}{(IsClosed ? "Z" : "")}";
}
}
}

20
src/Avalonia.Visuals/Media/PathGeometry.cs

@ -104,12 +104,26 @@ namespace Avalonia.Media
_figuresPropertiesObserver?.Dispose();
_figuresObserver = figures?.ForEachItem(
_ => InvalidateGeometry(),
_ => InvalidateGeometry(),
() => InvalidateGeometry());
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()
=> $"{(FillRule != FillRule.EvenOdd ? "F1 " : "")}{(string.Join(" ", Figures))}";

34
tests/Avalonia.Visuals.UnitTests/Media/PathSegmentTests.cs

@ -0,0 +1,34 @@
using Avalonia.Media;
using Xunit;
namespace Avalonia.Visuals.UnitTests.Media
{
public class PathSegmentTests
{
[Fact]
public void PathSegment_Triggers_Invalidation_On_Property_Change()
{
var targetSegment = new ArcSegment()
{
Size = new Size(10, 10),
Point = new Point(5, 5)
};
var target = new PathGeometry
{
Figures = new PathFigures
{
new PathFigure { IsClosed = false, Segments = new PathSegments { targetSegment } }
}
};
var changed = false;
target.Changed += (s, e) => changed = true;
targetSegment.Size = new Size(20, 20);
Assert.True(changed);
}
}
}
Loading…
Cancel
Save