|
|
@@ -9,128 +9,17 @@ using Avalonia.Collections;
|
|
|
using Avalonia.Controls;
|
|
|
using Avalonia.Controls.Metadata;
|
|
|
using Avalonia.Markup.Xaml.MarkupExtensions;
|
|
|
-using Avalonia.Media;
|
|
|
using Avalonia.Styling;
|
|
|
using Avalonia.VisualTree;
|
|
|
|
|
|
namespace Avalonia.Diagnostics.ViewModels
|
|
|
{
|
|
|
- internal class StyleViewModel : ViewModelBase
|
|
|
- {
|
|
|
- private readonly IStyleInstance _styleInstance;
|
|
|
- private bool _isActive;
|
|
|
-
|
|
|
- public StyleViewModel(IStyleInstance styleInstance, string name, List<SetterViewModel> setters)
|
|
|
- {
|
|
|
- _styleInstance = styleInstance;
|
|
|
- IsActive = styleInstance.IsActive;
|
|
|
- Name = name;
|
|
|
- Setters = setters;
|
|
|
- }
|
|
|
-
|
|
|
- public bool IsActive
|
|
|
- {
|
|
|
- get => _isActive;
|
|
|
- set => RaiseAndSetIfChanged(ref _isActive, value);
|
|
|
- }
|
|
|
-
|
|
|
- public string Name { get; }
|
|
|
-
|
|
|
- public List<SetterViewModel> Setters { get; }
|
|
|
-
|
|
|
- public void Update()
|
|
|
- {
|
|
|
- IsActive = _styleInstance.IsActive;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- internal enum ValueKind
|
|
|
- {
|
|
|
- Regular,
|
|
|
- Resource
|
|
|
- }
|
|
|
-
|
|
|
- internal class SetterViewModel : ViewModelBase
|
|
|
- {
|
|
|
- public string Name { get; }
|
|
|
-
|
|
|
- public object Value { get; }
|
|
|
-
|
|
|
- public ValueKind Kind { get; }
|
|
|
-
|
|
|
- public bool IsSpecialKind => Kind != ValueKind.Regular;
|
|
|
-
|
|
|
- public IBrush KindColor { get; }
|
|
|
-
|
|
|
- public SetterViewModel(string name, object value, ValueKind kind)
|
|
|
- {
|
|
|
- Name = name;
|
|
|
- Value = value;
|
|
|
- Kind = kind;
|
|
|
-
|
|
|
- if (Kind == ValueKind.Resource)
|
|
|
- {
|
|
|
- KindColor = Brushes.Brown;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- KindColor = Brushes.Transparent;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- internal class PseudoClassesViewModel : ViewModelBase
|
|
|
- {
|
|
|
- private readonly StyledElement _source;
|
|
|
- private readonly IPseudoClasses _pseudoClasses;
|
|
|
- private bool _isActive;
|
|
|
- private bool _isUpdating;
|
|
|
-
|
|
|
- public PseudoClassesViewModel(string name, StyledElement source)
|
|
|
- {
|
|
|
- Name = name;
|
|
|
- _source = source;
|
|
|
- _pseudoClasses = _source.Classes;
|
|
|
-
|
|
|
- Update();
|
|
|
- }
|
|
|
-
|
|
|
- public string Name { get; }
|
|
|
-
|
|
|
- public bool IsActive
|
|
|
- {
|
|
|
- get => _isActive;
|
|
|
- set
|
|
|
- {
|
|
|
- RaiseAndSetIfChanged(ref _isActive, value);
|
|
|
-
|
|
|
- if (!_isUpdating)
|
|
|
- {
|
|
|
- _pseudoClasses.Set(Name, value);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- public void Update()
|
|
|
- {
|
|
|
- try
|
|
|
- {
|
|
|
- _isUpdating = true;
|
|
|
-
|
|
|
- IsActive = _source.Classes.Contains(Name);
|
|
|
- }
|
|
|
- finally
|
|
|
- {
|
|
|
- _isUpdating = false;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
internal class ControlDetailsViewModel : ViewModelBase, IDisposable
|
|
|
{
|
|
|
private readonly IVisual _control;
|
|
|
private readonly IDictionary<object, List<PropertyViewModel>> _propertyIndex;
|
|
|
private AvaloniaPropertyViewModel _selectedProperty;
|
|
|
+ private string _styleFilter;
|
|
|
|
|
|
public ControlDetailsViewModel(TreePageViewModel treePage, IVisual control)
|
|
|
{
|
|
|
@@ -196,32 +85,46 @@ namespace Avalonia.Diagnostics.ViewModels
|
|
|
{
|
|
|
var setterValue = regularSetter.Value;
|
|
|
|
|
|
- ValueKind kind = ValueKind.Regular;
|
|
|
+ var resourceInfo = GetResourceInfo(setterValue);
|
|
|
+
|
|
|
+ SetterViewModel setterVm;
|
|
|
|
|
|
- if (setterValue is DynamicResourceExtension dynResource)
|
|
|
+ if (resourceInfo.HasValue)
|
|
|
{
|
|
|
- var resolved = styledElement.FindResource(dynResource.ResourceKey);
|
|
|
-
|
|
|
- if (resolved != null)
|
|
|
- {
|
|
|
- setterValue = $"{resolved} ({dynResource.ResourceKey})";
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- setterValue = dynResource.ResourceKey;
|
|
|
- }
|
|
|
-
|
|
|
- kind = ValueKind.Resource;
|
|
|
+ var resourceKey = resourceInfo.Value.resourceKey;
|
|
|
+ var resourceValue = styledElement.FindResource(resourceKey);
|
|
|
+
|
|
|
+ setterVm = new ResourceSetterViewModel(regularSetter.Property, resourceKey, resourceValue, resourceInfo.Value.isDynamic);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ setterVm = new SetterViewModel(regularSetter.Property, setterValue);
|
|
|
}
|
|
|
|
|
|
- setters.Add(new SetterViewModel(regularSetter.Property?.Name ?? "?", setterValue, kind));
|
|
|
+ setters.Add(setterVm);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
AppliedStyles.Add(new StyleViewModel(appliedStyle, style.Selector?.ToString() ?? "No selector", setters));
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ UpdateStyles();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private (object resourceKey, bool isDynamic)? GetResourceInfo(object value)
|
|
|
+ {
|
|
|
+ if (value is StaticResourceExtension staticResource)
|
|
|
+ {
|
|
|
+ return (staticResource.ResourceKey, false);
|
|
|
}
|
|
|
+ else if (value is DynamicResourceExtension dynamicResource)
|
|
|
+ {
|
|
|
+ return (dynamicResource.ResourceKey, true);
|
|
|
+ }
|
|
|
+
|
|
|
+ return null;
|
|
|
}
|
|
|
|
|
|
public TreePageViewModel TreePage { get; }
|
|
|
@@ -237,9 +140,46 @@ namespace Avalonia.Diagnostics.ViewModels
|
|
|
get => _selectedProperty;
|
|
|
set => RaiseAndSetIfChanged(ref _selectedProperty, value);
|
|
|
}
|
|
|
+
|
|
|
+ public string StyleFilter
|
|
|
+ {
|
|
|
+ get => _styleFilter;
|
|
|
+ set => RaiseAndSetIfChanged(ref _styleFilter, value);
|
|
|
+ }
|
|
|
|
|
|
public ControlLayoutViewModel Layout { get; }
|
|
|
|
|
|
+ protected override void OnPropertyChanged(PropertyChangedEventArgs e)
|
|
|
+ {
|
|
|
+ base.OnPropertyChanged(e);
|
|
|
+
|
|
|
+ if (e.PropertyName == nameof(StyleFilter))
|
|
|
+ {
|
|
|
+ UpdateStyleFilters();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void UpdateStyleFilters()
|
|
|
+ {
|
|
|
+ var filter = StyleFilter;
|
|
|
+ bool hasFilter = !string.IsNullOrEmpty(filter);
|
|
|
+
|
|
|
+ foreach (var style in AppliedStyles)
|
|
|
+ {
|
|
|
+ var hasVisibleSetter = false;
|
|
|
+
|
|
|
+ foreach (var setter in style.Setters)
|
|
|
+ {
|
|
|
+ setter.IsVisible =
|
|
|
+ !hasFilter || setter.Name.IndexOf(filter, StringComparison.OrdinalIgnoreCase) >= 0;
|
|
|
+
|
|
|
+ hasVisibleSetter |= setter.IsVisible;
|
|
|
+ }
|
|
|
+
|
|
|
+ style.IsVisible = hasVisibleSetter;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
public void Dispose()
|
|
|
{
|
|
|
if (_control is INotifyPropertyChanged inpc)
|
|
|
@@ -333,6 +273,39 @@ namespace Avalonia.Diagnostics.ViewModels
|
|
|
style.Update();
|
|
|
}
|
|
|
|
|
|
+ var propertyBuckets = new Dictionary<AvaloniaProperty, List<SetterViewModel>>();
|
|
|
+
|
|
|
+ foreach (var style in AppliedStyles)
|
|
|
+ {
|
|
|
+ if (!style.IsActive)
|
|
|
+ {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ foreach (var setter in style.Setters)
|
|
|
+ {
|
|
|
+ if (propertyBuckets.TryGetValue(setter.Property, out var setters))
|
|
|
+ {
|
|
|
+ foreach (var otherSetter in setters)
|
|
|
+ {
|
|
|
+ otherSetter.IsActive = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ setter.IsActive = true;
|
|
|
+
|
|
|
+ setters.Add(setter);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ setter.IsActive = true;
|
|
|
+
|
|
|
+ setters = new List<SetterViewModel> { setter };
|
|
|
+
|
|
|
+ propertyBuckets.Add(setter.Property, setters);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
foreach (var pseudoClass in PseudoClasses)
|
|
|
{
|
|
|
pseudoClass.Update();
|