Skip to content

Commit

Permalink
feat(DevTools): Pin properties (#13371)
Browse files Browse the repository at this point in the history
* feat(DevTools): Pin properties

* fix: Address review

* fix: revert using

* feat: Show Pin ToggleButton on pointer over.
  • Loading branch information
workgroupengineering authored Nov 5, 2023
1 parent 083c7cb commit 52cbe29
Show file tree
Hide file tree
Showing 9 changed files with 253 additions and 74 deletions.
39 changes: 39 additions & 0 deletions src/Avalonia.Diagnostics/Assets/Icons.axaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:ClassModifier="internal">
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default">
<SolidColorBrush Color="Black" x:Key="PinColor"/>
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<SolidColorBrush Color="White" x:Key="PinColor"/>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>

<!-- Add Resources Here -->
<DrawingGroup x:Key="Pin_xaml">
<DrawingGroup.ClipGeometry>
<RectangleGeometry Rect="0.0,-960.0,960.0,960.0"/>
</DrawingGroup.ClipGeometry>
<GeometryDrawing Brush="{DynamicResource PinColor}">
<GeometryDrawing.Geometry>
<PathGeometry Figures="m 640 -480 l 80 80 v 80 H 520 v 240 l -40 40 l -40 -40 v -240 H 240 v -80 l 80 -80 v -280 h -40 v -80 h 400 v 80 h -40 v 280 Z m -286 80 h 252 l -46 -46 v -314 H 400 v 314 l -46 46 Z m 126 0 Z" FillRule="NonZero"/>
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingGroup>

<DrawingGroup x:Key="unpin_xaml">
<DrawingGroup.ClipGeometry>
<RectangleGeometry Rect="0.0,-960.0,960.0,960.0"/>
</DrawingGroup.ClipGeometry>
<GeometryDrawing Brush="{DynamicResource PinColor}">
<GeometryDrawing.Geometry>
<PathGeometry Figures="m 440 -600 l 80 -80 h 80 v 200 h 240 l 40 40 l -40 40 H 600 v 200 h -80 l -80 -80 H 160 v 40 H 80 v -400 h 80 v 40 z m 80 286 v -252 l -46 46 H 160 v 160 h 314 z m 0 -126 z" FillRule="NonZero"/>
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingGroup>

<DrawingImage Drawing="{StaticResource unpin_xaml}" x:Key="UnpinIcon"/>
<DrawingImage Drawing="{StaticResource Pin_xaml}" x:Key="PinIcon"/>

</ResourceDictionary>
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System;
using System.Globalization;
using Avalonia.Data;
using Avalonia.Data.Converters;
using Avalonia.Media;

namespace Avalonia.Diagnostics.Converters;

internal class BoolToImageConverter : IValueConverter
{
public IImage? TrueImage { get; set; }

public IImage? FalseImage { get; set; }

public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) =>
value switch
{
true => TrueImage,
false => FalseImage,
_ => null
};

public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) =>
BindingOperations.DoNothing;
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.ComponentModel;
using Avalonia.Data;

namespace Avalonia.Diagnostics.ViewModels
Expand Down Expand Up @@ -49,7 +50,7 @@ public override object? Value
}
}

public override string Group => _group;
public override string Group => IsPinned ? "Pinned" : _group;

public override Type? DeclaringType { get; }
public override Type PropertyType => _propertyType;
Expand Down Expand Up @@ -114,5 +115,14 @@ public override void Update()
}
RaisePropertyChanged(nameof(Type));
}

