Browse Source

Added IAffectsRender interface...

...and use it as a general-purpose means of attaching to property values that can affect render.
pull/1881/head
Steven Kirk 8 years ago
parent
commit
ac0c9e69cd
  1. 7
      src/Avalonia.Controls/Border.cs
  2. 2
      src/Avalonia.Controls/Panel.cs
  3. 3
      src/Avalonia.Controls/Presenters/ContentPresenter.cs
  4. 3
      src/Avalonia.Controls/Shapes/Shape.cs
  5. 3
      src/Avalonia.Controls/TextBlock.cs
  6. 10
      src/Avalonia.Visuals/Media/Brush.cs
  7. 6
      src/Avalonia.Visuals/Media/GradientBrush.cs
  8. 16
      src/Avalonia.Visuals/Media/IAffectsRender.cs
  9. 7
      src/Avalonia.Visuals/Media/IMutableBrush.cs
  10. 35
      src/Avalonia.Visuals/Visual.cs
  11. 4
      tests/Avalonia.Visuals.UnitTests/Media/ImageBrushTests.cs
  12. 20
      tests/Avalonia.Visuals.UnitTests/Media/LinearGradientBrushTests.cs
  13. 4
      tests/Avalonia.Visuals.UnitTests/Media/SolidColorBrushTests.cs

7
src/Avalonia.Controls/Border.cs

@ -43,9 +43,12 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
static Border() static Border()
{ {
AffectsRender<Border>(BorderThicknessProperty, CornerRadiusProperty); AffectsRender<Border>(
BackgroundProperty,
BorderBrushProperty,
BorderThicknessProperty,
CornerRadiusProperty);
AffectsMeasure<Border>(BorderThicknessProperty); AffectsMeasure<Border>(BorderThicknessProperty);
BrushAffectsRender<Border>(BackgroundProperty, BorderBrushProperty);
} }
/// <summary> /// <summary>

2
src/Avalonia.Controls/Panel.cs

@ -30,7 +30,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
static Panel() static Panel()
{ {
BrushAffectsRender<Panel>(BackgroundProperty); AffectsRender<Panel>(BackgroundProperty);
ClipToBoundsProperty.OverrideDefaultValue<Panel>(true); ClipToBoundsProperty.OverrideDefaultValue<Panel>(true);
} }

3
src/Avalonia.Controls/Presenters/ContentPresenter.cs

