Browse Source

Refactored most of Style into StyleBase.

Ready for `ControlTheme` class, which is a style without a selector.
pull/8262/head
Steven Kirk 4 years ago
parent
commit
c02439aaaf
  1. 148
      src/Avalonia.Base/Styling/Style.cs
  2. 137
      src/Avalonia.Base/Styling/StyleBase.cs
  3. 10
      src/Avalonia.Base/Styling/StyleChildren.cs

148
src/Avalonia.Base/Styling/Style.cs

@ -1,23 +1,12 @@
using System;
using System.Collections.Generic;
using Avalonia.Animation;
using Avalonia.Controls;
using Avalonia.Metadata;
namespace Avalonia.Styling
{
/// <summary>
/// Defines a style.
/// </summary>
public class Style : AvaloniaObject, IStyle, IResourceProvider
public class Style : StyleBase
{
private IResourceHost? _owner;
private StyleChildren? _children;
private IResourceDictionary? _resources;
private List<ISetter>? _setters;
private List<IAnimation>? _animations;
private StyleCache? _childCache;
/// <summary>
/// Initializes a new instance of the <see cref="Style"/> class.
/// </summary>
@ -34,114 +23,11 @@ namespace Avalonia.Styling
Selector = selector(null);
}
/// <summary>
/// Gets the children of the style.
/// </summary>
public IList<IStyle> Children => _children ??= new(this);
/// <summary>
/// Gets the <see cref="StyledElement"/> or Application that hosts the style.
/// </summary>
public IResourceHost? Owner
{
get => _owner;
private set
{
if (_owner != value)
{
_owner = value;
OwnerChanged?.Invoke(this, EventArgs.Empty);
}
}
}
/// <summary>
/// Gets the parent style if this style is hosted in a <see cref="Style.Children"/> collection.
/// </summary>
public Style? Parent { get; private set; }
/// <summary>
/// Gets or sets a dictionary of style resources.
/// </summary>
public IResourceDictionary Resources
{
get => _resources ?? (Resources = new ResourceDictionary());
set
{
value = value ?? throw new ArgumentNullException(nameof(value));
var hadResources = _resources?.HasResources ?? false;
_resources = value;
if (Owner is object)
{
_resources.AddOwner(Owner);
if (hadResources || _resources.HasResources)
{
Owner.NotifyHostedResourcesChanged(ResourcesChangedEventArgs.Empty);
}
}
}
}
/// <summary>
/// Gets or sets the style's selector.
/// </summary>
public Selector? Selector { get; set; }
/// <summary>
/// Gets the style's setters.
/// </summary>
public IList<ISetter> Setters => _setters ??= new List<ISetter>();
/// <summary>
/// Gets the style's animations.
/// </summary>
public IList<IAnimation> Animations => _animations ??= new List<IAnimation>();
bool IResourceNode.HasResources => _resources?.Count > 0;
IReadOnlyList<IStyle> IStyle.Children => (IReadOnlyList<IStyle>?)_children ?? Array.Empty<IStyle>();
public event EventHandler? OwnerChanged;
public void Add(ISetter setter) => Setters.Add(setter);
public void Add(IStyle style) => Children.Add(style);
public SelectorMatchResult TryAttach(IStyleable target, IStyleHost? host)
{
target = target ?? throw new ArgumentNullException(nameof(target));
var match = Selector is object ? Selector.Match(target, Parent) :
target == host ? SelectorMatch.AlwaysThisInstance : SelectorMatch.NeverThisInstance;
if (match.IsMatch && (_setters is object || _animations is object))
{
var instance = new StyleInstance(this, target, _setters, _animations, match.Activator);
target.StyleApplied(instance);
instance.Start();
}
var result = match.Result;
if (_children is not null)
{
_childCache ??= new StyleCache();
var childResult = _childCache.TryAttach(_children, target, host);
if (childResult > result)
result = childResult;
}
return result;
}
public bool TryGetResource(object key, out object? result)
{
result = null;
return _resources?.TryGetResource(key, out result) ?? false;
}
/// <summary>
/// Returns a string representation of the style.
/// </summary>
@ -158,33 +44,17 @@ namespace Avalonia.Styling
}
}
void IResourceProvider.AddOwner(IResourceHost owner)
{
owner = owner ?? throw new ArgumentNullException(nameof(owner));
if (Owner != null)
{
throw new InvalidOperationException("The Style already has a parent.");
}
Owner = owner;
_resources?.AddOwner(owner);
}
void IResourceProvider.RemoveOwner(IResourceHost owner)
protected override SelectorMatch Matches(IStyleable target, IStyleHost? host)
{
owner = owner ?? throw new ArgumentNullException(nameof(owner));
if (Owner == owner)
{
Owner = null;
_resources?.RemoveOwner(owner);
}
return Selector?.Match(target, Parent) ??
(target == host ?
SelectorMatch.AlwaysThisInstance :
SelectorMatch.NeverThisInstance);
}
internal void SetParent(Style? parent)
internal override void SetParent(StyleBase? parent)
{
if (parent?.Selector is not null)
if (parent is Style parentStyle && parentStyle.Selector is not null)
{
if (Selector is null)
throw new InvalidOperationException("Child styles must have a selector.");
@ -192,7 +62,7 @@ namespace Avalonia.Styling
throw new InvalidOperationException("Child styles must have a nesting selector.");
}
Parent = parent;
base.SetParent(parent);
}
}
}

