Browse Source

Allow Geometry classes to be instantiated in XAML

pull/1117/head
Eli Arbel 9 years ago
parent
commit
f05cb39a36
  1. 51
      src/Avalonia.Visuals/Media/EllipseGeometry.cs
  2. 80
      src/Avalonia.Visuals/Media/LineGeometry.cs
  3. 119
      src/Avalonia.Visuals/Media/PolylineGeometry.cs
  4. 51
      src/Avalonia.Visuals/Media/RectangleGeometry.cs
  5. 9
      src/Avalonia.Visuals/Points.cs

51
src/Avalonia.Visuals/Media/EllipseGeometry.cs

@ -11,16 +11,51 @@ namespace Avalonia.Media
/// </summary>
public class EllipseGeometry : Geometry
{
/// <summary>
/// Defines the <see cref="Rect"/> property.
/// </summary>
public static readonly StyledProperty<Rect> RectProperty =
AvaloniaProperty.Register<EllipseGeometry, Rect>(nameof(Rect));
public Rect Rect
{
get => GetValue(RectProperty);
set => SetValue(RectProperty, value);
}
static EllipseGeometry()
{
RectProperty.Changed.AddClassHandler<EllipseGeometry>(x => x.RectChanged);
}
/// <summary>
/// Initializes a new instance of the <see cref="EllipseGeometry"/> class.
/// </summary>
/// <param name="rect">The rectangle that the ellipse should fill.</param>
public EllipseGeometry(Rect rect)
public EllipseGeometry()
{
IPlatformRenderInterface factory = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>();
IStreamGeometryImpl impl = factory.CreateStreamGeometry();
PlatformImpl = factory.CreateStreamGeometry();
}
/// <summary>
/// Initializes a new instance of the <see cref="EllipseGeometry"/> class.
/// </summary>
/// <param name="rect">The rectangle that the ellipse should fill.</param>
public EllipseGeometry(Rect rect) : this()
{
Rect = rect;
}
/// <inheritdoc/>
public override Geometry Clone()
{
return new EllipseGeometry(Rect);
}
using (IStreamGeometryContextImpl ctx = impl.Open())
private void RectChanged(AvaloniaPropertyChangedEventArgs e)
{
var rect = (Rect)e.NewValue;
using (var ctx = ((IStreamGeometryImpl)PlatformImpl).Open())
{
double controlPointRatio = (Math.Sqrt(2) - 1) * 4 / 3;
var center = rect.Center;
@ -45,14 +80,6 @@ namespace Avalonia.Media
ctx.CubicBezierTo(new Point(x0, y1), new Point(x1, y0), new Point(x2, y0));
ctx.EndFigure(true);
}
PlatformImpl = impl;
}
/// <inheritdoc/>
public override Geometry Clone()
{
return new EllipseGeometry(Bounds);
}
}
}

80
src/Avalonia.Visuals/Media/LineGeometry.cs

