Browse Source

Merge pull request #3073 from MarchingCube/optimize-selectors

Optimize TypeNameAndClassSelector.
pull/3087/head
Steven Kirk 7 years ago
committed by GitHub
parent
commit
1b9099753f
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 90
      src/Avalonia.Styling/Styling/TypeNameAndClassSelector.cs

90
src/Avalonia.Styling/Styling/TypeNameAndClassSelector.cs

@ -18,8 +18,9 @@ namespace Avalonia.Styling
internal class TypeNameAndClassSelector : Selector internal class TypeNameAndClassSelector : Selector
{ {
private readonly Selector _previous; private readonly Selector _previous;
private readonly Lazy<List<string>> _classes = new Lazy<List<string>>(() => new List<string>());
private Type _targetType; private Type _targetType;
private Lazy<List<string>> _classes = new Lazy<List<string>>(() => new List<string>());
private string _selectorString; private string _selectorString;
public static TypeNameAndClassSelector OfType(Selector previous, Type targetType) public static TypeNameAndClassSelector OfType(Selector previous, Type targetType)
@ -27,6 +28,7 @@ namespace Avalonia.Styling
var result = new TypeNameAndClassSelector(previous); var result = new TypeNameAndClassSelector(previous);
result._targetType = targetType; result._targetType = targetType;
result.IsConcreteType = true; result.IsConcreteType = true;
return result; return result;
} }
@ -35,6 +37,7 @@ namespace Avalonia.Styling
var result = new TypeNameAndClassSelector(previous); var result = new TypeNameAndClassSelector(previous);
result._targetType = targetType; result._targetType = targetType;
result.IsConcreteType = false; result.IsConcreteType = false;
return result; return result;
} }
@ -42,6 +45,7 @@ namespace Avalonia.Styling
{ {
var result = new TypeNameAndClassSelector(previous); var result = new TypeNameAndClassSelector(previous);
result.Name = name; result.Name = name;
return result; return result;
} }
@ -49,6 +53,7 @@ namespace Avalonia.Styling
{ {
var result = new TypeNameAndClassSelector(previous); var result = new TypeNameAndClassSelector(previous);
result.Classes.Add(className); result.Classes.Add(className);
return result; return result;
} }
@ -126,9 +131,11 @@ namespace Avalonia.Styling
if (subscribe) if (subscribe)
{ {
var observable = new ClassObserver(control.Classes, _classes.Value); var observable = new ClassObserver(control.Classes, _classes.Value);
return new SelectorMatch(observable); return new SelectorMatch(observable);
} }
else if (!Matches(control.Classes))
if (!AreClassesMatching(control.Classes, Classes))
{ {
return SelectorMatch.NeverThisInstance; return SelectorMatch.NeverThisInstance;
} }
@ -139,21 +146,6 @@ namespace Avalonia.Styling
protected override Selector MovePrevious() => _previous; protected override Selector MovePrevious() => _previous;
private bool Matches(IEnumerable<string> classes)
{
int remaining = Classes.Count;
foreach (var c in classes)
{
if (Classes.Contains(c))
{
--remaining;
}
}
return remaining == 0;
}
private string BuildSelectorString() private string BuildSelectorString()
{ {
var builder = new StringBuilder(); var builder = new StringBuilder();
@ -199,11 +191,41 @@ namespace Avalonia.Styling
return builder.ToString(); return builder.ToString();
} }
private class ClassObserver : LightweightObservableBase<bool> private static bool AreClassesMatching(IReadOnlyList<string> classes, IList<string> toMatch)
{ {
readonly IList<string> _match; int remainingMatches = toMatch.Count;
IAvaloniaReadOnlyList<string> _classes; int classesCount = classes.Count;
bool _value;
// Early bail out - we can't match if control does not have enough classes.
if (classesCount < remainingMatches)
{
return false;
}
for (var i = 0; i < classesCount; i++)
{
var c = classes[i];
if (toMatch.Contains(c))
{
--remainingMatches;
// Already matched so we can skip checking other classes.
if (remainingMatches == 0)
{
break;
}
}
}
return remainingMatches == 0;
}
private sealed class ClassObserver : LightweightObservableBase<bool>
{
private readonly IList<string> _match;
private readonly IAvaloniaReadOnlyList<string> _classes;
private bool _hasMatch;
public ClassObserver(IAvaloniaReadOnlyList<string> classes, IList<string> match) public ClassObserver(IAvaloniaReadOnlyList<string> classes, IList<string> match)
{ {
@ -215,42 +237,32 @@ namespace Avalonia.Styling
protected override void Initialize() protected override void Initialize()
{ {
_value = GetResult(); _hasMatch = IsMatching();
_classes.CollectionChanged += ClassesChanged; _classes.CollectionChanged += ClassesChanged;
} }
protected override void Subscribed(IObserver<bool> observer, bool first) protected override void Subscribed(IObserver<bool> observer, bool first)
{ {
observer.OnNext(_value); observer.OnNext(_hasMatch);
} }
private void ClassesChanged(object sender, NotifyCollectionChangedEventArgs e) private void ClassesChanged(object sender, NotifyCollectionChangedEventArgs e)
{ {
if (e.Action != NotifyCollectionChangedAction.Move) if (e.Action != NotifyCollectionChangedAction.Move)
{ {
var value = GetResult(); var hasMatch = IsMatching();
if (value != _value) if (hasMatch != _hasMatch)
{ {
PublishNext(GetResult()); PublishNext(hasMatch);
_value = value; _hasMatch = hasMatch;
} }
} }
} }
private bool GetResult() private bool IsMatching()
{ {
int remaining = _match.Count; return AreClassesMatching(_classes, _match);
foreach (var c in _classes)
{
if (_match.Contains(c))
{
--remaining;
}
}
return remaining == 0;
} }
} }
} }

Loading…
Cancel
Save