Skip to content

Improve DevTools previewer of setter values by including all value priorities #13802

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
May 9, 2024
18 changes: 18 additions & 0 deletions api/Avalonia.nupkg.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1015,6 +1015,18 @@
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Diagnostics.AppliedStyle</Target>
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Diagnostics.StyleDiagnostics</Target>
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Input.FocusManager.&lt;GetFocusScopeAncestors&gt;d__18</Target>
Expand Down Expand Up @@ -1069,6 +1081,12 @@
<Left>baseline/netstandard2.0/Avalonia.Controls.dll</Left>
<Right>target/netstandard2.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Diagnostics.StyledElementExtensions.GetStyleDiagnostics(Avalonia.StyledElement)</Target>
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>P:Avalonia.Rendering.Composition.ICompositionGpuImportedObject.ImportCompleted</Target>
Expand Down
18 changes: 0 additions & 18 deletions src/Avalonia.Base/Diagnostics/AppliedStyle.cs

This file was deleted.

27 changes: 27 additions & 0 deletions src/Avalonia.Base/Diagnostics/IValueFrameDiagnostic.cs
Original file line number Diff line number Diff line change
@@ -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; }
}
18 changes: 18 additions & 0 deletions src/Avalonia.Base/Diagnostics/LocalValueFrameDiagnostic.cs
Original file line number Diff line number Diff line change
@@ -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; }
}
21 changes: 0 additions & 21 deletions src/Avalonia.Base/Diagnostics/StyleDiagnostics.cs

This file was deleted.

63 changes: 63 additions & 0 deletions src/Avalonia.Base/Diagnostics/StyleValueFrameDiagnostic.cs
Original file line number Diff line number Diff line change
@@ -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);
}
}
21 changes: 10 additions & 11 deletions src/Avalonia.Base/Diagnostics/StyledElementExtensions.cs
Original file line number Diff line number Diff line change
@@ -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>
/// Defines diagnostic extensions on <see cref="StyledElement"/>s.
/// Gets a style diagnostics for a <see cref="StyledElement"/>.
/// </summary>
public static class StyledElementExtensions
/// <param name="styledElement">The element.</param>
public static ValueStoreDiagnostic GetValueStoreDiagnostic(this StyledElement styledElement)
{
/// <summary>
/// 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();
}
return styledElement.GetValueStore().GetStoreDiagnostic();
}
}
37 changes: 37 additions & 0 deletions src/Avalonia.Base/Diagnostics/ValueFrameDiagnostic.cs
Original file line number Diff line number Diff line change
@@ -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());
}
}
}
}
}
17 changes: 17 additions & 0 deletions src/Avalonia.Base/Diagnostics/ValueStoreDiagnostic.cs
Original file line number Diff line number Diff line change
@@ -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;
}
}
6 changes: 6 additions & 0 deletions src/Avalonia.Base/PropertyStore/FramePriority.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ public static FramePriority ToFramePriority(this BindingPriority priority, Frame
return (FramePriority)(p * 3 + (int)type);
}

public static BindingPriority ToBindingPriority(this FramePriority priority)
{
var p = (int)priority / 3;
return p == 0 ? BindingPriority.Animation : (BindingPriority)p;
}

public static bool IsType(this FramePriority priority, FrameType type)
{
return (FrameType)((int)priority % 3) == type;
Expand Down
34 changes: 34 additions & 0 deletions src/Avalonia.Base/PropertyStore/ValueStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,40 @@ public AvaloniaPropertyValue GetDiagnostic(AvaloniaProperty property)
overridden);
}

public ValueStoreDiagnostic GetStoreDiagnostic()
{
var frames = new List<IValueFrameDiagnostic>();

var effectiveLocalValues = new List<ValueEntryDiagnostic>(_effectiveValues.Count);
for (var i = 0; i < _effectiveValues.Count; i++)
{
_effectiveValues.GetKeyValue(i, out var key, out var effectiveValue);
if (effectiveValue.Priority == BindingPriority.LocalValue)
{
effectiveLocalValues.Add(new ValueEntryDiagnostic(key, effectiveValue.Value));
}
}

if (effectiveLocalValues.Count > 0)
{
frames.Add(new LocalValueFrameDiagnostic(effectiveLocalValues));
}

foreach (var frame in Frames)
{
if (frame is StyleInstance { Source: StyleBase } styleInstance)
{
frames.Add(new StyleValueFrameDiagnostic(styleInstance));
}
else
{
frames.Add(new ValueFrameDiagnostic(frame));
}
}

return new ValueStoreDiagnostic(frames);
}

private int InsertFrame(ValueFrame frame)
{
Debug.Assert(!_frames.Contains(frame));
Expand Down
13 changes: 0 additions & 13 deletions src/Avalonia.Base/StyledElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -418,19 +418,6 @@ protected void InitializeIfNeeded()
}
}

internal StyleDiagnostics GetStyleDiagnosticsInternal()
{
var styles = new List<AppliedStyle>();

foreach (var frame in GetValueStore().Frames)
{
if (frame is IStyleInstance style)
styles.Add(new(style));
}

return new StyleDiagnostics(styles);
}

/// <inheritdoc/>
void ILogical.NotifyAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,14 @@ partial class BrushEditor : TemplatedControl
public BrushEditor()
{
FlyoutBase.SetAttachedFlyout(this, new Flyout { Content = _colorView });
_colorView.ColorChanged += (_, e) => Brush = new ImmutableSolidColorBrush(e.NewColor);
_colorView.ColorChanged += (_, e) =>
{
// Avoid unnecessary value setters by checking if color was actually changed.
if (Brush is null || Brush is not ISolidColorBrush oldSolidBrush || oldSolidBrush.Color != e.NewColor)
{
Brush = new ImmutableSolidColorBrush(e.NewColor);
}
};
clearHandler = (s, e) => Brush = default;
}

Expand Down
Loading