@ -10,35 +10,89 @@ namespace Avalonia.Media
/// </summary>
public class LineGeometry : Geometry
{
private Point _startPoint;
private Point _endPoint;
/// <summary>
/// Defines the <see cref="StartPoint"/> property.
/// </summary>
public static readonly StyledProperty<Point> StartPointProperty =
AvaloniaProperty.Register<LineGeometry, Point>(nameof(StartPoint));
public Point StartPoint
{
get => GetValue(StartPointProperty);
set => SetValue(StartPointProperty, value);
}
/// <summary>
/// Defines the <see cref="EndPoint"/> property.
/// </summary>
public static readonly StyledProperty<Point> EndPointProperty =
AvaloniaProperty.Register<LineGeometry, Point>(nameof(EndPoint));
private bool _isDirty;
public Point EndPoint
{
get => GetValue(EndPointProperty);
set => SetValue(EndPointProperty, value);
}
static LineGeometry()
{
StartPointProperty.Changed.AddClassHandler<LineGeometry>(x => x.PointsChanged);
EndPointProperty.Changed.AddClassHandler<LineGeometry>(x => x.PointsChanged);
}
/// <summary>
/// Initializes a new instance of the <see cref="LineGeometry"/> class.
/// </summary>
public LineGeometry()
{
IPlatformRenderInterface factory = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>();
PlatformImpl = factory.CreateStreamGeometry();
}
/// <summary>
/// Initializes a new instance of the <see cref="LineGeometry"/> class.
/// </summary>
/// <param name="startPoint">The start point.</param>
/// <param name="endPoint">The end point.</param>
public LineGeometry(Point startPoint, Point endPoint)
public LineGeometry(Point startPoint, Point endPoint) : this()
{
_startPoint = startPoint;
_endPoint = endPoint;
IPlatformRenderInterface factory = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>();
IStreamGeometryImpl impl = factory.CreateStreamGeometry();
StartPoint = startPoint;
EndPoint = endPoint;
}
using (IStreamGeometryContextImpl context = impl.Open())
public override IGeometryImpl PlatformImpl
{
get
{
context.BeginFigure(_startPoint, false);
context.LineTo(_endPoint);
context.EndFigure(false);
PrepareIfNeeded();
return base.PlatformImpl;
}
protected set => base.PlatformImpl = value;
}
PlatformImpl = impl;
public void PrepareIfNeeded()
{
if (_isDirty)
{
_isDirty = false;
using (var context = ((IStreamGeometryImpl)PlatformImpl).Open())
{
context.BeginFigure(StartPoint, false);
context.LineTo(EndPoint);
context.EndFigure(false);
}
}
}
/// <inheritdoc/>
public override Geometry Clone()
{
return new LineGeometry(_startPoint, _endPoint);
PrepareIfNeeded();
return new LineGeometry(StartPoint, EndPoint);
}
private void PointsChanged(AvaloniaPropertyChangedEventArgs e) => _isDirty = true;
}
}

119
src/Avalonia.Visuals/Media/PolylineGeometry.cs

@ -3,10 +3,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Avalonia.Platform;
using Avalonia.Metadata;
using Avalonia.Collections;
namespace Avalonia.Media
{
@ -15,36 +14,122 @@ namespace Avalonia.Media
/// </summary>
public class PolylineGeometry : Geometry
{
private IList<Point> _points;
private bool _isFilled;
/// <summary>
/// Defines the <see cref="Points"/> property.
/// </summary>
public static readonly DirectProperty<PolylineGeometry, Points> PointsProperty =
AvaloniaProperty.RegisterDirect<PolylineGeometry, Points>(nameof(Points), g => g.Points, (g, f) => g.Points = f);
public PolylineGeometry(IList<Point> points, bool isFilled)
/// <summary>
/// Defines the <see cref="IsFilled"/> property.
/// </summary>
public static readonly AvaloniaProperty<bool> IsFilledProperty =
AvaloniaProperty.Register<PolylineGeometry, bool>(nameof(IsFilled));
static PolylineGeometry()
{
PointsProperty.Changed.Subscribe(onNext: v =>
{
(v.Sender as PolylineGeometry)?.OnPointsChanged(v.OldValue as Points, v.NewValue as Points);
});
IsFilledProperty.Changed.AddClassHandler<PolylineGeometry>(x => a => x.NotifyChanged());
}
/// <summary>
/// Initializes a new instance of the <see cref="PolylineGeometry"/> class.
/// </summary>
public PolylineGeometry()
{
_points = points;
_isFilled = isFilled;
IPlatformRenderInterface factory = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>();
IStreamGeometryImpl impl = factory.CreateStreamGeometry();
PlatformImpl = factory.CreateStreamGeometry();
Points = new Points();
}
/// <summary>
/// Initializes a new instance of the <see cref="PolylineGeometry"/> class.
/// </summary>
public PolylineGeometry(IEnumerable<Point> points, bool isFilled) : this()
{
Points.AddRange(points);
IsFilled = isFilled;
PrepareIfNeeded();
}
using (IStreamGeometryContextImpl context = impl.Open())
public void PrepareIfNeeded()
{
if (_isDirty)
{
if (points.Count > 0)
_isDirty = false;
using (var context = ((IStreamGeometryImpl)PlatformImpl).Open())
{
context.BeginFigure(points[0], isFilled);
for (int i = 1; i < points.Count; i++)
var points = Points;
var isFilled = IsFilled;
if (points.Count > 0)
{
context.LineTo(points[i]);
context.BeginFigure(points[0], isFilled);
for (int i = 1; i < points.Count; i++)
{
context.LineTo(points[i]);
}
context.EndFigure(isFilled);
}
context.EndFigure(isFilled);
}
}
}
PlatformImpl = impl;
/// <summary>
/// Gets or sets the figures.
/// </summary>
/// <value>
/// The points.
/// </value>
[Content]
public Points Points
{
get => _points;
set => SetAndRaise(PointsProperty, ref _points, value);
}
public bool IsFilled
{
get => GetValue(IsFilledProperty);
set => SetValue(IsFilledProperty, value);
}
public override IGeometryImpl PlatformImpl
{
get
{
PrepareIfNeeded();
return base.PlatformImpl;
}
protected set => base.PlatformImpl = value;
}
private Points _points;
private bool _isDirty;
private IDisposable _pointsObserver;
/// <inheritdoc/>
public override Geometry Clone()
{
return new PolylineGeometry(new List<Point>(_points), _isFilled);
PrepareIfNeeded();
return new PolylineGeometry(Points, IsFilled);
}
private void OnPointsChanged(Points oldValue, Points newValue)
{
_pointsObserver?.Dispose();
_pointsObserver = newValue?.ForEachItem(f => NotifyChanged(), f => NotifyChanged(), () => NotifyChanged());
}
internal void NotifyChanged()
{
_isDirty = true;
}
}
}

