Browse Source

Merge branch 'master' into fixes/virtualizing-null-recycle-key

pull/11421/head
Max Katz 3 years ago
committed by GitHub
parent
commit
3ec3c0efd6
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 18
      src/Avalonia.Base/Diagnostics/AppliedStyle.cs
  2. 4
      src/Avalonia.Base/Diagnostics/StyleDiagnostics.cs
  3. 4
      src/Avalonia.Base/StyledElement.cs
  4. 3
      src/Avalonia.Base/Styling/Activators/IStyleActivator.cs
  5. 3
      src/Avalonia.Base/Styling/Activators/IStyleActivatorSink.cs
  6. 12
      src/Avalonia.Base/Styling/ChildSelector.cs
  7. 12
      src/Avalonia.Base/Styling/DescendentSelector.cs
  8. 24
      src/Avalonia.Base/Styling/ISetter.cs
  9. 2
      src/Avalonia.Base/Styling/ISetterInstance.cs
  10. 4
      src/Avalonia.Base/Styling/ISetterValue.cs
  11. 3
      src/Avalonia.Base/Styling/IStyleInstance.cs
  12. 12
      src/Avalonia.Base/Styling/NestingSelector.cs
  13. 12
      src/Avalonia.Base/Styling/NotSelector.cs
  14. 14
      src/Avalonia.Base/Styling/NthChildSelector.cs
  15. 2
      src/Avalonia.Base/Styling/NthLastChildSelector.cs
  16. 12
      src/Avalonia.Base/Styling/OrSelector.cs
  17. 12
      src/Avalonia.Base/Styling/PropertyEqualsSelector.cs
  18. 14
      src/Avalonia.Base/Styling/Selector.cs
  19. 4
      src/Avalonia.Base/Styling/SelectorMatch.cs
  20. 4
      src/Avalonia.Base/Styling/Setter.cs
  21. 12
      src/Avalonia.Base/Styling/SetterBase.cs
  22. 6
      src/Avalonia.Base/Styling/StyleBase.cs
  23. 12
      src/Avalonia.Base/Styling/TemplateSelector.cs
  24. 12
      src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs
  25. 2
      src/Avalonia.Controls/ContextMenu.cs
  26. 2
      src/Avalonia.Controls/Control.cs
  27. 2
      src/Avalonia.Controls/VirtualizingStackPanel.cs
  28. 2
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs
  29. 4
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/StyleViewModel.cs
  30. 2
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathTransformer.cs
  31. 4
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
  32. 2
      src/Markup/Avalonia.Markup/Data/TemplateBinding.cs
  33. 36
      tests/Avalonia.Benchmarks/Styling/SelectorBenchmark.cs
  34. 36
      tests/Avalonia.Controls.UnitTests/VirtualizingStackPanelTests.cs
  35. 4
      tests/Avalonia.UnitTests/StyleHelpers.cs

18
src/Avalonia.Base/Diagnostics/AppliedStyle.cs

@ -0,0 +1,18 @@
using Avalonia.Styling;
namespace Avalonia.Diagnostics
{
public class AppliedStyle
{
private readonly IStyleInstance _instance;
internal AppliedStyle(IStyleInstance instance)
{
_instance = instance;
}
public bool HasActivator => _instance.HasActivator;
public bool IsActive => _instance.IsActive;
public StyleBase Style => (StyleBase)_instance.Source;
}
}

4
src/Avalonia.Base/Diagnostics/StyleDiagnostics.cs

@ -11,9 +11,9 @@ namespace Avalonia.Diagnostics
/// <summary> /// <summary>
/// Currently applied styles. /// Currently applied styles.
/// </summary> /// </summary>
public IReadOnlyList<IStyleInstance> AppliedStyles { get; } public IReadOnlyList<AppliedStyle> AppliedStyles { get; }
public StyleDiagnostics(IReadOnlyList<IStyleInstance> appliedStyles) public StyleDiagnostics(IReadOnlyList<AppliedStyle> appliedStyles)
{ {
AppliedStyles = appliedStyles; AppliedStyles = appliedStyles;
} }

4
src/Avalonia.Base/StyledElement.cs

