diff --git a/FModel/ViewModels/AnimGraphViewModel.cs b/FModel/ViewModels/AnimGraphViewModel.cs index ca5e616a..c6be01cf 100644 --- a/FModel/ViewModels/AnimGraphViewModel.cs +++ b/FModel/ViewModels/AnimGraphViewModel.cs @@ -212,16 +212,18 @@ public class AnimGraphViewModel /// /// Determines a display name for a layer based on the types of nodes it contains. + /// A Root node's "Name" property defines the layer/sub-graph name + /// (e.g., "AnimGraph" for the main output pose, or a specific name for LinkedAnimLayer sub-graphs). /// private static string GetLayerName(List nodes, int index) { - // The final output pose layer contains an AnimGraphNode_Root whose - // "Name" property (in AdditionalProperties) is set to "AnimGraph" var rootNode = nodes.FirstOrDefault(n => - n.AdditionalProperties.TryGetValue("Name", out var name) && - name.Equals("AnimGraph", StringComparison.OrdinalIgnoreCase)); - if (rootNode != null) - return "AnimGraph"; + n.ExportType.EndsWith("_Root", StringComparison.OrdinalIgnoreCase) && + n.AdditionalProperties.TryGetValue("Name", out _)); + if (rootNode != null && + rootNode.AdditionalProperties.TryGetValue("Name", out var rootName) && + !string.IsNullOrEmpty(rootName)) + return rootName; var stateMachine = nodes.FirstOrDefault(n => n.ExportType.Contains("StateMachine", StringComparison.OrdinalIgnoreCase)); diff --git a/FModel/Views/AnimGraphViewer.xaml.cs b/FModel/Views/AnimGraphViewer.xaml.cs index c6eb207d..4cb6c787 100644 --- a/FModel/Views/AnimGraphViewer.xaml.cs +++ b/FModel/Views/AnimGraphViewer.xaml.cs @@ -63,69 +63,67 @@ public partial class AnimGraphViewer LayerTabControl.Items.Clear(); _layerStates.Clear(); - // If no layers were built (empty graph), show nothing if (_viewModel.Layers.Count == 0) return; - // Show only the final output pose layer (AnimGraph) initially. - // The root node of the output layer has a "Name" property set to "AnimGraph" - // in its AdditionalProperties (from the AnimGraphNode_Root struct). + // Show only the AnimGraph layer initially var outputLayer = _viewModel.Layers.FirstOrDefault(l => - l.Nodes.Any(n => n.AdditionalProperties.TryGetValue("Name", out var name) && - name.Equals("AnimGraph", StringComparison.OrdinalIgnoreCase))) + l.Name.Equals("AnimGraph", StringComparison.OrdinalIgnoreCase)) ?? _viewModel.Layers[0]; - var layersToShow = new[] { outputLayer }; + AddLayerTab(outputLayer); - foreach (var layer in layersToShow) - { - var tabItem = new System.Windows.Controls.TabItem - { - Header = layer.Name, - Tag = layer - }; - - // Create canvas container for this layer - var canvasBorder = new Border - { - ClipToBounds = true, - Background = new SolidColorBrush(Color.FromRgb(24, 24, 24)) - }; - - var canvas = new Canvas(); - var scaleTransform = new ScaleTransform(1, 1); - var translateTransform = new TranslateTransform(0, 0); - var transformGroup = new TransformGroup(); - transformGroup.Children.Add(scaleTransform); - transformGroup.Children.Add(translateTransform); - canvas.RenderTransform = transformGroup; - - canvasBorder.Child = canvas; - canvasBorder.MouseWheel += OnMouseWheel; - canvasBorder.AddHandler(UIElement.MouseLeftButtonDownEvent, - new MouseButtonEventHandler(OnCanvasMouseDown), true); - canvasBorder.MouseLeftButtonUp += OnCanvasMouseUp; - canvasBorder.MouseMove += OnCanvasMouseMove; - - tabItem.Content = canvasBorder; - - var state = new LayerCanvasState - { - Layer = layer, - Canvas = canvas, - ScaleTransform = scaleTransform, - TranslateTransform = translateTransform - }; - _layerStates[layer] = state; - - LayerTabControl.Items.Add(tabItem); - } - - // Select the first tab if (LayerTabControl.Items.Count > 0) LayerTabControl.SelectedIndex = 0; } + /// + /// Creates a new tab for the given layer and selects it. + /// + private void AddLayerTab(AnimGraphLayer layer) + { + var tabItem = new System.Windows.Controls.TabItem + { + Header = layer.Name, + Tag = layer + }; + + var canvasBorder = new Border + { + ClipToBounds = true, + Background = new SolidColorBrush(Color.FromRgb(24, 24, 24)) + }; + + var canvas = new Canvas(); + var scaleTransform = new ScaleTransform(1, 1); + var translateTransform = new TranslateTransform(0, 0); + var transformGroup = new TransformGroup(); + transformGroup.Children.Add(scaleTransform); + transformGroup.Children.Add(translateTransform); + canvas.RenderTransform = transformGroup; + + canvasBorder.Child = canvas; + canvasBorder.MouseWheel += OnMouseWheel; + canvasBorder.AddHandler(UIElement.MouseLeftButtonDownEvent, + new MouseButtonEventHandler(OnCanvasMouseDown), true); + canvasBorder.MouseLeftButtonUp += OnCanvasMouseUp; + canvasBorder.MouseMove += OnCanvasMouseMove; + + tabItem.Content = canvasBorder; + + var state = new LayerCanvasState + { + Layer = layer, + Canvas = canvas, + ScaleTransform = scaleTransform, + TranslateTransform = translateTransform + }; + _layerStates[layer] = state; + + LayerTabControl.Items.Add(tabItem); + LayerTabControl.SelectedItem = tabItem; + } + private void OnLayerTabChanged(object sender, SelectionChangedEventArgs e) { if (LayerTabControl.SelectedItem is not System.Windows.Controls.TabItem { Tag: AnimGraphLayer layer }) @@ -309,9 +307,15 @@ public partial class AnimGraphViewer border.ToolTip = $"{node.ExportType}\n{node.Name}"; - // Click to select node and show properties + // Click to select, double-click to open linked layer border.MouseLeftButtonDown += (s, e) => { + if (e.ClickCount == 2) + { + TryOpenLinkedLayer(node); + e.Handled = true; + return; + } SelectNode(node, border); e.Handled = true; }; @@ -397,6 +401,37 @@ public partial class AnimGraphViewer PopulatePropertiesPanel(node); } + /// + /// When a LinkedAnimLayer node is double-clicked, opens its corresponding + /// layer sub-graph in a new tab. The layer is identified by matching the + /// node's "Layer" property with a layer whose Root node has the same "Name". + /// + private void TryOpenLinkedLayer(AnimGraphNode node) + { + if (!node.ExportType.Contains("LinkedAnimLayer", StringComparison.OrdinalIgnoreCase)) + return; + + if (!node.AdditionalProperties.TryGetValue("Layer", out var layerName) || string.IsNullOrEmpty(layerName)) + return; + + var targetLayer = _viewModel.Layers.FirstOrDefault(l => + l.Name.Equals(layerName, StringComparison.OrdinalIgnoreCase)); + if (targetLayer == null) + return; + + // If tab already exists, just select it + foreach (System.Windows.Controls.TabItem tab in LayerTabControl.Items) + { + if (tab.Tag == targetLayer) + { + LayerTabControl.SelectedItem = tab; + return; + } + } + + AddLayerTab(targetLayer); + } + /// /// Fills the properties panel with the selected node's information, /// similar to UE's Details panel when a node is selected.