diff --git a/src/Avalonia.Diagnostics/Diagnostics/Constants.cs b/src/Avalonia.Diagnostics/Diagnostics/Constants.cs
new file mode 100644
index 0000000000..1686ddf8cf
--- /dev/null
+++ b/src/Avalonia.Diagnostics/Diagnostics/Constants.cs
@@ -0,0 +1,15 @@
+namespace Avalonia.Diagnostics;
+
+internal static class Constants
+{
+ ///
+ /// DevTools Clipboard data format
+ ///
+ static public class DataFormats
+ {
+ ///
+ /// Clipboard data format for the selector. It is added for quick format recognition in IDEs
+ ///
+ public const string Avalonia_DevTools_Selector = nameof(Avalonia_DevTools_Selector);
+ }
+}
diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs
index f6fe121f28..e56b0606d6 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs
+++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs
@@ -25,7 +25,7 @@ namespace Avalonia.Diagnostics.ViewModels
}
public event EventHandler? ClipboardCopyRequested;
-
+
public MainViewModel MainView { get; }
public FilterViewModel PropertiesFilter { get; }
@@ -117,11 +117,11 @@ namespace Avalonia.Diagnostics.ViewModels
if (currentVisual is not null)
{
var selector = GetVisualSelector(currentVisual);
-
+
ClipboardCopyRequested?.Invoke(this, selector);
}
}
-
+
public void CopySelectorFromTemplateParent()
{
var parts = new List();
@@ -130,7 +130,7 @@ namespace Avalonia.Diagnostics.ViewModels
while (currentVisual is not null)
{
parts.Add(GetVisualSelector(currentVisual));
-
+
currentVisual = currentVisual.TemplatedParent as Visual;
}
@@ -148,7 +148,7 @@ namespace Avalonia.Diagnostics.ViewModels
if (SelectedNode is { } selectedNode)
{
ExpandNode(selectedNode);
-
+
var stack = new Stack();
stack.Push(selectedNode);
@@ -192,8 +192,8 @@ namespace Avalonia.Diagnostics.ViewModels
{
(SelectedNode?.Visual as Control)?.BringIntoView();
}
-
-
+
+
public void Focus()
{
(SelectedNode?.Visual as Control)?.Focus();
@@ -205,10 +205,10 @@ namespace Avalonia.Diagnostics.ViewModels
var classes = string.Concat(visual.Classes
.Where(c => !c.StartsWith(":"))
.Select(c => '.' + c));
- var typeName = StyledElement.GetStyleKey(visual);
-
- return $"{typeName}{name}{classes}";
- }
+ var pseudo = string.Concat(visual.Classes.Where(c => c[0] == ':').Select(c => c));
+ var type = StyledElement.GetStyleKey(visual);
+ return $$"""{{{type.Assembly.FullName}}}{{type.Namespace}}|{{type.Name}}{{name}}{{classes}}{{pseudo}}""";
+ }
private void ExpandNode(TreeNode? node)
{
diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs b/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs
index 69dc375ede..7597cf9ef4 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs
+++ b/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs
@@ -75,9 +75,40 @@ namespace Avalonia.Diagnostics.Views
AvaloniaXamlLoader.Load(this);
}
- private void OnClipboardCopyRequested(object? sender, string e)
+ private void OnClipboardCopyRequested(object? sender, string selector)
{
- TopLevel.GetTopLevel(this)?.Clipboard?.SetTextAsync(e);
+ if (TopLevel.GetTopLevel(this)?.Clipboard is { } clipboard)
+ {
+ var @do = new DataObject();
+ var text = ToText(selector);
+ @do.Set(DataFormats.Text, text);
+ @do.Set(Constants.DataFormats.Avalonia_DevTools_Selector, selector);
+ clipboard.SetDataObjectAsync(@do);
+ }
+ }
+
+ private static string ToText(string text)
+ {
+ var sb = new System.Text.StringBuilder();
+ var bufferStartIndex = -1;
+ for (var ic = 0; ic < text.Length; ic++)
+ {
+ var c = text[ic];
+ switch (c)
+ {
+ case '{':
+ bufferStartIndex = sb.Length;
+ break;
+ case '}' when bufferStartIndex > -1:
+ sb.Remove(bufferStartIndex, sb.Length - bufferStartIndex);
+ bufferStartIndex = sb.Length;
+ break;
+ default:
+ sb.Append(c);
+ break;
+ }
+ }
+ return sb.ToString();
}
}
}