@ -420,12 +420,12 @@ namespace Avalonia
internal StyleDiagnostics GetStyleDiagnosticsInternal() internal StyleDiagnostics GetStyleDiagnosticsInternal()
{ {
var styles = new List<IStyleInstance>(); var styles = new List<AppliedStyle>();
foreach (var frame in GetValueStore().Frames) foreach (var frame in GetValueStore().Frames)
{ {
if (frame is IStyleInstance style) if (frame is IStyleInstance style)
styles.Add(style); styles.Add(new(style));
} }
return new StyleDiagnostics(styles); return new StyleDiagnostics(styles);

3
src/Avalonia.Base/Styling/Activators/IStyleActivator.cs

@ -15,8 +15,7 @@ namespace Avalonia.Styling.Activators
/// - The activation state can be re-evaluated at any time by calling <see cref="GetIsActive"/> /// - The activation state can be re-evaluated at any time by calling <see cref="GetIsActive"/>
/// - No error or completion messages /// - No error or completion messages
/// </remarks> /// </remarks>
[Unstable] internal interface IStyleActivator : IDisposable
public interface IStyleActivator : IDisposable
{ {
/// <summary> /// <summary>
/// Gets a value indicating whether the style is subscribed. /// Gets a value indicating whether the style is subscribed.

3
src/Avalonia.Base/Styling/Activators/IStyleActivatorSink.cs

@ -5,8 +5,7 @@ namespace Avalonia.Styling.Activators
/// <summary> /// <summary>
/// Receives notifications from an <see cref="IStyleActivator"/>. /// Receives notifications from an <see cref="IStyleActivator"/>.
/// </summary> /// </summary>
[Unstable] internal interface IStyleActivatorSink
public interface IStyleActivatorSink
{ {
/// <summary> /// <summary>
/// Called when the subscribed activator value changes. /// Called when the subscribed activator value changes.

12
src/Avalonia.Base/Styling/ChildSelector.cs

@ -19,13 +19,13 @@ namespace Avalonia.Styling
} }
/// <inheritdoc/> /// <inheritdoc/>
public override bool InTemplate => _parent.InTemplate; internal override bool InTemplate => _parent.InTemplate;
/// <inheritdoc/> /// <inheritdoc/>
public override bool IsCombinator => true; internal override bool IsCombinator => true;
/// <inheritdoc/> /// <inheritdoc/>
public override Type? TargetType => null; internal override Type? TargetType => null;
public override string ToString(Style? owner) public override string ToString(Style? owner)
{ {
@ -37,7 +37,7 @@ namespace Avalonia.Styling
return _selectorString; return _selectorString;
} }
protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe) private protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe)
{ {
var controlParent = ((ILogical)control).LogicalParent; var controlParent = ((ILogical)control).LogicalParent;
@ -64,7 +64,7 @@ namespace Avalonia.Styling
} }
} }
protected override Selector? MovePrevious() => null; private protected override Selector? MovePrevious() => null;
protected override Selector? MovePreviousOrParent() => _parent; private protected override Selector? MovePreviousOrParent() => _parent;
} }
} }

12
src/Avalonia.Base/Styling/DescendentSelector.cs

@ -17,13 +17,13 @@ namespace Avalonia.Styling
} }
/// <inheritdoc/> /// <inheritdoc/>
public override bool IsCombinator => true; internal override bool IsCombinator => true;
/// <inheritdoc/> /// <inheritdoc/>
public override bool InTemplate => _parent.InTemplate; internal override bool InTemplate => _parent.InTemplate;
/// <inheritdoc/> /// <inheritdoc/>
public override Type? TargetType => null; internal override Type? TargetType => null;
public override string ToString(Style? owner) public override string ToString(Style? owner)
{ {
@ -35,7 +35,7 @@ namespace Avalonia.Styling
return _selectorString; return _selectorString;
} }
protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe) private protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe)
{ {
var c = (ILogical)control; var c = (ILogical)control;
var descendantMatches = new OrActivatorBuilder(); var descendantMatches = new OrActivatorBuilder();
@ -69,7 +69,7 @@ namespace Avalonia.Styling
} }
} }
protected override Selector? MovePrevious() => null; private protected override Selector? MovePrevious() => null;
protected override Selector? MovePreviousOrParent() => _parent; private protected override Selector? MovePreviousOrParent() => _parent;
} }
} }

24
src/Avalonia.Base/Styling/ISetter.cs

@ -1,24 +0,0 @@
using System;
using Avalonia.Metadata;
namespace Avalonia.Styling
{
/// <summary>
/// Represents a setter for a <see cref="Style"/>.
/// </summary>
[NotClientImplementable]
public interface ISetter
{
/// <summary>
/// Instances a setter on a control.
/// </summary>
/// <param name="styleInstance">The style which contains the setter.</param>
/// <param name="target">The control.</param>
/// <returns>An <see cref="ISetterInstance"/>.</returns>
/// <remarks>
/// This method should return an <see cref="ISetterInstance"/> which can be used to apply
/// the setter to the specified control.
/// </remarks>
ISetterInstance Instance(IStyleInstance styleInstance, StyledElement target);
}
}

2
src/Avalonia.Base/Styling/ISetterInstance.cs

