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