@ -90,9 +90,8 @@ namespace Avalonia.Controls.Presenters
/// </summary> /// </summary>
static ContentPresenter() static ContentPresenter()
{ {
AffectsRender<ContentPresenter>(BorderThicknessProperty, CornerRadiusProperty); AffectsRender<ContentPresenter>(BackgroundProperty, BorderBrushProperty, BorderThicknessProperty, CornerRadiusProperty);
AffectsMeasure<ContentPresenter>(BorderThicknessProperty, PaddingProperty); AffectsMeasure<ContentPresenter>(BorderThicknessProperty, PaddingProperty);
BrushAffectsRender<ContentPresenter>(BackgroundProperty, BorderBrushProperty);
ContentProperty.Changed.AddClassHandler<ContentPresenter>(x => x.ContentChanged); ContentProperty.Changed.AddClassHandler<ContentPresenter>(x => x.ContentChanged);
ContentTemplateProperty.Changed.AddClassHandler<ContentPresenter>(x => x.ContentChanged); ContentTemplateProperty.Changed.AddClassHandler<ContentPresenter>(x => x.ContentChanged);
TemplatedParentProperty.Changed.AddClassHandler<ContentPresenter>(x => x.TemplatedParentChanged); TemplatedParentProperty.Changed.AddClassHandler<ContentPresenter>(x => x.TemplatedParentChanged);

3
src/Avalonia.Controls/Shapes/Shape.cs

@ -33,8 +33,7 @@ namespace Avalonia.Controls.Shapes
static Shape() static Shape()
{ {
AffectsMeasure<Shape>(StretchProperty, StrokeThicknessProperty); AffectsMeasure<Shape>(StretchProperty, StrokeThicknessProperty);
AffectsRender<Shape>(StrokeDashArrayProperty); AffectsRender<Shape>(FillProperty, StrokeProperty, StrokeDashArrayProperty);
BrushAffectsRender<Shape>(FillProperty, StrokeProperty);
} }
public Geometry DefiningGeometry public Geometry DefiningGeometry

3
src/Avalonia.Controls/TextBlock.cs

@ -101,10 +101,11 @@ namespace Avalonia.Controls
{ {
ClipToBoundsProperty.OverrideDefaultValue<TextBlock>(true); ClipToBoundsProperty.OverrideDefaultValue<TextBlock>(true);
AffectsRender<TextBlock>( AffectsRender<TextBlock>(
BackgroundProperty,
ForegroundProperty,
FontWeightProperty, FontWeightProperty,
FontSizeProperty, FontSizeProperty,
FontStyleProperty); FontStyleProperty);
BrushAffectsRender<TextBlock>(BackgroundProperty, ForegroundProperty);
} }
/// <summary> /// <summary>

10
src/Avalonia.Visuals/Media/Brush.cs

@ -19,7 +19,7 @@ namespace Avalonia.Media
AvaloniaProperty.Register<Brush, double>(nameof(Opacity), 1.0); AvaloniaProperty.Register<Brush, double>(nameof(Opacity), 1.0);
/// <inheritdoc/> /// <inheritdoc/>
public event EventHandler Changed; public event EventHandler Invalidated;
/// <summary> /// <summary>
/// Gets or sets the opacity of the brush. /// Gets or sets the opacity of the brush.
@ -63,14 +63,14 @@ namespace Avalonia.Media
/// <param name="properties">The properties.</param> /// <param name="properties">The properties.</param>
/// <remarks> /// <remarks>
/// After a call to this method in a brush's static constructor, any change to the /// After a call to this method in a brush's static constructor, any change to the
/// property will cause the <see cref="Changed"/> event to be raised on the brush. /// property will cause the <see cref="Invalidated"/> event to be raised on the brush.
/// </remarks> /// </remarks>
protected static void AffectsRender<T>(params AvaloniaProperty[] properties) protected static void AffectsRender<T>(params AvaloniaProperty[] properties)
where T : Brush where T : Brush
{ {
void Invalidate(AvaloniaPropertyChangedEventArgs e) void Invalidate(AvaloniaPropertyChangedEventArgs e)
{ {
(e.Sender as T)?.RaiseChanged(EventArgs.Empty); (e.Sender as T)?.RaiseInvalidated(EventArgs.Empty);
} }
foreach (var property in properties) foreach (var property in properties)
@ -80,9 +80,9 @@ namespace Avalonia.Media
} }
/// <summary> /// <summary>
/// Raises the <see cref="Changed"/> event. /// Raises the <see cref="Invalidated"/> event.
/// </summary> /// </summary>
/// <param name="e">The event args.</param> /// <param name="e">The event args.</param>
protected void RaiseChanged(EventArgs e) => Changed?.Invoke(this, e); protected void RaiseInvalidated(EventArgs e) => Invalidated?.Invoke(this, e);
} }
} }

6
src/Avalonia.Visuals/Media/GradientBrush.cs

@ -80,18 +80,18 @@ namespace Avalonia.Media
brush._gradientStopsSubscription = newValue.TrackItemPropertyChanged(brush.GradientStopChanged); brush._gradientStopsSubscription = newValue.TrackItemPropertyChanged(brush.GradientStopChanged);
} }
brush.RaiseChanged(EventArgs.Empty); brush.RaiseInvalidated(EventArgs.Empty);
} }
} }
private void GradientStopsChanged(object sender, NotifyCollectionChangedEventArgs e) private void GradientStopsChanged(object sender, NotifyCollectionChangedEventArgs e)
{ {
RaiseChanged(EventArgs.Empty); RaiseInvalidated(EventArgs.Empty);
} }
private void GradientStopChanged(Tuple<object, PropertyChangedEventArgs> e) private void GradientStopChanged(Tuple<object, PropertyChangedEventArgs> e)
{ {
RaiseChanged(EventArgs.Empty); RaiseInvalidated(EventArgs.Empty);
} }
} }
} }

16
src/Avalonia.Visuals/Media/IAffectsRender.cs