@ -3,7 +3,7 @@
namespace Avalonia.Styling namespace Avalonia.Styling
{ {
/// <summary> /// <summary>
/// Represents an <see cref="ISetter"/> that has been instanced on a control. /// Represents a <see cref="Setter"/> that has been instanced on a control.
/// </summary> /// </summary>
[Unstable] [Unstable]
public interface ISetterInstance public interface ISetterInstance

4
src/Avalonia.Base/Styling/ISetterValue.cs

@ -3,13 +3,13 @@
namespace Avalonia.Styling namespace Avalonia.Styling
{ {
/// <summary> /// <summary>
/// Customizes the behavior of a class when added as a value to an <see cref="ISetter"/>. /// Customizes the behavior of a class when added as a value to a <see cref="SetterBase"/>.
/// </summary> /// </summary>
public interface ISetterValue public interface ISetterValue
{ {
/// <summary> /// <summary>
/// Notifies that the object has been added as a setter value. /// Notifies that the object has been added as a setter value.
/// </summary> /// </summary>
void Initialize(ISetter setter); void Initialize(SetterBase setter);
} }
} }

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

@ -5,8 +5,7 @@ namespace Avalonia.Styling
/// <summary> /// <summary>
/// Represents a <see cref="Style"/> that has been instanced on a control. /// Represents a <see cref="Style"/> that has been instanced on a control.
/// </summary> /// </summary>
[Unstable] internal interface IStyleInstance
public interface IStyleInstance
{ {
/// <summary> /// <summary>
/// Gets the source style. /// Gets the source style.

12
src/Avalonia.Base/Styling/NestingSelector.cs

@ -7,13 +7,13 @@ namespace Avalonia.Styling
/// </summary> /// </summary>
internal class NestingSelector : Selector internal class NestingSelector : Selector
{ {
public override bool InTemplate => false; internal override bool InTemplate => false;
public override bool IsCombinator => false; internal override bool IsCombinator => false;
public override Type? TargetType => null; internal override Type? TargetType => null;
public override string ToString(Style? owner) => owner?.Parent?.ToString() ?? "^"; public override string ToString(Style? owner) => owner?.Parent?.ToString() ?? "^";
protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe) private protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe)
{ {
if (parent is Style s && s.Selector is not null) if (parent is Style s && s.Selector is not null)
{ {
@ -32,7 +32,7 @@ namespace Avalonia.Styling
"Nesting selector was specified but cannot determine parent selector."); "Nesting selector was specified but cannot determine parent selector.");
} }
protected override Selector? MovePrevious() => null; private protected override Selector? MovePrevious() => null;
protected override Selector? MovePreviousOrParent() => null; private protected override Selector? MovePreviousOrParent() => null;
} }
} }

12
src/Avalonia.Base/Styling/NotSelector.cs

@ -26,13 +26,13 @@ namespace Avalonia.Styling
} }
/// <inheritdoc/> /// <inheritdoc/>
public override bool InTemplate => _argument.InTemplate; internal override bool InTemplate => _argument.InTemplate;
/// <inheritdoc/> /// <inheritdoc/>
public override bool IsCombinator => false; internal override bool IsCombinator => false;
/// <inheritdoc/> /// <inheritdoc/>
public override Type? TargetType => _previous?.TargetType; internal override Type? TargetType => _previous?.TargetType;
/// <inheritdoc/> /// <inheritdoc/>
public override string ToString(Style? owner) public override string ToString(Style? owner)
@ -45,7 +45,7 @@ namespace Avalonia.Styling
return _selectorString; return _selectorString;
} }
protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe) private protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe)
{ {
var innerResult = _argument.Match(control, parent, subscribe); var innerResult = _argument.Match(control, parent, subscribe);
@ -66,7 +66,7 @@ namespace Avalonia.Styling
} }
} }
protected override Selector? MovePrevious() => _previous; private protected override Selector? MovePrevious() => _previous;
protected override Selector? MovePreviousOrParent() => _previous; private protected override Selector? MovePreviousOrParent() => _previous;
} }
} }

14
src/Avalonia.Base/Styling/NthChildSelector.cs

