Browse Source
* Replace "Active Styles" dev tools analysis with "Active Value Frames" * Remove old diagnostic methods from public API (breaking change) * Show full style selectors * Avoid unnecessary value setters by checking if color was actually changed * Run UpdateStyles from the dispatcher to fix flickering issue * Fix buildpull/15676/head
committed by
GitHub
18 changed files with 408 additions and 226 deletions
@ -1,18 +0,0 @@ |
|||||
using Avalonia.Styling; |
|
||||
|
|
||||
namespace Avalonia.Diagnostics |
|
||||
{ |
|
||||
public sealed 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; |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,27 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using Avalonia.Data; |
||||
|
using Avalonia.Metadata; |
||||
|
|
||||
|
namespace Avalonia.Diagnostics; |
||||
|
|
||||
|
public record ValueEntryDiagnostic(AvaloniaProperty Property, object? Value); |
||||
|
|
||||
|
[Unstable] |
||||
|
[NotClientImplementable] |
||||
|
public interface IValueFrameDiagnostic |
||||
|
{ |
||||
|
public enum FrameType |
||||
|
{ |
||||
|
Unknown = 0, |
||||
|
Local, |
||||
|
Theme, |
||||
|
Style, |
||||
|
Template |
||||
|
} |
||||
|
|
||||
|
string? Description { get; } |
||||
|
FrameType Type { get; } |
||||
|
bool IsActive { get; } |
||||
|
BindingPriority Priority { get; } |
||||
|
IEnumerable<ValueEntryDiagnostic> Values { get; } |
||||
|
} |
||||
@ -0,0 +1,18 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using Avalonia.Data; |
||||
|
|
||||
|
namespace Avalonia.Diagnostics; |
||||
|
|
||||
|
internal class LocalValueFrameDiagnostic : IValueFrameDiagnostic |
||||
|
{ |
||||
|
public LocalValueFrameDiagnostic(IEnumerable<ValueEntryDiagnostic> values) |
||||
|
{ |
||||
|
Values = values; |
||||
|
} |
||||
|
|
||||
|
public string? Description => null; |
||||
|
public IValueFrameDiagnostic.FrameType Type => IValueFrameDiagnostic.FrameType.Local; |
||||
|
public bool IsActive => true; |
||||
|
public BindingPriority Priority => BindingPriority.LocalValue; |
||||
|
public IEnumerable<ValueEntryDiagnostic> Values { get; } |
||||
|
} |
||||
@ -1,21 +0,0 @@ |
|||||
using System.Collections.Generic; |
|
||||
using Avalonia.Styling; |
|
||||
|
|
||||
namespace Avalonia.Diagnostics |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Contains information about style related diagnostics of a control.
|
|
||||
/// </summary>
|
|
||||
public class StyleDiagnostics |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Currently applied styles.
|
|
||||
/// </summary>
|
|
||||
public IReadOnlyList<AppliedStyle> AppliedStyles { get; } |
|
||||
|
|
||||
public StyleDiagnostics(IReadOnlyList<AppliedStyle> appliedStyles) |
|
||||
{ |
|
||||
AppliedStyles = appliedStyles; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,63 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using Avalonia.Data; |
||||
|
using Avalonia.PropertyStore; |
||||
|
using Avalonia.Styling; |
||||
|
|
||||
|
namespace Avalonia.Diagnostics; |
||||
|
|
||||
|
internal class StyleValueFrameDiagnostic : IValueFrameDiagnostic |
||||
|
{ |
||||
|
private readonly StyleInstance _styleInstance; |
||||
|
|
||||
|
internal StyleValueFrameDiagnostic(StyleInstance styleInstance) |
||||
|
{ |
||||
|
_styleInstance = styleInstance; |
||||
|
} |
||||
|
|
||||
|
public string? Description => _styleInstance.Source switch |
||||
|
{ |
||||
|
Style s => GetFullSelector(s), |
||||
|
ControlTheme t => t.TargetType?.Name, |
||||
|
_ => null |
||||
|
}; |
||||
|
|
||||
|
public IValueFrameDiagnostic.FrameType Type => _styleInstance.Source switch |
||||
|
{ |
||||
|
Style => IValueFrameDiagnostic.FrameType.Style, |
||||
|
ControlTheme => IValueFrameDiagnostic.FrameType.Theme, |
||||
|
_ => IValueFrameDiagnostic.FrameType.Unknown |
||||
|
}; |
||||
|
|
||||
|
public bool IsActive => _styleInstance.IsActive(); |
||||
|
public BindingPriority Priority => _styleInstance.FramePriority.ToBindingPriority(); |
||||
|
public IEnumerable<ValueEntryDiagnostic> Values |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
foreach (var setter in ((StyleBase)_styleInstance.Source!).Setters) |
||||
|
{ |
||||
|
if (setter is Setter { Property: not null } regularSetter) |
||||
|
{ |
||||
|
yield return new ValueEntryDiagnostic(regularSetter.Property, regularSetter.Value); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private string GetFullSelector(Style? style) |
||||
|
{ |
||||
|
var selectors = new Stack<string>(); |
||||
|
|
||||
|
while (style is not null) |
||||
|
{ |
||||
|
if (style.Selector is not null) |
||||
|
{ |
||||
|
selectors.Push(style.Selector.ToString()); |
||||
|
} |
||||
|
|
||||
|
style = style.Parent as Style; |
||||
|
} |
||||
|
|
||||
|
return string.Concat(selectors); |
||||
|
} |
||||
|
} |
||||
@ -1,17 +1,16 @@ |
|||||
namespace Avalonia.Diagnostics |
namespace Avalonia.Diagnostics; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines diagnostic extensions on <see cref="StyledElement"/>s.
|
||||
|
/// </summary>
|
||||
|
public static class StyledElementExtensions |
||||
{ |
{ |
||||
/// <summary>
|
/// <summary>
|
||||
/// Defines diagnostic extensions on <see cref="StyledElement"/>s.
|
/// Gets a style diagnostics for a <see cref="StyledElement"/>.
|
||||
/// </summary>
|
/// </summary>
|
||||
public static class StyledElementExtensions |
/// <param name="styledElement">The element.</param>
|
||||
|
public static ValueStoreDiagnostic GetValueStoreDiagnostic(this StyledElement styledElement) |
||||
{ |
{ |
||||
/// <summary>
|
return styledElement.GetValueStore().GetStoreDiagnostic(); |
||||
/// Gets a style diagnostics for a <see cref="StyledElement"/>.
|
|
||||
/// </summary>
|
|
||||
/// <param name="styledElement">The element.</param>
|
|
||||
public static StyleDiagnostics GetStyleDiagnostics(this StyledElement styledElement) |
|
||||
{ |
|
||||
return styledElement.GetStyleDiagnosticsInternal(); |
|
||||
} |
|
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -0,0 +1,37 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using Avalonia.Data; |
||||
|
using Avalonia.PropertyStore; |
||||
|
using Avalonia.Styling; |
||||
|
|
||||
|
namespace Avalonia.Diagnostics; |
||||
|
|
||||
|
internal sealed class ValueFrameDiagnostic : IValueFrameDiagnostic |
||||
|
{ |
||||
|
private readonly ValueFrame _valueFrame; |
||||
|
|
||||
|
internal ValueFrameDiagnostic(ValueFrame valueFrame) |
||||
|
{ |
||||
|
_valueFrame = valueFrame; |
||||
|
} |
||||
|
|
||||
|
public string? Description => (_valueFrame.Owner?.Owner as StyledElement)?.StyleKey.Name; |
||||
|
|
||||
|
public IValueFrameDiagnostic.FrameType Type => IValueFrameDiagnostic.FrameType.Template; |
||||
|
|
||||
|
public bool IsActive => _valueFrame.IsActive(); |
||||
|
public BindingPriority Priority => _valueFrame.FramePriority.ToBindingPriority(); |
||||
|
public IEnumerable<ValueEntryDiagnostic> Values |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
for (var i = 0; i < _valueFrame.EntryCount; i++) |
||||
|
{ |
||||
|
var entry = _valueFrame.GetEntry(i); |
||||
|
if (entry.HasValue()) |
||||
|
{ |
||||
|
yield return new ValueEntryDiagnostic(entry.Property, entry.GetValue()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,17 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using Avalonia.Styling; |
||||
|
|
||||
|
namespace Avalonia.Diagnostics; |
||||
|
|
||||
|
public class ValueStoreDiagnostic |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Currently applied frames.
|
||||
|
/// </summary>
|
||||
|
public IReadOnlyList<IValueFrameDiagnostic> AppliedFrames { get; } |
||||
|
|
||||
|
internal ValueStoreDiagnostic(IReadOnlyList<IValueFrameDiagnostic> appliedFrames) |
||||
|
{ |
||||
|
AppliedFrames = appliedFrames; |
||||
|
} |
||||
|
} |
||||
@ -1,43 +0,0 @@ |
|||||
using System.Collections.Generic; |
|
||||
using Avalonia.Styling; |
|
||||
|
|
||||
namespace Avalonia.Diagnostics.ViewModels |
|
||||
{ |
|
||||
internal class StyleViewModel : ViewModelBase |
|
||||
{ |
|
||||
private readonly AppliedStyle _styleInstance; |
|
||||
private bool _isActive; |
|
||||
private bool _isVisible; |
|
||||
|
|
||||
public StyleViewModel(AppliedStyle styleInstance, string name, List<SetterViewModel> setters) |
|
||||
{ |
|
||||
_styleInstance = styleInstance; |
|
||||
IsVisible = true; |
|
||||
Name = name; |
|
||||
Setters = setters; |
|
||||
|
|
||||
Update(); |
|
||||
} |
|
||||
|
|
||||
public bool IsActive |
|
||||
{ |
|
||||
get => _isActive; |
|
||||
set => RaiseAndSetIfChanged(ref _isActive, value); |
|
||||
} |
|
||||
|
|
||||
public bool IsVisible |
|
||||
{ |
|
||||
get => _isVisible; |
|
||||
set => RaiseAndSetIfChanged(ref _isVisible, value); |
|
||||
} |
|
||||
|
|
||||
public string Name { get; } |
|
||||
|
|
||||
public List<SetterViewModel> Setters { get; } |
|
||||
|
|
||||
public void Update() |
|
||||
{ |
|
||||
IsActive = _styleInstance.IsActive; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,117 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using Avalonia.Controls; |
||||
|
using Avalonia.Data; |
||||
|
using Avalonia.Input.Platform; |
||||
|
using Avalonia.Markup.Xaml.MarkupExtensions; |
||||
|
using Avalonia.Styling; |
||||
|
|
||||
|
namespace Avalonia.Diagnostics.ViewModels |
||||
|
{ |
||||
|
internal class ValueFrameViewModel : ViewModelBase |
||||
|
{ |
||||
|
private readonly IValueFrameDiagnostic _valueFrame; |
||||
|
private bool _isActive; |
||||
|
private bool _isVisible; |
||||
|
|
||||
|
public ValueFrameViewModel(StyledElement styledElement, IValueFrameDiagnostic valueFrame, IClipboard? clipboard) |
||||
|
{ |
||||
|
_valueFrame = valueFrame; |
||||
|
IsVisible = true; |
||||
|
|
||||
|
Description = (_valueFrame.Type, _valueFrame.Description) switch |
||||
|
{ |
||||
|
(IValueFrameDiagnostic.FrameType.Local, _) => "Local Values " + _valueFrame.Description, |
||||
|
(IValueFrameDiagnostic.FrameType.Template, _) => "Template " + _valueFrame.Description, |
||||
|
(IValueFrameDiagnostic.FrameType.Theme, _) => "Theme " + _valueFrame.Description, |
||||
|
(_, {Length:>0}) => _valueFrame.Description, |
||||
|
_ => _valueFrame.Priority.ToString() |
||||
|
}; |
||||
|
|
||||
|
Setters = new List<SetterViewModel>(); |
||||
|
|
||||
|
foreach (var (setterProperty, setterValue) in valueFrame.Values) |
||||
|
{ |
||||
|
var resourceInfo = GetResourceInfo(setterValue); |
||||
|
|
||||
|
SetterViewModel setterVm; |
||||
|
|
||||
|
if (resourceInfo.HasValue) |
||||
|
{ |
||||
|
var resourceKey = resourceInfo.Value.resourceKey; |
||||
|
var resourceValue = styledElement.FindResource(resourceKey); |
||||
|
|
||||
|
setterVm = new ResourceSetterViewModel(setterProperty, resourceKey, resourceValue, |
||||
|
resourceInfo.Value.isDynamic, clipboard); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
var isBinding = IsBinding(setterValue); |
||||
|
|
||||
|
if (isBinding) |
||||
|
{ |
||||
|
setterVm = new BindingSetterViewModel(setterProperty, setterValue, clipboard); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
setterVm = new SetterViewModel(setterProperty, setterValue, clipboard); |
||||
|
} |
||||
|
} |
||||
|
Setters.Add(setterVm); |
||||
|
} |
||||
|
|
||||
|
Update(); |
||||
|
} |
||||
|
|
||||
|
public bool IsActive |
||||
|
{ |
||||
|
get => _isActive; |
||||
|
set => RaiseAndSetIfChanged(ref _isActive, value); |
||||
|
} |
||||
|
|
||||
|
public bool IsVisible |
||||
|
{ |
||||
|
get => _isVisible; |
||||
|
set => RaiseAndSetIfChanged(ref _isVisible, value); |
||||
|
} |
||||
|
|
||||
|
public string? Description { get; } |
||||
|
|
||||
|
public List<SetterViewModel> Setters { get; } |
||||
|
|
||||
|
public void Update() |
||||
|
{ |
||||
|
IsActive = _valueFrame.IsActive; |
||||
|
} |
||||
|
|
||||
|
private static (object resourceKey, bool isDynamic)? GetResourceInfo(object? value) |
||||
|
{ |
||||
|
if (value is StaticResourceExtension staticResource |
||||
|
&& staticResource.ResourceKey != null) |
||||
|
{ |
||||
|
return (staticResource.ResourceKey, false); |
||||
|
} |
||||
|
else if (value is DynamicResourceExtension dynamicResource |
||||
|
&& dynamicResource.ResourceKey != null) |
||||
|
{ |
||||
|
return (dynamicResource.ResourceKey, true); |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
private static bool IsBinding(object? value) |
||||
|
{ |
||||
|
switch (value) |
||||
|
{ |
||||
|
case Binding: |
||||
|
case CompiledBindingExtension: |
||||
|
case TemplateBinding: |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue