Browse Source

Optimize Pen's subscriptions to IAffects render

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

Loading…
Cancel
Save