@ -12,7 +12,7 @@ namespace Avalonia.Styling
/// <remarks> /// <remarks>
/// Element indices are 1-based. /// Element indices are 1-based.
/// </remarks> /// </remarks>
public class NthChildSelector : Selector internal class NthChildSelector : Selector
{ {
private const string NthChildSelectorName = "nth-child"; private const string NthChildSelectorName = "nth-child";
private const string NthLastChildSelectorName = "nth-last-child"; private const string NthLastChildSelectorName = "nth-last-child";
@ -39,16 +39,16 @@ namespace Avalonia.Styling
} }
public override bool InTemplate => _previous?.InTemplate ?? false; internal override bool InTemplate => _previous?.InTemplate ?? false;
public override bool IsCombinator => false; internal override bool IsCombinator => false;
public override Type? TargetType => _previous?.TargetType; internal override Type? TargetType => _previous?.TargetType;
public int Step { get; } public int Step { get; }
public int Offset { get; } public int Offset { get; }
protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe) private protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe)
{ {
if (!(control is ILogical logical)) if (!(control is ILogical logical))
{ {
@ -103,8 +103,8 @@ namespace Avalonia.Styling
return match ? SelectorMatch.AlwaysThisInstance : SelectorMatch.NeverThisInstance; return match ? SelectorMatch.AlwaysThisInstance : SelectorMatch.NeverThisInstance;
} }
protected override Selector? MovePrevious() => _previous; private protected override Selector? MovePrevious() => _previous;
protected override Selector? MovePreviousOrParent() => _previous; private protected override Selector? MovePreviousOrParent() => _previous;
public override string ToString(Style? owner) public override string ToString(Style? owner)
{ {

2
src/Avalonia.Base/Styling/NthLastChildSelector.cs

@ -8,7 +8,7 @@ namespace Avalonia.Styling
/// <remarks> /// <remarks>
/// Element indices are 1-based. /// Element indices are 1-based.
/// </remarks> /// </remarks>
public class NthLastChildSelector : NthChildSelector internal class NthLastChildSelector : NthChildSelector
{ {
/// <summary> /// <summary>
/// Creates an instance of <see cref="NthLastChildSelector"/> /// Creates an instance of <see cref="NthLastChildSelector"/>

12
src/Avalonia.Base/Styling/OrSelector.cs

@ -36,13 +36,13 @@ namespace Avalonia.Styling
} }
/// <inheritdoc/> /// <inheritdoc/>
public override bool InTemplate => false; internal override bool InTemplate => false;
/// <inheritdoc/> /// <inheritdoc/>
public override bool IsCombinator => false; internal override bool IsCombinator => false;
/// <inheritdoc/> /// <inheritdoc/>
public override Type? TargetType => _targetType ??= EvaluateTargetType(); internal override Type? TargetType => _targetType ??= EvaluateTargetType();
/// <inheritdoc/> /// <inheritdoc/>
public override string ToString(Style? owner) public override string ToString(Style? owner)
@ -55,7 +55,7 @@ namespace Avalonia.Styling
return _selectorString; return _selectorString;
} }
protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe) private protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe)
{ {
var activators = new OrActivatorBuilder(); var activators = new OrActivatorBuilder();
var neverThisInstance = false; var neverThisInstance = false;
@ -94,8 +94,8 @@ namespace Avalonia.Styling
} }
} }
protected override Selector? MovePrevious() => null; private protected override Selector? MovePrevious() => null;
protected override Selector? MovePreviousOrParent() => null; private protected override Selector? MovePreviousOrParent() => null;
internal override void ValidateNestingSelector(bool inControlTheme) internal override void ValidateNestingSelector(bool inControlTheme)
{ {

12
src/Avalonia.Base/Styling/PropertyEqualsSelector.cs

@ -28,13 +28,13 @@ namespace Avalonia.Styling
} }
/// <inheritdoc/> /// <inheritdoc/>
public override bool InTemplate => _previous?.InTemplate ?? false; internal override bool InTemplate => _previous?.InTemplate ?? false;
/// <inheritdoc/> /// <inheritdoc/>
public override bool IsCombinator => false; internal override bool IsCombinator => false;
/// <inheritdoc/> /// <inheritdoc/>
public override Type? TargetType => _previous?.TargetType; internal override Type? TargetType => _previous?.TargetType;
/// <inheritdoc/> /// <inheritdoc/>
public override string ToString(Style? owner) public override string ToString(Style? owner)
@ -73,7 +73,7 @@ namespace Avalonia.Styling
} }
/// <inheritdoc/> /// <inheritdoc/>
protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe) private protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe)
{ {
if (subscribe) if (subscribe)
{ {
@ -88,8 +88,8 @@ namespace Avalonia.Styling
} }
protected override Selector? MovePrevious() => _previous; private protected override Selector? MovePrevious() => _previous;
protected override Selector? MovePreviousOrParent() => _previous; private protected override Selector? MovePreviousOrParent() => _previous;
[UnconditionalSuppressMessage("Trimming", "IL2026", Justification = TrimmingMessages.TypeConvertionSupressWarningMessage)] [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = TrimmingMessages.TypeConvertionSupressWarningMessage)]
[UnconditionalSuppressMessage("Trimming", "IL2067", Justification = TrimmingMessages.TypeConvertionSupressWarningMessage)] [UnconditionalSuppressMessage("Trimming", "IL2067", Justification = TrimmingMessages.TypeConvertionSupressWarningMessage)]

14
src/Avalonia.Base/Styling/Selector.cs

@ -14,7 +14,7 @@ namespace Avalonia.Styling
/// Gets a value indicating whether either this selector or a previous selector has moved /// Gets a value indicating whether either this selector or a previous selector has moved
/// into a template. /// into a template.
/// </summary> /// </summary>
public abstract bool InTemplate { get; } internal abstract bool InTemplate { get; }
/// <summary> /// <summary>
/// Gets a value indicating whether this selector is a combinator. /// Gets a value indicating whether this selector is a combinator.
@ -22,12 +22,12 @@ namespace Avalonia.Styling
/// <remarks> /// <remarks>
/// A combinator is a selector such as Child or Descendent which links simple selectors. /// A combinator is a selector such as Child or Descendent which links simple selectors.
/// </remarks> /// </remarks>
public abstract bool IsCombinator { get; } internal abstract bool IsCombinator { get; }
/// <summary> /// <summary>
/// Gets the target type of the selector, if available. /// Gets the target type of the selector, if available.
/// </summary> /// </summary>
public abstract Type? TargetType { get; } internal abstract Type? TargetType { get; }
/// <summary> /// <summary>
/// Tries to match the selector with a control. /// Tries to match the selector with a control.
@ -41,7 +41,7 @@ namespace Avalonia.Styling
/// or simply return an immediate result. /// or simply return an immediate result.
/// </param> /// </param>
/// <returns>A <see cref="SelectorMatch"/>.</returns> /// <returns>A <see cref="SelectorMatch"/>.</returns>
public SelectorMatch Match(StyledElement control, IStyle? parent = null, bool subscribe = true) internal SelectorMatch Match(StyledElement control, IStyle? parent = null, bool subscribe = true)
{ {
// First match the selector until a combinator is found. Selectors are stored from // First match the selector until a combinator is found. Selectors are stored from
// right-to-left, so MatchUntilCombinator reverses this order because the type selector // right-to-left, so MatchUntilCombinator reverses this order because the type selector
@ -88,17 +88,17 @@ namespace Avalonia.Styling
/// or simply return an immediate result. /// or simply return an immediate result.
/// </param> /// </param>
/// <returns>A <see cref="SelectorMatch"/>.</returns> /// <returns>A <see cref="SelectorMatch"/>.</returns>
protected abstract SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe); private protected abstract SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe);
/// <summary> /// <summary>
/// Moves to the previous selector. /// Moves to the previous selector.
/// </summary> /// </summary>
protected abstract Selector? MovePrevious(); private protected abstract Selector? MovePrevious();
/// <summary> /// <summary>
/// Moves to the previous selector or the parent selector. /// Moves to the previous selector or the parent selector.
/// </summary> /// </summary>
protected abstract Selector? MovePreviousOrParent(); private protected abstract Selector? MovePreviousOrParent();
internal virtual void ValidateNestingSelector(bool inControlTheme) internal virtual void ValidateNestingSelector(bool inControlTheme)
{ {

4
src/Avalonia.Base/Styling/SelectorMatch.cs

@ -8,7 +8,7 @@ namespace Avalonia.Styling
/// <summary> /// <summary>
/// Describes how a <see cref="SelectorMatch"/> matches a control and its type. /// Describes how a <see cref="SelectorMatch"/> matches a control and its type.
/// </summary> /// </summary>
public enum SelectorMatchResult internal enum SelectorMatchResult
{ {
/// <summary> /// <summary>
/// The selector never matches this type. /// The selector never matches this type.
@ -43,7 +43,7 @@ namespace Avalonia.Styling
/// A selector match describes whether and how a <see cref="Selector"/> matches a control, and /// A selector match describes whether and how a <see cref="Selector"/> matches a control, and
/// in addition whether the selector can ever match a control of the same type. /// in addition whether the selector can ever match a control of the same type.
/// </remarks> /// </remarks>
public readonly record struct SelectorMatch internal readonly record struct SelectorMatch
{ {
/// <summary> /// <summary>
/// A selector match with the result of <see cref="SelectorMatchResult.NeverThisType"/>. /// A selector match with the result of <see cref="SelectorMatchResult.NeverThisType"/>.

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

@ -14,7 +14,7 @@ namespace Avalonia.Styling
/// A <see cref="Setter"/> is used to set a <see cref="AvaloniaProperty"/> value on a /// A <see cref="Setter"/> is used to set a <see cref="AvaloniaProperty"/> value on a
/// <see cref="AvaloniaObject"/> depending on a condition. /// <see cref="AvaloniaObject"/> depending on a condition.
/// </remarks> /// </remarks>
public class Setter : ISetter, IValueEntry, ISetterInstance, IAnimationSetter public class Setter : SetterBase, IValueEntry, ISetterInstance, IAnimationSetter
{ {
private object? _value; private object? _value;
private DirectPropertySetterInstance? _direct; private DirectPropertySetterInstance? _direct;
@ -66,7 +66,7 @@ namespace Avalonia.Styling
void IValueEntry.Unsubscribe() { } void IValueEntry.Unsubscribe() { }
[UnconditionalSuppressMessage("Trimming", "IL2026", Justification = TrimmingMessages.ImplicitTypeConvertionSupressWarningMessage)] [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = TrimmingMessages.ImplicitTypeConvertionSupressWarningMessage)]
ISetterInstance ISetter.Instance(IStyleInstance instance, StyledElement target) internal override ISetterInstance Instance(IStyleInstance instance, StyledElement target)
{ {
if (target is not AvaloniaObject ao) if (target is not AvaloniaObject ao)
throw new InvalidOperationException("Don't know how to instance a style on this type."); throw new InvalidOperationException("Don't know how to instance a style on this type.");

12
src/Avalonia.Base/Styling/SetterBase.cs

@ -0,0 +1,12 @@
namespace Avalonia.Styling
{
/// <summary>
/// Represents the base class for value setters.
/// </summary>
public abstract class SetterBase
{
internal abstract ISetterInstance Instance(
IStyleInstance styleInstance,
StyledElement target);
}
}

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

@ -16,7 +16,7 @@ namespace Avalonia.Styling
private IResourceHost? _owner; private IResourceHost? _owner;
private StyleChildren? _children; private StyleChildren? _children;
private IResourceDictionary? _resources; private IResourceDictionary? _resources;
private List<ISetter>? _setters; private List<SetterBase>? _setters;
private List<IAnimation>? _animations; private List<IAnimation>? _animations;
private StyleInstance? _sharedInstance; private StyleInstance? _sharedInstance;
@ -60,7 +60,7 @@ namespace Avalonia.Styling
} }
} }
public IList<ISetter> Setters => _setters ??= new List<ISetter>(); public IList<SetterBase> Setters => _setters ??= new();
public IList<IAnimation> Animations => _animations ??= new List<IAnimation>(); public IList<IAnimation> Animations => _animations ??= new List<IAnimation>();
bool IResourceNode.HasResources => _resources?.Count > 0; bool IResourceNode.HasResources => _resources?.Count > 0;
@ -69,7 +69,7 @@ namespace Avalonia.Styling
internal bool HasChildren => _children?.Count > 0; internal bool HasChildren => _children?.Count > 0;
internal bool HasSettersOrAnimations => _setters?.Count > 0 || _animations?.Count > 0; internal bool HasSettersOrAnimations => _setters?.Count > 0 || _animations?.Count > 0;
public void Add(ISetter setter) => Setters.Add(setter); public void Add(SetterBase setter) => Setters.Add(setter);
public void Add(IStyle style) => Children.Add(style); public void Add(IStyle style) => Children.Add(style);
public event EventHandler? OwnerChanged; public event EventHandler? OwnerChanged;

12
src/Avalonia.Base/Styling/TemplateSelector.cs

@ -18,13 +18,13 @@ namespace Avalonia.Styling
} }
/// <inheritdoc/> /// <inheritdoc/>
public override bool InTemplate => true; internal override bool InTemplate => true;
/// <inheritdoc/> /// <inheritdoc/>
public override bool IsCombinator => true; internal override bool IsCombinator => true;
/// <inheritdoc/> /// <inheritdoc/>
public override Type? TargetType => null; internal override Type? TargetType => null;
public override string ToString(Style? owner) public override string ToString(Style? owner)
{ {
@ -36,7 +36,7 @@ namespace Avalonia.Styling
return _selectorString; return _selectorString;
} }
protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe) private protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe)
{ {
var templatedParent = control.TemplatedParent as StyledElement; var templatedParent = control.TemplatedParent as StyledElement;
@ -48,7 +48,7 @@ namespace Avalonia.Styling
return _parent.Match(templatedParent, parent, subscribe); return _parent.Match(templatedParent, parent, subscribe);
} }
protected override Selector? MovePrevious() => null; private protected override Selector? MovePrevious() => null;
protected override Selector? MovePreviousOrParent() => _parent; private protected override Selector? MovePreviousOrParent() => _parent;
} }
} }