@ -0,0 +1,16 @@
using System;
namespace Avalonia.Media
{
/// <summary>
/// Signals to a self-rendering control that changes to the resource should invoke
/// <see cref="Visual.InvalidateVisual"/>.
/// </summary>
public interface IAffectsRender
{
/// <summary>
/// Raised when the resource changes visually.
/// </summary>
event EventHandler Invalidated;
}
}

7
src/Avalonia.Visuals/Media/IMutableBrush.cs

@ -5,13 +5,8 @@ namespace Avalonia.Media
/// <summary> /// <summary>
/// Represents a mutable brush which can return an immutable clone of itself. /// Represents a mutable brush which can return an immutable clone of itself.
/// </summary> /// </summary>
public interface IMutableBrush : IBrush public interface IMutableBrush : IBrush, IAffectsRender
{ {
/// <summary>
/// Raised when the brush changes visually.
/// </summary>
event EventHandler Changed;
/// <summary> /// <summary>
/// Creates an immutable clone of the brush. /// Creates an immutable clone of the brush.
/// </summary> /// </summary>

35
src/Avalonia.Visuals/Visual.cs

@ -338,45 +338,20 @@ namespace Avalonia
/// FrameworkPropertyMetadata.AffectsRender flag. /// FrameworkPropertyMetadata.AffectsRender flag.
/// </remarks> /// </remarks>
protected static void AffectsRender<T>(params AvaloniaProperty[] properties) protected static void AffectsRender<T>(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);
}
}
/// <summary>
/// Indicates that a brush property change should cause <see cref="InvalidateVisual"/> to be
/// called.
/// </summary>
/// <param name="properties">The properties.</param>
/// <remarks>
/// 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 <see cref="IMutableBrush.Changed"/> event.
/// </remarks>
protected static void BrushAffectsRender<T>(params AvaloniaProperty<IBrush>[] properties)
where T : Visual where T : Visual
{ {
void Invalidate(AvaloniaPropertyChangedEventArgs e) void Invalidate(AvaloniaPropertyChangedEventArgs e)
{ {
if (e.Sender is T sender) 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(); sender.InvalidateVisual();
@ -582,7 +557,7 @@ namespace Avalonia
OnVisualParentChanged(old, value); OnVisualParentChanged(old, value);
} }
private void BrushChanged(object sender, EventArgs e) => InvalidateVisual(); private void AffectsRenderInvalidated(object sender, EventArgs e) => InvalidateVisual();
/// <summary> /// <summary>
/// Called when the <see cref="VisualChildren"/> collection changes. /// Called when the <see cref="VisualChildren"/> collection changes.

4
tests/Avalonia.Visuals.UnitTests/Media/ImageBrushTests.cs

@ -11,14 +11,14 @@ namespace Avalonia.Visuals.UnitTests.Media
public class ImageBrushTests public class ImageBrushTests
{ {
[Fact] [Fact]
public void Changing_Source_Raises_Changed() public void Changing_Source_Raises_Invalidated()
{ {
var bitmap1 = Mock.Of<IBitmap>(); var bitmap1 = Mock.Of<IBitmap>();
var bitmap2 = Mock.Of<IBitmap>(); var bitmap2 = Mock.Of<IBitmap>();
var target = new ImageBrush(bitmap1); var target = new ImageBrush(bitmap1);
var raised = false; var raised = false;
target.Changed += (s, e) => raised = true; target.Invalidated += (s, e) => raised = true;
target.Source = bitmap2; target.Source = bitmap2;
Assert.True(raised); Assert.True(raised);

20
tests/Avalonia.Visuals.UnitTests/Media/LinearGradientBrushTests.cs

@ -9,7 +9,7 @@ namespace Avalonia.Visuals.UnitTests.Media
public class LinearGradientBrushTests public class LinearGradientBrushTests
{ {
[Fact] [Fact]
public void Changing_StartPoint_Raises_Changed() public void Changing_StartPoint_Raises_Invalidated()
{ {
var bitmap1 = Mock.Of<IBitmap>(); var bitmap1 = Mock.Of<IBitmap>();
var bitmap2 = Mock.Of<IBitmap>(); var bitmap2 = Mock.Of<IBitmap>();
@ -17,14 +17,14 @@ namespace Avalonia.Visuals.UnitTests.Media
var raised = false; var raised = false;
target.StartPoint = new RelativePoint(); target.StartPoint = new RelativePoint();
target.Changed += (s, e) => raised = true; target.Invalidated += (s, e) => raised = true;
target.StartPoint = new RelativePoint(10, 10, RelativeUnit.Absolute); target.StartPoint = new RelativePoint(10, 10, RelativeUnit.Absolute);
Assert.True(raised); Assert.True(raised);
} }
[Fact] [Fact]
public void Changing_EndPoint_Raises_Changed() public void Changing_EndPoint_Raises_Invalidated()
{ {
var bitmap1 = Mock.Of<IBitmap>(); var bitmap1 = Mock.Of<IBitmap>();
var bitmap2 = Mock.Of<IBitmap>(); var bitmap2 = Mock.Of<IBitmap>();
@ -32,14 +32,14 @@ namespace Avalonia.Visuals.UnitTests.Media
var raised = false; var raised = false;
target.EndPoint = new RelativePoint(); target.EndPoint = new RelativePoint();
target.Changed += (s, e) => raised = true; target.Invalidated += (s, e) => raised = true;
target.EndPoint = new RelativePoint(10, 10, RelativeUnit.Absolute); target.EndPoint = new RelativePoint(10, 10, RelativeUnit.Absolute);
Assert.True(raised); Assert.True(raised);
} }
[Fact] [Fact]
public void Changing_GradientStops_Raises_Changed() public void Changing_GradientStops_Raises_Invalidated()
{ {
var bitmap1 = Mock.Of<IBitmap>(); var bitmap1 = Mock.Of<IBitmap>();
var bitmap2 = Mock.Of<IBitmap>(); var bitmap2 = Mock.Of<IBitmap>();
@ -47,14 +47,14 @@ namespace Avalonia.Visuals.UnitTests.Media
var raised = false; var raised = false;
target.GradientStops = new GradientStops { new GradientStop(Colors.Red, 0) }; 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) }; target.GradientStops = new GradientStops { new GradientStop(Colors.Green, 0) };
Assert.True(raised); Assert.True(raised);
} }
[Fact] [Fact]
public void Adding_GradientStop_Raises_Changed() public void Adding_GradientStop_Raises_Invalidated()
{ {
var bitmap1 = Mock.Of<IBitmap>(); var bitmap1 = Mock.Of<IBitmap>();
var bitmap2 = Mock.Of<IBitmap>(); var bitmap2 = Mock.Of<IBitmap>();
@ -62,14 +62,14 @@ namespace Avalonia.Visuals.UnitTests.Media
var raised = false; var raised = false;
target.GradientStops = new GradientStops { new GradientStop(Colors.Red, 0) }; 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)); target.GradientStops.Add(new GradientStop(Colors.Green, 1));
Assert.True(raised); Assert.True(raised);
} }
[Fact] [Fact]
public void Changing_GradientStop_Offset_Raises_Changed() public void Changing_GradientStop_Offset_Raises_Invalidated()
{ {
var bitmap1 = Mock.Of<IBitmap>(); var bitmap1 = Mock.Of<IBitmap>();
var bitmap2 = Mock.Of<IBitmap>(); var bitmap2 = Mock.Of<IBitmap>();
@ -77,7 +77,7 @@ namespace Avalonia.Visuals.UnitTests.Media
var raised = false; var raised = false;
target.GradientStops = new GradientStops { new GradientStop(Colors.Red, 0) }; 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; target.GradientStops[0].Offset = 0.5;
Assert.True(raised); Assert.True(raised);

4
tests/Avalonia.Visuals.UnitTests/Media/SolidColorBrushTests.cs

@ -7,12 +7,12 @@ namespace Avalonia.Visuals.UnitTests.Media
public class SolidColorBrushTests public class SolidColorBrushTests
{ {
[Fact] [Fact]
public void Changing_Color_Raises_Changed() public void Changing_Color_Raises_Invalidated()
{ {
var target = new SolidColorBrush(Colors.Red); var target = new SolidColorBrush(Colors.Red);
var raised = false; var raised = false;
target.Changed += (s, e) => raised = true; target.Invalidated += (s, e) => raised = true;
target.Color = Colors.Green; target.Color = Colors.Green;
Assert.True(raised); Assert.True(raised);

Loading…
Cancel
Save