From ac0c9e69cd79b97e96de4cb620231ca30c68cc44 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 12 Sep 2018 00:38:58 +0200 Subject: [PATCH] Added IAffectsRender interface... ...and use it as a general-purpose means of attaching to property values that can affect render. --- src/Avalonia.Controls/Border.cs | 7 ++-- src/Avalonia.Controls/Panel.cs | 2 +- .../Presenters/ContentPresenter.cs | 3 +- src/Avalonia.Controls/Shapes/Shape.cs | 3 +- src/Avalonia.Controls/TextBlock.cs | 3 +- src/Avalonia.Visuals/Media/Brush.cs | 10 +++--- src/Avalonia.Visuals/Media/GradientBrush.cs | 6 ++-- src/Avalonia.Visuals/Media/IAffectsRender.cs | 16 +++++++++ src/Avalonia.Visuals/Media/IMutableBrush.cs | 7 +--- src/Avalonia.Visuals/Visual.cs | 35 +++---------------- .../Media/ImageBrushTests.cs | 4 +-- .../Media/LinearGradientBrushTests.cs | 20 +++++------ .../Media/SolidColorBrushTests.cs | 4 +-- 13 files changed, 54 insertions(+), 66 deletions(-) create mode 100644 src/Avalonia.Visuals/Media/IAffectsRender.cs diff --git a/src/Avalonia.Controls/Border.cs b/src/Avalonia.Controls/Border.cs index cd5d3952e7..0edb272015 100644 --- a/src/Avalonia.Controls/Border.cs +++ b/src/Avalonia.Controls/Border.cs @@ -43,9 +43,12 @@ namespace Avalonia.Controls /// static Border() { - AffectsRender(BorderThicknessProperty, CornerRadiusProperty); + AffectsRender( + BackgroundProperty, + BorderBrushProperty, + BorderThicknessProperty, + CornerRadiusProperty); AffectsMeasure(BorderThicknessProperty); - BrushAffectsRender(BackgroundProperty, BorderBrushProperty); } /// diff --git a/src/Avalonia.Controls/Panel.cs b/src/Avalonia.Controls/Panel.cs index 9b768749df..5415f3974d 100644 --- a/src/Avalonia.Controls/Panel.cs +++ b/src/Avalonia.Controls/Panel.cs @@ -30,7 +30,7 @@ namespace Avalonia.Controls /// static Panel() { - BrushAffectsRender(BackgroundProperty); + AffectsRender(BackgroundProperty); ClipToBoundsProperty.OverrideDefaultValue(true); } diff --git a/src/Avalonia.Controls/Presenters/ContentPresenter.cs b/src/Avalonia.Controls/Presenters/ContentPresenter.cs index a8019fc306..8d703cfc1c 100644 --- a/src/Avalonia.Controls/Presenters/ContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ContentPresenter.cs @@ -90,9 +90,8 @@ namespace Avalonia.Controls.Presenters /// static ContentPresenter() { - AffectsRender(BorderThicknessProperty, CornerRadiusProperty); + AffectsRender(BackgroundProperty, BorderBrushProperty, BorderThicknessProperty, CornerRadiusProperty); AffectsMeasure(BorderThicknessProperty, PaddingProperty); - BrushAffectsRender(BackgroundProperty, BorderBrushProperty); ContentProperty.Changed.AddClassHandler(x => x.ContentChanged); ContentTemplateProperty.Changed.AddClassHandler(x => x.ContentChanged); TemplatedParentProperty.Changed.AddClassHandler(x => x.TemplatedParentChanged); diff --git a/src/Avalonia.Controls/Shapes/Shape.cs b/src/Avalonia.Controls/Shapes/Shape.cs index 7ca0867031..f77c43acd0 100644 --- a/src/Avalonia.Controls/Shapes/Shape.cs +++ b/src/Avalonia.Controls/Shapes/Shape.cs @@ -33,8 +33,7 @@ namespace Avalonia.Controls.Shapes static Shape() { AffectsMeasure(StretchProperty, StrokeThicknessProperty); - AffectsRender(StrokeDashArrayProperty); - BrushAffectsRender(FillProperty, StrokeProperty); + AffectsRender(FillProperty, StrokeProperty, StrokeDashArrayProperty); } public Geometry DefiningGeometry diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs index 5a98ef3ecc..af7b0f835e 100644 --- a/src/Avalonia.Controls/TextBlock.cs +++ b/src/Avalonia.Controls/TextBlock.cs @@ -101,10 +101,11 @@ namespace Avalonia.Controls { ClipToBoundsProperty.OverrideDefaultValue(true); AffectsRender( + BackgroundProperty, + ForegroundProperty, FontWeightProperty, FontSizeProperty, FontStyleProperty); - BrushAffectsRender(BackgroundProperty, ForegroundProperty); } /// diff --git a/src/Avalonia.Visuals/Media/Brush.cs b/src/Avalonia.Visuals/Media/Brush.cs index 8ba7c1be04..c2c041f073 100644 --- a/src/Avalonia.Visuals/Media/Brush.cs +++ b/src/Avalonia.Visuals/Media/Brush.cs @@ -19,7 +19,7 @@ namespace Avalonia.Media AvaloniaProperty.Register(nameof(Opacity), 1.0); /// - public event EventHandler Changed; + public event EventHandler Invalidated; /// /// Gets or sets the opacity of the brush. @@ -63,14 +63,14 @@ namespace Avalonia.Media /// The properties. /// /// After a call to this method in a brush's static constructor, any change to the - /// property will cause the event to be raised on the brush. + /// property will cause the event to be raised on the brush. /// protected static void AffectsRender(params AvaloniaProperty[] properties) where T : Brush { void Invalidate(AvaloniaPropertyChangedEventArgs e) { - (e.Sender as T)?.RaiseChanged(EventArgs.Empty); + (e.Sender as T)?.RaiseInvalidated(EventArgs.Empty); } foreach (var property in properties) @@ -80,9 +80,9 @@ namespace Avalonia.Media } /// - /// Raises the event. + /// Raises the event. /// /// The event args. - protected void RaiseChanged(EventArgs e) => Changed?.Invoke(this, e); + protected void RaiseInvalidated(EventArgs e) => Invalidated?.Invoke(this, e); } } diff --git a/src/Avalonia.Visuals/Media/GradientBrush.cs b/src/Avalonia.Visuals/Media/GradientBrush.cs index c123813cee..8fd2dcf27f 100644 --- a/src/Avalonia.Visuals/Media/GradientBrush.cs +++ b/src/Avalonia.Visuals/Media/GradientBrush.cs @@ -80,18 +80,18 @@ namespace Avalonia.Media brush._gradientStopsSubscription = newValue.TrackItemPropertyChanged(brush.GradientStopChanged); } - brush.RaiseChanged(EventArgs.Empty); + brush.RaiseInvalidated(EventArgs.Empty); } } private void GradientStopsChanged(object sender, NotifyCollectionChangedEventArgs e) { - RaiseChanged(EventArgs.Empty); + RaiseInvalidated(EventArgs.Empty); } private void GradientStopChanged(Tuple e) { - RaiseChanged(EventArgs.Empty); + RaiseInvalidated(EventArgs.Empty); } } } diff --git a/src/Avalonia.Visuals/Media/IAffectsRender.cs b/src/Avalonia.Visuals/Media/IAffectsRender.cs new file mode 100644 index 0000000000..0024195ce5 --- /dev/null +++ b/src/Avalonia.Visuals/Media/IAffectsRender.cs @@ -0,0 +1,16 @@ +using System; + +namespace Avalonia.Media +{ + /// + /// Signals to a self-rendering control that changes to the resource should invoke + /// . + /// + public interface IAffectsRender + { + /// + /// Raised when the resource changes visually. + /// + event EventHandler Invalidated; + } +} diff --git a/src/Avalonia.Visuals/Media/IMutableBrush.cs b/src/Avalonia.Visuals/Media/IMutableBrush.cs index 762731a6a8..415db61d68 100644 --- a/src/Avalonia.Visuals/Media/IMutableBrush.cs +++ b/src/Avalonia.Visuals/Media/IMutableBrush.cs @@ -5,13 +5,8 @@ namespace Avalonia.Media /// /// Represents a mutable brush which can return an immutable clone of itself. /// - public interface IMutableBrush : IBrush + public interface IMutableBrush : IBrush, IAffectsRender { - /// - /// Raised when the brush changes visually. - /// - event EventHandler Changed; - /// /// Creates an immutable clone of the brush. /// diff --git a/src/Avalonia.Visuals/Visual.cs b/src/Avalonia.Visuals/Visual.cs index fdff16494f..e5fcf1ba1d 100644 --- a/src/Avalonia.Visuals/Visual.cs +++ b/src/Avalonia.Visuals/Visual.cs @@ -338,45 +338,20 @@ namespace Avalonia /// FrameworkPropertyMetadata.AffectsRender flag. /// protected static void AffectsRender(params AvaloniaProperty[] properties) - where T : class, IVisual - { - void Invalidate(AvaloniaPropertyChangedEventArgs e) - { - (e.Sender as T)?.InvalidateVisual(); - } - - foreach (var property in properties) - { - property.Changed.Subscribe(Invalidate); - } - } - - /// - /// Indicates that a brush property change should cause to be - /// called. - /// - /// The properties. - /// - /// This method should be called in a control's static constructor with each property - /// on the control which when changed should cause a redraw. It not only triggers an - /// invalidation when the property itself changes, but also when the brush raises - /// the event. - /// - protected static void BrushAffectsRender(params AvaloniaProperty[] properties) where T : Visual { void Invalidate(AvaloniaPropertyChangedEventArgs e) { if (e.Sender is T sender) { - if (e.OldValue is IMutableBrush oldValue) + if (e.OldValue is IAffectsRender oldValue) { - oldValue.Changed -= sender.BrushChanged; + oldValue.Invalidated -= sender.AffectsRenderInvalidated; } - if (e.NewValue is IMutableBrush newValue) + if (e.NewValue is IAffectsRender newValue) { - newValue.Changed += sender.BrushChanged; + newValue.Invalidated += sender.AffectsRenderInvalidated; } sender.InvalidateVisual(); @@ -582,7 +557,7 @@ namespace Avalonia OnVisualParentChanged(old, value); } - private void BrushChanged(object sender, EventArgs e) => InvalidateVisual(); + private void AffectsRenderInvalidated(object sender, EventArgs e) => InvalidateVisual(); /// /// Called when the collection changes. diff --git a/tests/Avalonia.Visuals.UnitTests/Media/ImageBrushTests.cs b/tests/Avalonia.Visuals.UnitTests/Media/ImageBrushTests.cs index f843a6e333..ff7b94105a 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/ImageBrushTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Media/ImageBrushTests.cs @@ -11,14 +11,14 @@ namespace Avalonia.Visuals.UnitTests.Media public class ImageBrushTests { [Fact] - public void Changing_Source_Raises_Changed() + public void Changing_Source_Raises_Invalidated() { var bitmap1 = Mock.Of(); var bitmap2 = Mock.Of(); var target = new ImageBrush(bitmap1); var raised = false; - target.Changed += (s, e) => raised = true; + target.Invalidated += (s, e) => raised = true; target.Source = bitmap2; Assert.True(raised); diff --git a/tests/Avalonia.Visuals.UnitTests/Media/LinearGradientBrushTests.cs b/tests/Avalonia.Visuals.UnitTests/Media/LinearGradientBrushTests.cs index 62f53108e6..b3f78dfe8f 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/LinearGradientBrushTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Media/LinearGradientBrushTests.cs @@ -9,7 +9,7 @@ namespace Avalonia.Visuals.UnitTests.Media public class LinearGradientBrushTests { [Fact] - public void Changing_StartPoint_Raises_Changed() + public void Changing_StartPoint_Raises_Invalidated() { var bitmap1 = Mock.Of(); var bitmap2 = Mock.Of(); @@ -17,14 +17,14 @@ namespace Avalonia.Visuals.UnitTests.Media var raised = false; target.StartPoint = new RelativePoint(); - target.Changed += (s, e) => raised = true; + target.Invalidated += (s, e) => raised = true; target.StartPoint = new RelativePoint(10, 10, RelativeUnit.Absolute); Assert.True(raised); } [Fact] - public void Changing_EndPoint_Raises_Changed() + public void Changing_EndPoint_Raises_Invalidated() { var bitmap1 = Mock.Of(); var bitmap2 = Mock.Of(); @@ -32,14 +32,14 @@ namespace Avalonia.Visuals.UnitTests.Media var raised = false; target.EndPoint = new RelativePoint(); - target.Changed += (s, e) => raised = true; + target.Invalidated += (s, e) => raised = true; target.EndPoint = new RelativePoint(10, 10, RelativeUnit.Absolute); Assert.True(raised); } [Fact] - public void Changing_GradientStops_Raises_Changed() + public void Changing_GradientStops_Raises_Invalidated() { var bitmap1 = Mock.Of(); var bitmap2 = Mock.Of(); @@ -47,14 +47,14 @@ namespace Avalonia.Visuals.UnitTests.Media var raised = false; target.GradientStops = new GradientStops { new GradientStop(Colors.Red, 0) }; - target.Changed += (s, e) => raised = true; + target.Invalidated += (s, e) => raised = true; target.GradientStops = new GradientStops { new GradientStop(Colors.Green, 0) }; Assert.True(raised); } [Fact] - public void Adding_GradientStop_Raises_Changed() + public void Adding_GradientStop_Raises_Invalidated() { var bitmap1 = Mock.Of(); var bitmap2 = Mock.Of(); @@ -62,14 +62,14 @@ namespace Avalonia.Visuals.UnitTests.Media var raised = false; target.GradientStops = new GradientStops { new GradientStop(Colors.Red, 0) }; - target.Changed += (s, e) => raised = true; + target.Invalidated += (s, e) => raised = true; target.GradientStops.Add(new GradientStop(Colors.Green, 1)); Assert.True(raised); } [Fact] - public void Changing_GradientStop_Offset_Raises_Changed() + public void Changing_GradientStop_Offset_Raises_Invalidated() { var bitmap1 = Mock.Of(); var bitmap2 = Mock.Of(); @@ -77,7 +77,7 @@ namespace Avalonia.Visuals.UnitTests.Media var raised = false; target.GradientStops = new GradientStops { new GradientStop(Colors.Red, 0) }; - target.Changed += (s, e) => raised = true; + target.Invalidated += (s, e) => raised = true; target.GradientStops[0].Offset = 0.5; Assert.True(raised); diff --git a/tests/Avalonia.Visuals.UnitTests/Media/SolidColorBrushTests.cs b/tests/Avalonia.Visuals.UnitTests/Media/SolidColorBrushTests.cs index 4e87b7081d..bcf63f9660 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/SolidColorBrushTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Media/SolidColorBrushTests.cs @@ -7,12 +7,12 @@ namespace Avalonia.Visuals.UnitTests.Media public class SolidColorBrushTests { [Fact] - public void Changing_Color_Raises_Changed() + public void Changing_Color_Raises_Invalidated() { var target = new SolidColorBrush(Colors.Red); var raised = false; - target.Changed += (s, e) => raised = true; + target.Invalidated += (s, e) => raised = true; target.Color = Colors.Green; Assert.True(raised);