12
src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs

@ -58,7 +58,7 @@ namespace Avalonia.Styling
} }
/// <inheritdoc/> /// <inheritdoc/>
public override bool InTemplate => _previous?.InTemplate ?? false; internal override bool InTemplate => _previous?.InTemplate ?? false;
/// <summary> /// <summary>
/// Gets the name of the control to match. /// Gets the name of the control to match.
@ -66,10 +66,10 @@ namespace Avalonia.Styling
public string? Name { get; set; } public string? Name { get; set; }
/// <inheritdoc/> /// <inheritdoc/>
public override Type? TargetType => _targetType ?? _previous?.TargetType; internal override Type? TargetType => _targetType ?? _previous?.TargetType;
/// <inheritdoc/> /// <inheritdoc/>
public override bool IsCombinator => false; internal override bool IsCombinator => false;
/// <summary> /// <summary>
/// Whether the selector matches the concrete <see cref="TargetType"/> or any object which /// Whether the selector matches the concrete <see cref="TargetType"/> or any object which
@ -89,7 +89,7 @@ namespace Avalonia.Styling
} }
/// <inheritdoc/> /// <inheritdoc/>
protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe) private protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe)
{ {
if (TargetType != null) if (TargetType != null)
{ {
@ -134,8 +134,8 @@ namespace Avalonia.Styling
return Name == null ? SelectorMatch.AlwaysThisType : SelectorMatch.AlwaysThisInstance; return Name == null ? SelectorMatch.AlwaysThisType : SelectorMatch.AlwaysThisInstance;
} }
protected override Selector? MovePrevious() => _previous; private protected override Selector? MovePrevious() => _previous;
protected override Selector? MovePreviousOrParent() => _previous; private protected override Selector? MovePreviousOrParent() => _previous;
private string BuildSelectorString(Style? owner) private string BuildSelectorString(Style? owner)
{ {

2
src/Avalonia.Controls/ContextMenu.cs

@ -285,7 +285,7 @@ namespace Avalonia.Controls
} }
} }
void ISetterValue.Initialize(ISetter setter) void ISetterValue.Initialize(SetterBase setter)
{ {
// ContextMenu can be assigned to the ContextMenu property in a setter. This overrides // ContextMenu can be assigned to the ContextMenu property in a setter. This overrides
// the behavior defined in Control which requires controls to be wrapped in a <template>. // the behavior defined in Control which requires controls to be wrapped in a <template>.

2
src/Avalonia.Controls/Control.cs

@ -213,7 +213,7 @@ namespace Avalonia.Controls
bool IDataTemplateHost.IsDataTemplatesInitialized => _dataTemplates != null; bool IDataTemplateHost.IsDataTemplatesInitialized => _dataTemplates != null;
/// <inheritdoc/> /// <inheritdoc/>
void ISetterValue.Initialize(ISetter setter) void ISetterValue.Initialize(SetterBase setter)
{ {
if (setter is Setter s && s.Property == ContextFlyoutProperty) if (setter is Setter s && s.Property == ContextFlyoutProperty)
{ {

2
src/Avalonia.Controls/VirtualizingStackPanel.cs

@ -343,7 +343,7 @@ namespace Avalonia.Controls
{ {
var items = Items; var items = Items;
if (_isInLayout || index < 0 || index >= items.Count || _realizedElements is null) if (_isInLayout || index < 0 || index >= items.Count || _realizedElements is null || !IsEffectivelyVisible)
return null; return null;
if (GetRealizedElement(index) is Control element) if (GetRealizedElement(index) is Control element)

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

@ -64,7 +64,7 @@ namespace Avalonia.Diagnostics.ViewModels
// We need to place styles without activator first, such styles will be overwritten by ones with activators. // 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)) foreach (var appliedStyle in styleDiagnostics.AppliedStyles.OrderBy(s => s.HasActivator))
{ {
var styleSource = appliedStyle.Source; var styleSource = appliedStyle.Style;
var setters = new List<SetterViewModel>(); var setters = new List<SetterViewModel>();

4
src/Avalonia.Diagnostics/Diagnostics/ViewModels/StyleViewModel.cs

@ -5,11 +5,11 @@ namespace Avalonia.Diagnostics.ViewModels
{ {
internal class StyleViewModel : ViewModelBase internal class StyleViewModel : ViewModelBase
{ {
private readonly IStyleInstance _styleInstance; private readonly AppliedStyle _styleInstance;
private bool _isActive; private bool _isActive;
private bool _isVisible; private bool _isVisible;
public StyleViewModel(IStyleInstance styleInstance, string name, List<SetterViewModel> setters) public StyleViewModel(AppliedStyle styleInstance, string name, List<SetterViewModel> setters)
{ {
_styleInstance = styleInstance; _styleInstance = styleInstance;
IsVisible = true; IsVisible = true;

2
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathTransformer.cs

@ -125,7 +125,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
var selfType = context.ParentNodes().OfType<XamlAstConstructableObjectNode>().First().Type.GetClrType(); var selfType = context.ParentNodes().OfType<XamlAstConstructableObjectNode>().First().Type.GetClrType();
// When using self bindings with setters we need to change target type to resolved selector type. // When using self bindings with setters we need to change target type to resolved selector type.
if (context.GetAvaloniaTypes().ISetter.IsAssignableFrom(selfType)) if (context.GetAvaloniaTypes().SetterBase.IsAssignableFrom(selfType))
{ {
selfType = context.ParentNodes().OfType<AvaloniaXamlIlTargetTypeMetadataNode>().First().TargetType.GetClrType(); selfType = context.ParentNodes().OfType<AvaloniaXamlIlTargetTypeMetadataNode>().First().TargetType.GetClrType();
} }

4
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs

@ -104,7 +104,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
public IXamlType TextDecorationCollection { get; } public IXamlType TextDecorationCollection { get; }
public IXamlType TextDecorations { get; } public IXamlType TextDecorations { get; }
public IXamlType TextTrimming { get; } public IXamlType TextTrimming { get; }
public IXamlType ISetter { get; } public IXamlType SetterBase { get; }
public IXamlType IStyle { get; } public IXamlType IStyle { get; }
public IXamlType StyleInclude { get; } public IXamlType StyleInclude { get; }
public IXamlType ResourceInclude { get; } public IXamlType ResourceInclude { get; }
@ -244,7 +244,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
TextDecorationCollection = cfg.TypeSystem.GetType("Avalonia.Media.TextDecorationCollection"); TextDecorationCollection = cfg.TypeSystem.GetType("Avalonia.Media.TextDecorationCollection");
TextDecorations = cfg.TypeSystem.GetType("Avalonia.Media.TextDecorations"); TextDecorations = cfg.TypeSystem.GetType("Avalonia.Media.TextDecorations");
TextTrimming = cfg.TypeSystem.GetType("Avalonia.Media.TextTrimming"); TextTrimming = cfg.TypeSystem.GetType("Avalonia.Media.TextTrimming");
ISetter = cfg.TypeSystem.GetType("Avalonia.Styling.ISetter"); SetterBase = cfg.TypeSystem.GetType("Avalonia.Styling.SetterBase");
IStyle = cfg.TypeSystem.GetType("Avalonia.Styling.IStyle"); IStyle = cfg.TypeSystem.GetType("Avalonia.Styling.IStyle");
StyleInclude = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.Styling.StyleInclude"); StyleInclude = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.Styling.StyleInclude");
ResourceInclude = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.Styling.ResourceInclude"); ResourceInclude = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.Styling.ResourceInclude");

2
src/Markup/Avalonia.Markup/Data/TemplateBinding.cs

@ -109,7 +109,7 @@ namespace Avalonia.Data
} }
/// <inheritdoc/> /// <inheritdoc/>
void ISetterValue.Initialize(ISetter setter) => _isSetterValue = true; void ISetterValue.Initialize(SetterBase setter) => _isSetterValue = true;
protected override void Subscribed() protected override void Subscribed()
{ {

36
tests/Avalonia.Benchmarks/Styling/SelectorBenchmark.cs

@ -37,62 +37,62 @@ namespace Avalonia.Benchmarks.Styling
} }
[Benchmark] [Benchmark]
public SelectorMatch IsSelector_NoMatch() public void IsSelector_NoMatch()
{ {
return _isCalendarSelector.Match(_notMatchingControl); _isCalendarSelector.Match(_notMatchingControl);
} }
[Benchmark] [Benchmark]
public SelectorMatch IsSelector_Match() public void IsSelector_Match()
{ {
return _isCalendarSelector.Match(_matchingControl); _isCalendarSelector.Match(_matchingControl);
} }
[Benchmark] [Benchmark]
public SelectorMatch ClassSelector_NoMatch() public void ClassSelector_NoMatch()
{ {
return _classSelector.Match(_notMatchingControl); _classSelector.Match(_notMatchingControl);
} }
[Benchmark] [Benchmark]
public SelectorMatch ClassSelector_Match() public void ClassSelector_Match()
{ {
return _classSelector.Match(_matchingControl); _classSelector.Match(_matchingControl);
} }
[Benchmark] [Benchmark]
public SelectorMatch OrSelector_One_Match() public void OrSelector_One_Match()
{ {
return _orSelectorTwo.Match(_matchingControl); _orSelectorTwo.Match(_matchingControl);
} }
[Benchmark] [Benchmark]
public SelectorMatch OrSelector_Five_Match() public void OrSelector_Five_Match()
{ {
return _orSelectorFive.Match(_matchingControl); _orSelectorFive.Match(_matchingControl);
} }
} }
internal class AlwaysMatchSelector : Selector internal class AlwaysMatchSelector : Selector
{ {
public override bool InTemplate => false; internal override bool InTemplate => false;
public override bool IsCombinator => false; internal override bool IsCombinator => false;
public override Type TargetType => null; internal override Type TargetType => null;
public override string ToString(Style owner) public override string ToString(Style owner)
{ {
return "Always"; return "Always";
} }
protected override SelectorMatch Evaluate(StyledElement control, IStyle parent, bool subscribe) private protected override SelectorMatch Evaluate(StyledElement control, IStyle parent, bool subscribe)
{ {
return SelectorMatch.AlwaysThisType; return SelectorMatch.AlwaysThisType;
} }
protected override Selector MovePrevious() => null; private protected override Selector MovePrevious() => null;
protected override Selector MovePreviousOrParent() => null; private protected override Selector MovePreviousOrParent() => null;
} }
} }

36
tests/Avalonia.Controls.UnitTests/VirtualizingStackPanelTests.cs

@ -692,7 +692,41 @@ namespace Avalonia.Controls.UnitTests
Assert.Null(firstItem.Parent); Assert.Null(firstItem.Parent);
Assert.Null(firstItem.VisualParent); Assert.Null(firstItem.VisualParent);
Assert.Empty(itemsControl.ItemsPanelRoot!.Children); Assert.Empty(itemsControl.ItemsPanelRoot!.Children);
}
[Fact]
public void ScrollIntoView_On_Effectively_Invisible_Panel_Does_Not_Create_Ghost_Elements()
{
var items = new[] { "foo", "bar", "baz" };
var (target, _, itemsControl) = CreateUnrootedTarget(items: items);
var container = new Decorator { Margin = new Thickness(100), Child = itemsControl };
var root = new TestRoot(true, container);
root.LayoutManager.ExecuteInitialLayoutPass();
// Clear the items and do a layout to recycle all elements.
itemsControl.ItemsSource = null;
root.LayoutManager.ExecuteLayoutPass();
// Should have no realized elements and 3 unrealized elements.
Assert.Equal(0, target.GetRealizedElements().Count);
Assert.Equal(3, target.Children.Count);
// Make the panel effectively invisible and set items.
container.IsVisible = false;
itemsControl.ItemsSource = items;
// Try to scroll into view while effectively invisible.
target.ScrollIntoView(0);
// Make the panel visible and layout.
container.IsVisible = true;
root.LayoutManager.ExecuteLayoutPass();
// Should have 3 realized elements and no unrealized elements.
Assert.Equal(3, target.GetRealizedElements().Count);
Assert.Equal(3, target.Children.Count);
} }
private static IReadOnlyList<int> GetRealizedIndexes(VirtualizingStackPanel target, ItemsControl itemsControl) private static IReadOnlyList<int> GetRealizedIndexes(VirtualizingStackPanel target, ItemsControl itemsControl)

4
tests/Avalonia.UnitTests/StyleHelpers.cs

@ -6,9 +6,9 @@ namespace Avalonia.UnitTests
{ {
public static class StyleHelpers public static class StyleHelpers
{ {
public static SelectorMatchResult TryAttach(Style style, StyledElement element, object? host = null) public static void TryAttach(Style style, StyledElement element, object? host = null)
{ {
return style.TryAttach(element, host ?? element, PropertyStore.FrameType.Style); style.TryAttach(element, host ?? element, PropertyStore.FrameType.Style);
} }
} }
} }

Loading…
Cancel
Save