Browse Source

Make Shapes update their geometry.

When a property that affects the Shape's geometry is changed, make sure
the DefiningGeometry gets updated.
Steven Kirk 9 years ago
parent
commit
28b6de1019

+ 8 - 15
src/Perspex.Controls/Shapes/Ellipse.cs

@@ -7,23 +7,16 @@ namespace Perspex.Controls.Shapes
 {
     public class Ellipse : Shape
     {
-        private Geometry _geometry;
-
-        private Size _geometrySize;
-
-        public override Geometry DefiningGeometry
+        static Ellipse()
         {
-            get
-            {
-                if (_geometry == null || _geometrySize != Bounds.Size)
-                {
-                    var rect = new Rect(Bounds.Size).Deflate(StrokeThickness);
-                    _geometry = new EllipseGeometry(rect);
-                    _geometrySize = Bounds.Size;
-                }
+            AffectsGeometry(BoundsProperty);
+            AffectsGeometry(StrokeThicknessProperty);
+        }
 
-                return _geometry;
-            }
+        protected override Geometry CreateDefiningGeometry()
+        {
+            var rect = new Rect(Bounds.Size).Deflate(StrokeThickness);
+            return new EllipseGeometry(rect);
         }
 
         protected override Size MeasureOverride(Size availableSize)

+ 4 - 21
src/Perspex.Controls/Shapes/Line.cs

@@ -1,11 +1,6 @@
 // Copyright (c) The Perspex Project. All rights reserved.
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 using Perspex.Media;
 
 namespace Perspex.Controls.Shapes
@@ -18,13 +13,11 @@ namespace Perspex.Controls.Shapes
         public static readonly StyledProperty<Point> EndPointProperty =
             PerspexProperty.Register<Line, Point>("EndPoint");
 
-        private LineGeometry _geometry;
-        private Point _startPoint;
-        private Point _endPoint;
-
         static Line()
         {
             StrokeThicknessProperty.OverrideDefaultValue<Line>(1);
+            AffectsGeometry(StartPointProperty);
+            AffectsGeometry(EndPointProperty);
         }
 
         public Point StartPoint
@@ -39,19 +32,9 @@ namespace Perspex.Controls.Shapes
             set { SetValue(EndPointProperty, value); }
         }
 
-        public override Geometry DefiningGeometry
+        protected override Geometry CreateDefiningGeometry()
         {
-            get
-            {
-                if (_geometry == null || StartPoint != _startPoint || EndPoint != _endPoint)
-                {
-                    _startPoint = StartPoint;
-                    _endPoint = EndPoint;
-                    _geometry = new LineGeometry(_startPoint, _endPoint);
-                }
-
-                return _geometry;
-            }
+            return new LineGeometry(StartPoint, EndPoint);
         }
     }
 }

+ 6 - 1
src/Perspex.Controls/Shapes/Path.cs

@@ -11,12 +11,17 @@ namespace Perspex.Controls.Shapes
         public static readonly StyledProperty<Geometry> DataProperty =
             PerspexProperty.Register<Path, Geometry>("Data");
 
+        static Path()
+        {
+            AffectsGeometry(DataProperty);
+        }
+
         public Geometry Data
         {
             get { return GetValue(DataProperty); }
             set { SetValue(DataProperty, value); }
         }
 
-        public override Geometry DefiningGeometry => Data;
+        protected override Geometry CreateDefiningGeometry() => Data;
     }
 }

+ 3 - 19
src/Perspex.Controls/Shapes/Polygon.cs

@@ -11,24 +11,9 @@ namespace Perspex.Controls.Shapes
         public static readonly StyledProperty<IList<Point>> PointsProperty =
             PerspexProperty.Register<Polygon, IList<Point>>("Points");
 
-        private Geometry _geometry;
-
         static Polygon()
         {
-            PointsProperty.Changed.AddClassHandler<Polygon>(x => x.PointsChanged);
-        }
-
-        public override Geometry DefiningGeometry
-        {
-            get
-            {
-                if (_geometry == null)
-                {
-                    _geometry = new PolylineGeometry(Points, true);
-                }
-
-                return _geometry;
-            }
+            AffectsGeometry(PointsProperty);
         }
 
         public IList<Point> Points
@@ -37,10 +22,9 @@ namespace Perspex.Controls.Shapes
             set { SetValue(PointsProperty, value); }
         }
 
-        private void PointsChanged(PerspexPropertyChangedEventArgs e)
+        protected override Geometry CreateDefiningGeometry()
         {
-            _geometry = null;
-            InvalidateMeasure();
+            return new PolylineGeometry(Points, true);
         }
     }
 }

+ 3 - 19
src/Perspex.Controls/Shapes/Polyline.cs

@@ -11,25 +11,10 @@ namespace Perspex.Controls.Shapes
         public static readonly StyledProperty<IList<Point>> PointsProperty =
             PerspexProperty.Register<Polyline, IList<Point>>("Points");
 
