Browse Source

Optimize memory usage of styles and fix ordering issue in devtools.

pull/8131/head
Dariusz Komosinski 4 years ago
parent
commit
b34095fda3
  1. 5
      src/Avalonia.Base/Styling/IStyleInstance.cs
  2. 10
      src/Avalonia.Base/Styling/PropertySetterInstance.cs
  3. 31
      src/Avalonia.Base/Styling/PropertySetterTemplateInstance.cs
  4. 9
      src/Avalonia.Base/Styling/Setter.cs
  5. 35
      src/Avalonia.Base/Styling/StyleInstance.cs
  6. 3
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs

5
src/Avalonia.Base/Styling/IStyleInstance.cs

@ -14,6 +14,11 @@ namespace Avalonia.Styling
/// </summary> /// </summary>
IStyle Source { get; } IStyle Source { get; }
/// <summary>
/// Gets a value indicating whether this style has an activator.
/// </summary>
bool HasActivator { get; }
/// <summary> /// <summary>
/// Gets a value indicating whether this style is active. /// Gets a value indicating whether this style is active.
/// </summary> /// </summary>

10
src/Avalonia.Base/Styling/PropertySetterInstance.cs

@ -44,7 +44,7 @@ namespace Avalonia.Styling
{ {
if (hasActivator) if (hasActivator)
{ {
if (_styledProperty is object) if (_styledProperty is not null)
{ {
_subscription = _target.Bind(_styledProperty, this, BindingPriority.StyleTrigger); _subscription = _target.Bind(_styledProperty, this, BindingPriority.StyleTrigger);
} }
@ -55,13 +55,15 @@ namespace Avalonia.Styling
} }
else else
{ {
if (_styledProperty is object) var target = (AvaloniaObject) _target;
if (_styledProperty is not null)
{ {
_subscription = _target.SetValue(_styledProperty!, _value, BindingPriority.Style); _subscription = target.SetValue(_styledProperty!, _value, BindingPriority.Style);
} }
else else
{ {
_target.SetValue(_directProperty!, _value); target.SetValue(_directProperty!, _value);
} }
} }
} }

31
src/Avalonia.Base/Styling/PropertySetterLazyInstance.cs → src/Avalonia.Base/Styling/PropertySetterTemplateInstance.cs

