Browse Source

Optimize Pen's subscriptions to IAffects render

pull/7303/head
Nikita Tsukanov 4 years ago
parent
commit
dfd0523e36
  1. 5
      src/Avalonia.Visuals/ApiCompatBaseline.txt
  2. 110
      src/Avalonia.Visuals/Media/Pen.cs

5
src/Avalonia.Visuals/ApiCompatBaseline.txt

@ -7,6 +7,9 @@ InterfacesShouldHaveSameMembers : Interface member 'public System.Threading.Task
MembersMustExist : Member 'public System.Threading.Tasks.Task Avalonia.Animation.PageSlide.Start(Avalonia.Visual, Avalonia.Visual, System.Boolean)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Media.GlyphRun..ctor()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Media.GlyphRun.GlyphTypeface.set(Avalonia.Media.GlyphTypeface)' does not exist in the implementation but it does exist in the contract.
CannotSealType : Type 'Avalonia.Media.Pen' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract.
MembersMustExist : Member 'protected void Avalonia.Media.Pen.AffectsRender<T>(Avalonia.AvaloniaProperty[])' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'protected void Avalonia.Media.Pen.RaiseInvalidated(System.EventArgs)' does not exist in the implementation but it does exist in the contract.
TypeCannotChangeClassification : Type 'Avalonia.Media.Immutable.ImmutableSolidColorBrush' is a 'class' in the implementation but is a 'struct' in the contract.
MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.DrawableTextRun.Draw(Avalonia.Media.DrawingContext)' does not exist in the implementation but it does exist in the contract.
CannotAddAbstractMembers : Member 'public void Avalonia.Media.TextFormatting.DrawableTextRun.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' is abstract in the implementation but is missing in the contract.
@ -83,4 +86,4 @@ InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Size Avaloni
InterfacesShouldHaveSameMembers : Interface member 'public System.TimeSpan Avalonia.Platform.IPlatformSettings.TouchDoubleClickTime' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Size Avalonia.Platform.IPlatformSettings.TouchDoubleClickSize.get()' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public System.TimeSpan Avalonia.Platform.IPlatformSettings.TouchDoubleClickTime.get()' is present in the implementation but not in the contract.
Total Issues: 84
Total Issues: 87

110
src/Avalonia.Visuals/Media/Pen.cs

@ -7,7 +7,7 @@ namespace Avalonia.Media
/// <summary>
/// Describes how a stroke is drawn.
/// </summary>
public class Pen : AvaloniaObject, IPen
public sealed class Pen : AvaloniaObject, IPen, IWeakEventSubscriber<EventArgs>
{
/// <summary>
/// Defines the <see cref="Brush"/> property.
@ -45,6 +45,9 @@ namespace Avalonia.Media
public static readonly StyledProperty<double> MiterLimitProperty =
AvaloniaProperty.Register<Pen, double>(nameof(MiterLimit), 10.0);
private EventHandler? _invalidated;
private IAffectsRender? _subscribedTo;
/// <summary>
/// Initializes a new instance of the <see cref="Pen"/> class.
/// </summary>
@ -96,17 +99,6 @@ namespace Avalonia.Media
DashStyle = dashStyle;
}
static Pen()
{
AffectsRender<Pen>(
BrushProperty,
ThicknessProperty,
DashStyleProperty,
LineCapProperty,
LineJoinProperty,
MiterLimitProperty);
}
/// <summary>
/// Gets or sets the brush used to draw the stroke.
/// </summary>
@ -116,6 +108,11 @@ namespace Avalonia.Media
set => SetValue(BrushProperty, value);
}
private static readonly WeakEvent<IAffectsRender, EventArgs> InvalidatedWeakEvent =
WeakEvent.Register<IAffectsRender>(
(s, h) => s.Invalidated += h,
(s, h) => s.Invalidated -= h);
/// <summary>
/// Gets or sets the stroke thickness.
/// </summary>
@ -165,7 +162,19 @@ namespace Avalonia.Media
/// <summary>
/// Raised when the pen changes.
/// </summary>
public event EventHandler? Invalidated;
public event EventHandler? Invalidated
{
add
{
_invalidated += value;
UpdateBrushSubscription();
}
remove
{
_invalidated -= value;
UpdateBrushSubscription();
}
}
/// <summary>
/// Creates an immutable clone of the brush.
@ -182,68 +191,33 @@ namespace Avalonia.Media
MiterLimit);
}
/// <summary>
/// Marks a property as affecting the pen's visual representation.
/// </summary>
/// <param name="properties">The properties.</param>
/// <remarks>
/// After a call to this method in a pen's static constructor, any change to the
/// property will cause the <see cref="Invalidated"/> event to be raised on the pen.
/// </remarks>
protected static void AffectsRender<T>(params AvaloniaProperty[] properties)
where T : Pen
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
{
static void Invalidate(AvaloniaPropertyChangedEventArgs e)
{
if (e.Sender is T sender)
{
sender.RaiseInvalidated(EventArgs.Empty);
}
}
_invalidated?.Invoke(this, EventArgs.Empty);
if(change.Property == BrushProperty)
UpdateBrushSubscription();
base.OnPropertyChanged(change);
}
static void InvalidateAndSubscribe(AvaloniaPropertyChangedEventArgs e)
void UpdateBrushSubscription()
{
if ((_invalidated == null || _subscribedTo != Brush) && _subscribedTo != null)
{
if (e.Sender is T sender)
{
if (e.OldValue is IAffectsRender oldValue)
{
WeakEventHandlerManager.Unsubscribe<EventArgs, T>(
oldValue,
nameof(oldValue.Invalidated),
sender.AffectsRenderInvalidated);
}
if (e.NewValue is IAffectsRender newValue)
{
WeakEventHandlerManager.Subscribe<IAffectsRender, EventArgs, T>(
newValue,
nameof(newValue.Invalidated),
sender.AffectsRenderInvalidated);
}
sender.RaiseInvalidated(EventArgs.Empty);
}
InvalidatedWeakEvent.Unsubscribe(_subscribedTo, this);
_subscribedTo = null;
}
foreach (var property in properties)
if (_invalidated != null && _subscribedTo != Brush && Brush is IAffectsRender affectsRender)
{
if (property.CanValueAffectRender())
{
property.Changed.Subscribe(e => InvalidateAndSubscribe(e));
}
else
{
property.Changed.Subscribe(e => Invalidate(e));
}
InvalidatedWeakEvent.Subscribe(affectsRender, this);
_subscribedTo = affectsRender;
}
}
/// <summary>
/// Raises the <see cref="Invalidated"/> event.
/// </summary>
/// <param name="e">The event args.</param>
protected void RaiseInvalidated(EventArgs e) => Invalidated?.Invoke(this, e);
private void AffectsRenderInvalidated(object? sender, EventArgs e) => RaiseInvalidated(EventArgs.Empty);
void IWeakEventSubscriber<EventArgs>.OnEvent(object? sender, WeakEvent ev, EventArgs e)
{
if (ev == InvalidatedWeakEvent)
_invalidated?.Invoke(this, EventArgs.Empty);
}
}
}

Loading…
Cancel
Save