137
src/Avalonia.Base/Styling/StyleBase.cs

@ -0,0 +1,137 @@
using System;
using System.Collections.Generic;
using Avalonia.Animation;
using Avalonia.Controls;
using Avalonia.Metadata;
namespace Avalonia.Styling
{
/// <summary>
/// Base class for <see cref="Style"/> and <see cref="ControlTheme"/>.
/// </summary>
public abstract class StyleBase : AvaloniaObject, IStyle, IResourceProvider
{
private IResourceHost? _owner;
private StyleChildren? _children;
private IResourceDictionary? _resources;
private List<ISetter>? _setters;
private List<IAnimation>? _animations;
private StyleCache? _childCache;
public IList<IStyle> Children => _children ??= new(this);
public IResourceHost? Owner
{
get => _owner;
private set
{
if (_owner != value)
{
_owner = value;
OwnerChanged?.Invoke(this, EventArgs.Empty);
}
}
}
public IStyle? Parent { get; private set; }
public IResourceDictionary Resources
{
get => _resources ?? (Resources = new ResourceDictionary());
set
{
value = value ?? throw new ArgumentNullException(nameof(value));
var hadResources = _resources?.HasResources ?? false;
_resources = value;
if (Owner is object)
{
_resources.AddOwner(Owner);
if (hadResources || _resources.HasResources)
{
Owner.NotifyHostedResourcesChanged(ResourcesChangedEventArgs.Empty);
}
}
}
}
public IList<ISetter> Setters => _setters ??= new List<ISetter>();
public IList<IAnimation> Animations => _animations ??= new List<IAnimation>();
bool IResourceNode.HasResources => _resources?.Count > 0;
IReadOnlyList<IStyle> IStyle.Children => (IReadOnlyList<IStyle>?)_children ?? Array.Empty<IStyle>();
public void Add(ISetter setter) => Setters.Add(setter);
public void Add(IStyle style) => Children.Add(style);
public event EventHandler? OwnerChanged;
public SelectorMatchResult TryAttach(IStyleable target, IStyleHost? host)
{
target = target ?? throw new ArgumentNullException(nameof(target));
var result = SelectorMatchResult.NeverThisType;
if (_setters?.Count > 0 || _animations?.Count > 0)
{
var match = Matches(target, host);
if (match.IsMatch)
{
var instance = new StyleInstance(this, target, _setters, _animations, match.Activator);
target.StyleApplied(instance);
instance.Start();
}
result = match.Result;
}
if (_children is not null)
{
_childCache ??= new StyleCache();
var childResult = _childCache.TryAttach(_children, target, host);
if (childResult > result)
result = childResult;
}
return result;
}
public bool TryGetResource(object key, out object? result)
{
result = null;
return _resources?.TryGetResource(key, out result) ?? false;
}
protected abstract SelectorMatch Matches(IStyleable target, IStyleHost? host);
internal virtual void SetParent(StyleBase? parent) => Parent = parent;
void IResourceProvider.AddOwner(IResourceHost owner)
{
owner = owner ?? throw new ArgumentNullException(nameof(owner));
if (Owner != null)
{
throw new InvalidOperationException("The Style already has a parent.");
}
Owner = owner;
_resources?.AddOwner(owner);
}
void IResourceProvider.RemoveOwner(IResourceHost owner)
{
owner = owner ?? throw new ArgumentNullException(nameof(owner));
if (Owner == owner)
{
Owner = null;
_resources?.RemoveOwner(owner);
}
}
}
}

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

@ -5,20 +5,20 @@ namespace Avalonia.Styling
{
internal class StyleChildren : Collection<IStyle>
{
private readonly Style _owner;
private readonly StyleBase _owner;
public StyleChildren(Style owner) => _owner = owner;
public StyleChildren(StyleBase owner) => _owner = owner;
protected override void InsertItem(int index, IStyle item)
{
(item as Style)?.SetParent(_owner);
(item as StyleBase)?.SetParent(_owner);
base.InsertItem(index, item);
}
protected override void RemoveItem(int index)
{
var item = Items[index];
(item as Style)?.SetParent(null);
(item as StyleBase)?.SetParent(null);
if (_owner.Owner is IResourceHost host)
(item as IResourceProvider)?.RemoveOwner(host);
base.RemoveItem(index);
@ -26,7 +26,7 @@ namespace Avalonia.Styling
protected override void SetItem(int index, IStyle item)
{
(item as Style)?.SetParent(_owner);
(item as StyleBase)?.SetParent(_owner);
base.SetItem(index, item);
if (_owner.Owner is IResourceHost host)
(item as IResourceProvider)?.AddOwner(host);

Loading…
Cancel
Save