A cross-platform UI framework for .NET
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

252 lines
6.7 KiB

using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.LogicalTree;
using Avalonia.Metadata;
using Avalonia.Styling;
using Avalonia.VisualTree;
namespace Avalonia.Diagnostics.ViewModels
{
internal class TreePageViewModel : ViewModelBase, IDisposable
{
private TreeNode? _selectedNode;
private ControlDetailsViewModel? _details;
public TreePageViewModel(MainViewModel mainView, TreeNode[] nodes)
{
MainView = mainView;
Nodes = nodes;
PropertiesFilter = new FilterViewModel();
PropertiesFilter.RefreshFilter += (s, e) => Details?.PropertiesView?.Refresh();
SettersFilter = new FilterViewModel();
SettersFilter.RefreshFilter += (s, e) => Details?.UpdateStyleFilters();
}
public event EventHandler<string>? ClipboardCopyRequested;
public MainViewModel MainView { get; }
public FilterViewModel PropertiesFilter { get; }
public FilterViewModel SettersFilter { get; }
public TreeNode[] Nodes { get; protected set; }
public TreeNode? SelectedNode
{
get => _selectedNode;
set
{
if (RaiseAndSetIfChanged(ref _selectedNode, value))
{
Details = value != null ?
new ControlDetailsViewModel(this, value.Visual) :
null;
Details?.UpdatePropertiesView(MainView.ShowImplementedInterfaces);
Details?.UpdateStyleFilters();
}
}
}
public ControlDetailsViewModel? Details
{
get => _details;
private set
{
var oldValue = _details;
if (RaiseAndSetIfChanged(ref _details, value))
{
oldValue?.Dispose();
}
}
}
public void Dispose()
{
foreach (var node in Nodes)
{
node.Dispose();
}
_details?.Dispose();
}
public TreeNode? FindNode(Control control)
{
foreach (var node in Nodes)
{
var result = FindNode(node, control);
if (result != null)
{
return result;
}
}
return null;
}
public void SelectControl(Control control)
{
var node = default(TreeNode);
Control? c = control;
while (node == null && c != null)
{
node = FindNode(c);
if (node == null)
{
c = c.GetVisualParent<Control>();
}
}
if (node != null)
{
SelectedNode = node;
ExpandNode(node.Parent);
}
}
public void CopySelector()
{
var currentVisual = SelectedNode?.Visual as Visual;
if (currentVisual is not null)
{
var selector = GetVisualSelector(currentVisual);
ClipboardCopyRequested?.Invoke(this, selector);
}
}
public void CopySelectorFromTemplateParent()
{
var parts = new List<string>();
var currentVisual = SelectedNode?.Visual as Visual;
while (currentVisual is not null)
{
parts.Add(GetVisualSelector(currentVisual));
currentVisual = currentVisual.TemplatedParent as Visual;
}
if (parts.Any())
{
parts.Reverse();
var selector = string.Join(" /template/ ", parts);
ClipboardCopyRequested?.Invoke(this, selector);
}
}
public void ExpandRecursively()
{
if (SelectedNode is { } selectedNode)
{
ExpandNode(selectedNode);
var stack = new Stack<TreeNode>();
stack.Push(selectedNode);
while (stack.Count > 0)
{
var item = stack.Pop();
item.IsExpanded = true;
foreach (var child in item.Children)
{
stack.Push(child);
}
}
}
}
public void CollapseChildren()
{
if (SelectedNode is { } selectedNode)
{
var stack = new Stack<TreeNode>();
stack.Push(selectedNode);
while (stack.Count > 0)
{
var item = stack.Pop();
item.IsExpanded = false;
foreach (var child in item.Children)
{
stack.Push(child);
}
}
}
}
public void CaptureNodeScreenshot()
{
MainView.Shot(null);
}
public void BringIntoView()
{
(SelectedNode?.Visual as Control)?.BringIntoView();
}
public void Focus()
{
(SelectedNode?.Visual as Control)?.Focus();
}
private static string GetVisualSelector(Visual visual)
{
var name = string.IsNullOrEmpty(visual.Name) ? "" : $"#{visual.Name}";
var classes = string.Concat(visual.Classes
.Where(c => !c.StartsWith(":"))
.Select(c => '.' + c));
var typeName = StyledElement.GetStyleKey(visual);
return $"{typeName}{name}{classes}";
}
private void ExpandNode(TreeNode? node)
{
if (node != null)
{
node.IsExpanded = true;
ExpandNode(node.Parent);
}
}
private TreeNode? FindNode(TreeNode node, Control control)
{
if (node.Visual == control)
{
return node;
}
else
{
foreach (var child in node.Children)
{
var result = FindNode(child, control);
if (result != null)
{
return result;
}
}
}
return null;
}
internal void UpdatePropertiesView()
{
Details?.UpdatePropertiesView(MainView?.ShowImplementedInterfaces ?? true);
}
}
}