51
src/Avalonia.Visuals/Media/RectangleGeometry.cs

@ -10,16 +10,51 @@ namespace Avalonia.Media
/// </summary>
public class RectangleGeometry : Geometry
{
/// <summary>
/// Defines the <see cref="Rect"/> property.
/// </summary>
public static readonly StyledProperty<Rect> RectProperty =
AvaloniaProperty.Register<RectangleGeometry, Rect>(nameof(Rect));
public Rect Rect
{
get => GetValue(RectProperty);
set => SetValue(RectProperty, value);
}
static RectangleGeometry()
{
RectProperty.Changed.AddClassHandler<RectangleGeometry>(x => x.RectChanged);
}
/// <summary>
/// Initializes a new instance of the <see cref="RectangleGeometry"/> class.
/// </summary>
/// <param name="rect">The rectangle bounds.</param>
public RectangleGeometry(Rect rect)
public RectangleGeometry()
{
IPlatformRenderInterface factory = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>();
IStreamGeometryImpl impl = factory.CreateStreamGeometry();
PlatformImpl = factory.CreateStreamGeometry();
}
/// <summary>
/// Initializes a new instance of the <see cref="RectangleGeometry"/> class.
/// </summary>
/// <param name="rect">The rectangle bounds.</param>
public RectangleGeometry(Rect rect) : this()
{
Rect = rect;
}
/// <inheritdoc/>
public override Geometry Clone()
{
return new RectangleGeometry(Rect);
}
using (IStreamGeometryContextImpl context = impl.Open())
private void RectChanged(AvaloniaPropertyChangedEventArgs e)
{
var rect = (Rect)e.NewValue;
using (var context = ((IStreamGeometryImpl)PlatformImpl).Open())
{
context.BeginFigure(rect.TopLeft, true);
context.LineTo(rect.TopRight);
@ -27,14 +62,6 @@ namespace Avalonia.Media
context.LineTo(rect.BottomLeft);
context.EndFigure(true);
}
PlatformImpl = impl;
}
/// <inheritdoc/>
public override Geometry Clone()
{
return new RectangleGeometry(Bounds);
}
}
}

9
src/Avalonia.Visuals/Points.cs

@ -0,0 +1,9 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Avalonia.Collections;
namespace Avalonia
{
public sealed class Points : AvaloniaList<Point> { }
}
Loading…
Cancel
Save