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>
static Border()
{
AffectsRender<Border>(BorderThicknessProperty, CornerRadiusProperty);
AffectsRender<Border>(
BackgroundProperty,
BorderBrushProperty,
BorderThicknessProperty,
CornerRadiusProperty);
AffectsMeasure<Border>(BorderThicknessProperty);
BrushAffectsRender<Border>(BackgroundProperty, BorderBrushProperty);
}
/// <summary>

2
src/Avalonia.Controls/Panel.cs

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

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

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

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

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

3
src/Avalonia.Controls/TextBlock.cs

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

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

@ -19,7 +19,7 @@ namespace Avalonia.Media
AvaloniaProperty.Register<Brush, double>(nameof(Opacity), 1.0);
/// <inheritdoc/>
public event EventHandler Changed;
public event EventHandler Invalidated;
/// <summary>
/// Gets or sets the opacity of the brush.
@ -63,14 +63,14 @@ namespace Avalonia.Media
/// <param name="properties">The properties.</param>
/// <remarks>
/// 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>
protected static void AffectsRender<T>(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
}
/// <summary>
/// Raises the <see cref="Changed"/> event.
/// Raises the <see cref="Invalidated"/> event.
/// </summary>
/// <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.RaiseChanged(EventArgs.Empty);
brush.RaiseInvalidated(EventArgs.Empty);
}
}
private void GradientStopsChanged(object sender, NotifyCollectionChangedEventArgs e)
{
RaiseChanged(EventArgs.Empty);
RaiseInvalidated(EventArgs.Empty);
}
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>
/// Represents a mutable brush which can return an immutable clone of itself.
/// </summary>
public interface IMutableBrush : IBrush
public interface IMutableBrush : IBrush, IAffectsRender
{
/// <summary>
/// Raised when the brush changes visually.
/// </summary>
event EventHandler Changed;
/// <summary>
/// Creates an immutable clone of the brush.
/// </summary>

35
src/Avalonia.Visuals/Visual.cs

@ -338,45 +338,20 @@ namespace Avalonia
/// FrameworkPropertyMetadata.AffectsRender flag.
/// </remarks>
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
{
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();
/// <summary>
/// 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
{
[Fact]
public void Changing_Source_Raises_Changed()
public void Changing_Source_Raises_Invalidated()
{
var bitmap1 = Mock.Of<IBitmap>();
var bitmap2 = Mock.Of<IBitmap>();
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);

20
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<IBitmap>();
var bitmap2 = Mock.Of<IBitmap>();
@ -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<IBitmap>();
var bitmap2 = Mock.Of<IBitmap>();
@ -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<IBitmap>();
var bitmap2 = Mock.Of<IBitmap>();
@ -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<IBitmap>();
var bitmap2 = Mock.Of<IBitmap>();
@ -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<IBitmap>();
var bitmap2 = Mock.Of<IBitmap>();
@ -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);

4
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);

Loading…
Cancel
Save