-        private Geometry _geometry;
-
         static Polyline()
         {
             StrokeThicknessProperty.OverrideDefaultValue<Polyline>(1);
-            PointsProperty.Changed.AddClassHandler<Polyline>(x => x.PointsChanged);
-        }
-
-        public override Geometry DefiningGeometry
-        {
-            get
-            {
-                if (_geometry == null)
-                {
-                    _geometry = new PolylineGeometry(Points, false);
-                }
-
-                return _geometry;
-            }
+            AffectsGeometry(PointsProperty);
         }
 
         public IList<Point> Points
@@ -38,10 +23,9 @@ namespace Perspex.Controls.Shapes
             set { SetValue(PointsProperty, value); }
         }
 
-        private void PointsChanged(PerspexPropertyChangedEventArgs e)
+        protected override Geometry CreateDefiningGeometry()
         {
-            _geometry = null;
-            InvalidateMeasure();
+            return new PolylineGeometry(Points, false);
         }
     }
 }

+ 8 - 15
src/Perspex.Controls/Shapes/Rectangle.cs

@@ -7,23 +7,16 @@ namespace Perspex.Controls.Shapes
 {
     public class Rectangle : Shape
     {
-        private Geometry _geometry;
-
-        private Size _geometrySize;
-
-        public override Geometry DefiningGeometry
+        static Rectangle()
         {
-            get
-            {
-                if (_geometry == null || _geometrySize != Bounds.Size)
-                {
-                    var rect = new Rect(Bounds.Size).Deflate(StrokeThickness);
-                    _geometry = new RectangleGeometry(rect);
-                    _geometrySize = Bounds.Size;
-                }
+            AffectsGeometry(BoundsProperty);
+            AffectsGeometry(StrokeThicknessProperty);
+        }
 
-                return _geometry;
-            }
+        protected override Geometry CreateDefiningGeometry()
+        {
+            var rect = new Rect(Bounds.Size).Deflate(StrokeThickness);
+            return new RectangleGeometry(rect);
         }
 
         protected override Size MeasureOverride(Size availableSize)

+ 56 - 3
src/Perspex.Controls/Shapes/Shape.cs

@@ -26,7 +26,7 @@ namespace Perspex.Controls.Shapes
             PerspexProperty.Register<Shape, double>("StrokeThickness");
 
         private Matrix _transform = Matrix.Identity;
-
+        private Geometry _definingGeometry;
         private Geometry _renderedGeometry;
 
         static Shape()
@@ -38,9 +38,17 @@ namespace Perspex.Controls.Shapes
             AffectsMeasure(StrokeThicknessProperty);
         }
 
-        public abstract Geometry DefiningGeometry
+        public Geometry DefiningGeometry
         {
-            get;
+            get
+            {
+                if (_definingGeometry == null)
+                {
+                    _definingGeometry = CreateDefiningGeometry();
+                }
+
+                return _definingGeometry;
+            }
         }
 
         public Brush Fill
@@ -110,6 +118,28 @@ namespace Perspex.Controls.Shapes
             }
         }
 
+        /// <summary>
+        /// Marks a property as affecting the shape's geometry.
+        /// </summary>
+        /// <param name="property">The property.</param>
+        /// <remarks>
+        /// After a call to this method in a control's static constructor, any change to the
+        /// property will cause <see cref="InvalidateGeometry"/> to be called on the element.
+        /// </remarks>
+        protected static void AffectsGeometry(PerspexProperty property)
+        {
+            property.Changed.Subscribe(AffectsGeometryInvalidate);
+        }
+
+        protected abstract Geometry CreateDefiningGeometry();
+
+        protected void InvalidateGeometry()
+        {
+            this._renderedGeometry = null;
+            this._definingGeometry = null;
+            InvalidateMeasure();
+        }
+
         protected override Size MeasureOverride(Size availableSize)
         {
             // This should probably use GetRenderBounds(strokeThickness) but then the calculations
@@ -195,5 +225,28 @@ namespace Perspex.Controls.Shapes
 
             return new Size(shapeSize.Width * sx, shapeSize.Height * sy);
         }
+
+        private static void AffectsGeometryInvalidate(PerspexPropertyChangedEventArgs e)
+        {
+            var control = e.Sender as Shape;
+
+            if (control != null)
+            {
+                // If the geometry is invalidated when Bounds changes, only invalidate when the Size
+                // portion changes.
+                if (e.Property == BoundsProperty)
+                {
+                    var oldBounds = (Rect)e.OldValue;
+                    var newBounds = (Rect)e.NewValue;
+
+                    if (oldBounds.Size == newBounds.Size)
+                    {
+                        return;
+                    }
+                }
+
+                control.InvalidateGeometry();
+            }
+        }
     }
 }