protected override void OnPropertyChanged(PropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if (e.PropertyName == nameof(IsPinned))
{
RaisePropertyChanged(nameof(Group));
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.ComponentModel;
using System.Reflection;

namespace Avalonia.Diagnostics.ViewModels
Expand Down Expand Up @@ -36,7 +37,7 @@ public ClrPropertyViewModel(object o, PropertyInfo property)
public PropertyInfo Property { get; }
public override object Key => Name;
public override string Name { get; }
public override string Group => "CLR Properties";
public override string Group => IsPinned ? "Pinned" : "CLR Properties";

public override Type AssignedType => _assignedType;
public override Type PropertyType => _propertyType;
Expand Down Expand Up @@ -82,5 +83,14 @@ public override void Update()
RaiseAndSetIfChanged(ref _assignedType, valueType ?? Property.PropertyType, nameof(AssignedType));
RaisePropertyChanged(nameof(Type));
}

protected override void OnPropertyChanged(PropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if (e.PropertyName == nameof(IsPinned))
{
RaisePropertyChanged(nameof(Group));
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
Expand All @@ -10,33 +11,43 @@
using Avalonia.Data;
using Avalonia.Markup.Xaml.MarkupExtensions;
using Avalonia.Styling;
using Avalonia.VisualTree;

namespace Avalonia.Diagnostics.ViewModels
{
internal class ControlDetailsViewModel : ViewModelBase, IDisposable, IClassesChangedListener
{
private readonly AvaloniaObject _avaloniaObject;
private readonly ISet<string> _pinnedProperties;
private IDictionary<object, PropertyViewModel[]>? _propertyIndex;
private PropertyViewModel? _selectedProperty;
private DataGridCollectionView? _propertiesView;
private bool _snapshotStyles;
private bool _showInactiveStyles;
private string? _styleStatus;
private object? _selectedEntity;
private readonly Stack<(string Name,object Entry)> _selectedEntitiesStack = new();
private readonly Stack<(string Name, object Entry)> _selectedEntitiesStack = new();
private string? _selectedEntityName;
private string? _selectedEntityType;
private bool _showImplementedInterfaces;
// new DataGridPathGroupDescription(nameof(AvaloniaPropertyViewModel.Group))
private readonly static IReadOnlyList<DataGridPathGroupDescription> GroupDescriptors = new DataGridPathGroupDescription[]
{
new DataGridPathGroupDescription(nameof(AvaloniaPropertyViewModel.Group))
};

public ControlDetailsViewModel(TreePageViewModel treePage, AvaloniaObject avaloniaObject)
private readonly static IReadOnlyList<DataGridSortDescription> SortDescriptions = new DataGridSortDescription[]
{
_avaloniaObject = avaloniaObject;
new DataGridComparerSortDescription(PropertyComparer.Instance!, ListSortDirection.Ascending),
};

public ControlDetailsViewModel(TreePageViewModel treePage, AvaloniaObject avaloniaObject, ISet<string> pinnedProperties)
{
_avaloniaObject = avaloniaObject;
_pinnedProperties = pinnedProperties;
TreePage = treePage;
Layout = avaloniaObject is Visual visual
? new ControlLayoutViewModel(visual)
: default;
Layout = avaloniaObject is Visual visual
? new ControlLayoutViewModel(visual)
: default;

NavigateToProperty(_avaloniaObject, (_avaloniaObject as Control)?.Name ?? _avaloniaObject.ToString());

Expand Down Expand Up @@ -84,7 +95,7 @@ public ControlDetailsViewModel(TreePageViewModel treePage, AvaloniaObject avalon
{
var setterValue = regularSetter.Value;

var resourceInfo = GetResourceInfo(setterValue);
var resourceInfo = GetResourceInfo(setterValue);

SetterViewModel setterVm;

Expand Down Expand Up @@ -175,13 +186,13 @@ public string? SelectedEntityName
get => _selectedEntityName;
set => RaiseAndSetIfChanged(ref _selectedEntityName, value);
}

public string? SelectedEntityType
{
get => _selectedEntityType;
set => RaiseAndSetIfChanged(ref _selectedEntityType, value);
}

public PropertyViewModel? SelectedProperty
{
get => _selectedProperty;
Expand Down Expand Up @@ -395,35 +406,53 @@ private bool FilterProperty(object arg)
return !(arg is PropertyViewModel property) || TreePage.PropertiesFilter.Filter(property.Name);
}

private class PropertyComparer : IComparer<PropertyViewModel>
private class PropertyComparer : IComparer<PropertyViewModel>, IComparer
{
public static PropertyComparer Instance { get; } = new PropertyComparer();

public int Compare(PropertyViewModel? x, PropertyViewModel? y)
{
var groupX = GroupIndex(x?.Group);
var groupY = GroupIndex(y?.Group);
if (x is null && y is null)
return 0;

if (x is null && y is not null)
return -1;

if (x is not null && y is null)
return 1;

var groupX = GroupIndex(x!.Group);
var groupY = GroupIndex(y!.Group);

if (groupX != groupY)
{
return groupX - groupY;
}
else
{
return string.CompareOrdinal(x?.Name, y?.Name);
return string.CompareOrdinal(x.Name, y.Name);
}
}

private static int GroupIndex(string? group)
{
switch (group)
{
case "Properties": return 0;
case "Attached Properties": return 1;
case "CLR Properties": return 2;
default: return 3;
case "Pinned":
return -1;
case "Properties":
return 0;
case "Attached Properties":
return 1;
case "CLR Properties":
return 2;
default:
return 3;
}
}

public int Compare(object? x, object? y) =>
Compare(x as PropertyViewModel, y as PropertyViewModel);
}

private static IEnumerable<PropertyInfo> GetAllPublicProperties(Type type)
Expand All @@ -438,8 +467,8 @@ public void NavigateToSelectedProperty()
var selectedProperty = SelectedProperty;
var selectedEntity = SelectedEntity;
var selectedEntityName = SelectedEntityName;
if (selectedEntity == null
|| selectedProperty == null
if (selectedEntity == null
|| selectedProperty == null
|| selectedProperty.PropertyType == typeof(string)
|| selectedProperty.PropertyType.IsValueType)
return;
Expand All @@ -455,19 +484,19 @@ public void NavigateToSelectedProperty()
break;

case ClrPropertyViewModel clrProperty:
{
property = GetAllPublicProperties(selectedEntity.GetType())
.FirstOrDefault(pi => clrProperty.Property == pi)?
.GetValue(selectedEntity);
{
property = GetAllPublicProperties(selectedEntity.GetType())
.FirstOrDefault(pi => clrProperty.Property == pi)?
.GetValue(selectedEntity);

break;
}
break;
}
}

if (property == null)
if (property == null)
return;

_selectedEntitiesStack.Push((Name:selectedEntityName!, Entry:selectedEntity));
_selectedEntitiesStack.Push((Name: selectedEntityName!, Entry: selectedEntity));

var propertyName = selectedProperty.Name;

Expand All @@ -492,7 +521,7 @@ public void NavigateToParentProperty()
RaisePropertyChanged(nameof(CanNavigateToParentProperty));
}
}

protected void NavigateToProperty(object o, string? entityName)
{
var oldSelectedEntity = SelectedEntity;
Expand All @@ -514,17 +543,19 @@ protected void NavigateToProperty(object o, string? entityName)

var properties = GetAvaloniaProperties(o)
.Concat(GetClrProperties(o, _showImplementedInterfaces))
.OrderBy(x => x, PropertyComparer.Instance)
.ThenBy(x => x.Name)
.Do(p =>
{
p.IsPinned = _pinnedProperties.Contains(p.FullName);
})
.ToArray();

_propertyIndex = properties
.GroupBy(x => x.Key)
.ToDictionary(x => x.Key, x => x.ToArray());


var view = new DataGridCollectionView(properties);
view.GroupDescriptions.Add(new DataGridPathGroupDescription(nameof(AvaloniaPropertyViewModel.Group)));
view.GroupDescriptions.AddRange(GroupDescriptors);
view.SortDescriptions.AddRange(SortDescriptions);
view.Filter = FilterProperty;
PropertiesView = view;

Expand All @@ -539,18 +570,18 @@ protected void NavigateToProperty(object o, string? entityName)
break;
}
}

internal void SelectProperty(AvaloniaProperty property)
{
SelectedProperty = null;

if (SelectedEntity != _avaloniaObject)
{
NavigateToProperty(
_avaloniaObject,
(_avaloniaObject as Control)?.Name ?? _avaloniaObject.ToString());
_avaloniaObject,
(_avaloniaObject as Control)?.Name ?? _avaloniaObject.ToString());
}

if (PropertiesView is null)
{
return;
Expand All @@ -561,7 +592,7 @@ internal void SelectProperty(AvaloniaProperty property)
if (o is AvaloniaPropertyViewModel propertyVm && propertyVm.Property == property)
{
SelectedProperty = propertyVm;

break;
}
}
Expand All @@ -573,5 +604,24 @@ internal void UpdatePropertiesView(bool showImplementedInterfaces)
SelectedProperty = null;
NavigateToProperty(_avaloniaObject, (_avaloniaObject as Control)?.Name ?? _avaloniaObject.ToString());
}

public void TogglePinnedProperty(object parameter)
{
if (parameter is PropertyViewModel model)
{
var fullname = model.FullName;
if (_pinnedProperties.Contains(fullname))
{
_pinnedProperties.Remove(fullname);
model.IsPinned = false;
}
else
{
_pinnedProperties.Add(fullname);
model.IsPinned = true;
}
PropertiesView?.Refresh();
}
}
}
}
Loading

0 comments on commit 52cbe29

Please sign in to comment.