Added copy feature

This commit is contained in:
Frederik Jacobsen 2025-10-18 21:55:14 +02:00
parent 8f3cbe7b5f
commit dd3fafc363
2 changed files with 76 additions and 0 deletions

View File

@ -30,6 +30,11 @@
BorderBrush="{Binding Cardinality, Converter={StaticResource OptionalToBrush}}" BorderBrush="{Binding Cardinality, Converter={StaticResource OptionalToBrush}}"
BorderThickness="{Binding Cardinality, Converter={StaticResource OptionalToThickness}}" BorderThickness="{Binding Cardinality, Converter={StaticResource OptionalToThickness}}"
Padding="8" Margin="0,0,8,6" Background="{DynamicResource PanelBackgroundBrush}"> Padding="8" Margin="0,0,8,6" Background="{DynamicResource PanelBackgroundBrush}">
<Border.ContextMenu>
<ContextMenu>
<MenuItem Header="Copy as TSV" Click="OnCopyAsTsvClick"/>
</ContextMenu>
</Border.ContextMenu>
<StackPanel Orientation="Vertical" Spacing="2"> <StackPanel Orientation="Vertical" Spacing="2">
<!-- Name on its own line, prominent --> <!-- Name on its own line, prominent -->
<TextBlock Text="{Binding Name}" FontWeight="SemiBold"/> <TextBlock Text="{Binding Name}" FontWeight="SemiBold"/>

View File

@ -1,4 +1,10 @@
using System.Globalization;
using System.Text;
using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.VisualTree;
using XSDVisualiser.Models;
namespace XSDVisualiser.Desktop.Views namespace XSDVisualiser.Desktop.Views
{ {
@ -8,5 +14,70 @@ namespace XSDVisualiser.Desktop.Views
{ {
InitializeComponent(); InitializeComponent();
} }
private async void OnCopyAsTsvClick(object? sender, RoutedEventArgs e)
{
// Determine the node from the menu item's DataContext
var node = (sender as MenuItem)?.DataContext as SchemaNode;
if (node == null)
node = (sender as Control)?.GetVisualAncestors().OfType<Control>().FirstOrDefault()?.DataContext as SchemaNode;
if (node == null)
return;
var tsv = BuildTsv(node);
var topLevel = TopLevel.GetTopLevel(this);
if (topLevel?.Clipboard != null)
{
await topLevel.Clipboard.SetTextAsync(tsv);
}
}
private static string BuildTsv(SchemaNode root)
{
var sb = new StringBuilder();
// Header
sb.AppendLine("Depth\tName\tNamespace\tTypeName\tBuiltInType\tMinOccurs\tMaxOccurs\tContentModel\tIsNillable");
AppendNode(sb, root, 0);
return sb.ToString();
}
private static void AppendNode(StringBuilder sb, SchemaNode node, int depth)
{
string San(string? s)
{
if (string.IsNullOrEmpty(s)) return string.Empty;
return s.Replace("\t", " ").Replace("\r", " ").Replace("\n", " ");
}
var min = node.Cardinality?.Min.ToString(CultureInfo.InvariantCulture) ?? string.Empty;
string max;
if (node.Cardinality?.MaxIsUnbounded == true)
max = "unbounded";
else
max = node.Cardinality != null ? node.Cardinality.Max.ToString(CultureInfo.InvariantCulture) : string.Empty;
var line = string.Join("\t", new[]
{
depth.ToString(CultureInfo.InvariantCulture),
San(node.Name),
San(node.Namespace),
San(node.TypeName),
San(node.BuiltInType),
min,
max,
San(node.ContentModel),
node.IsNillable ? "true" : "false"
});
sb.AppendLine(line);
if (node.Children != null)
{
foreach (var child in node.Children)
{
AppendNode(sb, child, depth + 1);
}
}
}
} }
} }