@ -11,42 +11,42 @@ namespace Avalonia.Styling
/// evaluated. /// evaluated.
/// </summary> /// </summary>
/// <typeparam name="T">The target property type.</typeparam> /// <typeparam name="T">The target property type.</typeparam>
internal class PropertySetterLazyInstance<T> : SingleSubscriberObservableBase<BindingValue<T>>, internal class PropertySetterTemplateInstance<T> : SingleSubscriberObservableBase<BindingValue<T>>,
ISetterInstance ISetterInstance
{ {
private readonly IStyleable _target; private readonly IStyleable _target;
private readonly StyledPropertyBase<T>? _styledProperty; private readonly StyledPropertyBase<T>? _styledProperty;
private readonly DirectPropertyBase<T>? _directProperty; private readonly DirectPropertyBase<T>? _directProperty;
private readonly Func<T> _valueFactory; private readonly ITemplate _template;
private BindingValue<T> _value; private BindingValue<T> _value;
private IDisposable? _subscription; private IDisposable? _subscription;
private bool _isActive; private bool _isActive;
public PropertySetterLazyInstance( public PropertySetterTemplateInstance(
IStyleable target, IStyleable target,
StyledPropertyBase<T> property, StyledPropertyBase<T> property,
Func<T> valueFactory) ITemplate template)
{ {
_target = target; _target = target;
_styledProperty = property; _styledProperty = property;
_valueFactory = valueFactory; _template = template;
} }
public PropertySetterLazyInstance( public PropertySetterTemplateInstance(
IStyleable target, IStyleable target,
DirectPropertyBase<T> property, DirectPropertyBase<T> property,
Func<T> valueFactory) ITemplate template)
{ {
_target = target; _target = target;
_directProperty = property; _directProperty = property;
_valueFactory = valueFactory; _template = template;
} }
public void Start(bool hasActivator) public void Start(bool hasActivator)
{ {
_isActive = !hasActivator; _isActive = !hasActivator;
if (_styledProperty is object) if (_styledProperty is not null)
{ {
var priority = hasActivator ? BindingPriority.StyleTrigger : BindingPriority.Style; var priority = hasActivator ? BindingPriority.StyleTrigger : BindingPriority.Style;
_subscription = _target.Bind(_styledProperty, this, priority); _subscription = _target.Bind(_styledProperty, this, priority);
@ -77,7 +77,7 @@ namespace Avalonia.Styling
public override void Dispose() public override void Dispose()
{ {
if (_subscription is object) if (_subscription is not null)
{ {
var sub = _subscription; var sub = _subscription;
_subscription = null; _subscription = null;
@ -85,7 +85,7 @@ namespace Avalonia.Styling
} }
else if (_isActive) else if (_isActive)
{ {
if (_styledProperty is object) if (_styledProperty is not null)
{ {
_target.ClearValue(_styledProperty); _target.ClearValue(_styledProperty);
} }
@ -101,22 +101,21 @@ namespace Avalonia.Styling
protected override void Subscribed() => PublishNext(); protected override void Subscribed() => PublishNext();
protected override void Unsubscribed() { } protected override void Unsubscribed() { }
private T GetValue() private void EnsureTemplate()
{ {
if (_value.HasValue) if (_value.HasValue)
{ {
return _value.Value; return;
} }
_value = _valueFactory(); _value = (T) _template.Build();
return _value.Value;
} }
private void PublishNext() private void PublishNext()
{ {
if (_isActive) if (_isActive)
{ {
GetValue(); EnsureTemplate();
PublishNext(_value); PublishNext(_value);
} }
else else

9
src/Avalonia.Base/Styling/Setter.cs

@ -1,9 +1,7 @@
using System; using System;
using Avalonia.Animation; using Avalonia.Animation;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Data.Core;
using Avalonia.Metadata; using Avalonia.Metadata;
using Avalonia.Utilities;
#nullable enable #nullable enable
@ -70,12 +68,5 @@ namespace Avalonia.Styling
return Property.CreateSetterInstance(target, Value); return Property.CreateSetterInstance(target, Value);
} }
private struct SetterVisitorData
{
public IStyleable target;
public object? value;
public ISetterInstance? result;
}
} }
} }

35
src/Avalonia.Base/Styling/StyleInstance.cs

@ -11,10 +11,10 @@ namespace Avalonia.Styling
/// <summary> /// <summary>
/// A <see cref="Style"/> which has been instanced on a control. /// A <see cref="Style"/> which has been instanced on a control.
/// </summary> /// </summary>
internal class StyleInstance : IStyleInstance, IStyleActivatorSink internal sealed class StyleInstance : IStyleInstance, IStyleActivatorSink
{ {
private readonly List<ISetterInstance>? _setters; private readonly ISetterInstance[]? _setters;
private readonly List<IDisposable>? _animations; private readonly IDisposable[]? _animations;
private readonly IStyleActivator? _activator; private readonly IStyleActivator? _activator;
private readonly Subject<bool>? _animationTrigger; private readonly Subject<bool>? _animationTrigger;
@ -30,41 +30,42 @@ namespace Avalonia.Styling
_activator = activator; _activator = activator;
IsActive = _activator is null; IsActive = _activator is null;
if (setters is object) if (setters is not null)
{ {
var setterCount = setters.Count; var setterCount = setters.Count;
_setters = new List<ISetterInstance>(setterCount); _setters = new ISetterInstance[setterCount];
for (var i = 0; i < setterCount; ++i) for (var i = 0; i < setterCount; ++i)
{ {
_setters.Add(setters[i].Instance(Target)); _setters[i] = setters[i].Instance(Target);
} }
} }
if (animations is object && target is Animatable animatable) if (animations is not null && target is Animatable animatable)
{ {
var animationsCount = animations.Count; var animationsCount = animations.Count;
_animations = new List<IDisposable>(animationsCount); _animations = new IDisposable[animationsCount];
_animationTrigger = new Subject<bool>(); _animationTrigger = new Subject<bool>();
for (var i = 0; i < animationsCount; ++i) for (var i = 0; i < animationsCount; ++i)
{ {
_animations.Add(animations[i].Apply(animatable, null, _animationTrigger)); _animations[i] = animations[i].Apply(animatable, null, _animationTrigger);
} }
} }
} }
public bool HasActivator => _activator is not null;
public bool IsActive { get; private set; } public bool IsActive { get; private set; }
public IStyle Source { get; } public IStyle Source { get; }
public IStyleable Target { get; } public IStyleable Target { get; }
public void Start() public void Start()
{ {
var hasActivator = _activator is object; var hasActivator = HasActivator;
if (_setters is object) if (_setters is not null)
{ {
foreach (var setter in _setters) foreach (var setter in _setters)
{ {
@ -76,7 +77,7 @@ namespace Avalonia.Styling
{ {
_activator!.Subscribe(this, 0); _activator!.Subscribe(this, 0);
} }
else if (_animationTrigger != null) else if (_animationTrigger is not null)
{ {
_animationTrigger.OnNext(true); _animationTrigger.OnNext(true);
} }
@ -84,7 +85,7 @@ namespace Avalonia.Styling
public void Dispose() public void Dispose()
{ {
if (_setters is object) if (_setters is not null)
{ {
foreach (var setter in _setters) foreach (var setter in _setters)
{ {
@ -92,11 +93,11 @@ namespace Avalonia.Styling
} }
} }
if (_animations is object) if (_animations is not null)
{ {
foreach (var subscripion in _animations) foreach (var subscription in _animations)
{ {
subscripion.Dispose(); subscription.Dispose();
} }
} }
@ -111,7 +112,7 @@ namespace Avalonia.Styling
_animationTrigger?.OnNext(value); _animationTrigger?.OnNext(value);
if (_setters is object) if (_setters is not null)
{ {
if (IsActive) if (IsActive)
{ {

3
src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs

@ -60,7 +60,8 @@ namespace Avalonia.Diagnostics.ViewModels
var styleDiagnostics = styledElement.GetStyleDiagnostics(); var styleDiagnostics = styledElement.GetStyleDiagnostics();
foreach (var appliedStyle in styleDiagnostics.AppliedStyles) // We need to place styles without activator first, such styles will be overwritten by ones with activators.
foreach (var appliedStyle in styleDiagnostics.AppliedStyles.OrderBy(s => s.HasActivator))
{ {
var styleSource = appliedStyle.Source; var styleSource = appliedStyle.Source;

Loading…
Cancel
Save