From 5998953bfe595aa4a388e80353562c8b247ea337 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 5 Mar 2026 02:14:42 +0000 Subject: [PATCH] Add closable tabs and single-line tab strip layout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Tab headers for dynamically-opened sub-graphs now include a close button (×). The initial AnimGraph tab is not closable. - TabControl uses a custom template with horizontal ScrollViewer to keep all tabs in a single line instead of wrapping. - Long tab names are truncated with ellipsis (MaxWidth=200). Co-authored-by: LoogLong <86428208+LoogLong@users.noreply.github.com> --- FModel/Views/AnimGraphViewer.xaml | 19 ++++++++- FModel/Views/AnimGraphViewer.xaml.cs | 63 +++++++++++++++++++++++++--- 2 files changed, 74 insertions(+), 8 deletions(-) diff --git a/FModel/Views/AnimGraphViewer.xaml b/FModel/Views/AnimGraphViewer.xaml index 76e204f7..d689a80e 100644 --- a/FModel/Views/AnimGraphViewer.xaml +++ b/FModel/Views/AnimGraphViewer.xaml @@ -41,9 +41,24 @@ - + + Background="{DynamicResource {x:Static adonisUi:Brushes.Layer0BackgroundBrush}}"> + + + + + + + + + + + + + + + diff --git a/FModel/Views/AnimGraphViewer.xaml.cs b/FModel/Views/AnimGraphViewer.xaml.cs index c034db3c..1836a315 100644 --- a/FModel/Views/AnimGraphViewer.xaml.cs +++ b/FModel/Views/AnimGraphViewer.xaml.cs @@ -86,7 +86,7 @@ public partial class AnimGraphViewer l.Name.Equals("AnimGraph", StringComparison.OrdinalIgnoreCase)) ?? _viewModel.Layers[0]; - AddLayerTab(outputLayer); + AddLayerTab(outputLayer, closable: false); if (LayerTabControl.Items.Count > 0) LayerTabControl.SelectedIndex = 0; @@ -95,13 +95,46 @@ public partial class AnimGraphViewer /// /// Creates a new tab for the given layer and selects it. /// - private void AddLayerTab(AnimGraphLayer layer) + private void AddLayerTab(AnimGraphLayer layer, bool closable = true) { - var tabItem = new System.Windows.Controls.TabItem + var tabItem = new System.Windows.Controls.TabItem { Tag = layer }; + + if (closable) { - Header = layer.Name, - Tag = layer - }; + // Build a header with text + close button + var headerPanel = new StackPanel { Orientation = Orientation.Horizontal }; + headerPanel.Children.Add(new TextBlock + { + Text = layer.Name, + VerticalAlignment = VerticalAlignment.Center, + MaxWidth = 200, + TextTrimming = TextTrimming.CharacterEllipsis + }); + var closeBtn = new Button + { + Content = "×", + FontSize = 12, + Padding = new Thickness(4, 0, 4, 0), + Margin = new Thickness(6, 0, 0, 0), + VerticalAlignment = VerticalAlignment.Center, + Background = Brushes.Transparent, + BorderThickness = new Thickness(0), + Cursor = Cursors.Hand, + Foreground = Brushes.Gray + }; + closeBtn.Click += (_, _) => CloseTab(tabItem); + headerPanel.Children.Add(closeBtn); + tabItem.Header = headerPanel; + } + else + { + tabItem.Header = new TextBlock + { + Text = layer.Name, + MaxWidth = 200, + TextTrimming = TextTrimming.CharacterEllipsis + }; + } var canvasBorder = new Border { @@ -139,6 +172,24 @@ public partial class AnimGraphViewer LayerTabControl.SelectedItem = tabItem; } + /// + /// Closes the given tab and cleans up its layer state. + /// + private void CloseTab(System.Windows.Controls.TabItem tabItem) + { + if (tabItem.Tag is AnimGraphLayer layer) + _layerStates.Remove(layer); + + var index = LayerTabControl.Items.IndexOf(tabItem); + LayerTabControl.Items.Remove(tabItem); + + // Select the previous tab or the first one + if (LayerTabControl.Items.Count > 0) + { + LayerTabControl.SelectedIndex = Math.Max(0, index - 1); + } + } + private void OnLayerTabChanged(object sender, SelectionChangedEventArgs e) { if (LayerTabControl.SelectedItem is not System.Windows.Controls.TabItem { Tag: AnimGraphLayer layer })