mirror of
https://github.com/4sval/FModel.git
synced 2026-03-25 03:04:53 -05:00
Add LinkedAnimLayer double-click to open layer sub-graph tab
Generalized GetLayerName to use any Root node's "Name" property as the layer name (not just "AnimGraph"), enabling proper naming for LinkedAnimLayer sub-graphs. Added double-click handling on nodes: double-clicking a LinkedAnimLayer node finds the matching layer and opens/selects a tab for it. Co-authored-by: LoogLong <86428208+LoogLong@users.noreply.github.com>
This commit is contained in:
parent
6516fab7a9
commit
1d2ef5905e
|
|
@ -212,16 +212,18 @@ public class AnimGraphViewModel
|
|||
|
||||
/// <summary>
|
||||
/// 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).
|
||||
/// </summary>
|
||||
private static string GetLayerName(List<AnimGraphNode> 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));
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new tab for the given layer and selects it.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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".
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fills the properties panel with the selected node's information,
|
||||
/// similar to UE's Details panel when a node is selected.
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user