1+ using System ;
2+ using System . Collections . Generic ;
3+ using System . Linq ;
4+ using Nonatomic . VSM2 . Editor . NodeGraph ;
5+ using Nonatomic . VSM2 . Editor . Persistence ;
6+ using Nonatomic . VSM2 . Editor . StateGraph . Nodes ;
7+ using Nonatomic . VSM2 . NodeGraph ;
8+ using Nonatomic . VSM2 . StateGraph ;
9+ using Nonatomic . VSM2 . StateGraph . States ;
10+ using UnityEditor ;
11+ using UnityEngine ;
12+ using UnityEngine . UIElements ;
13+
14+ namespace Nonatomic . VSM2 . Editor . StateGraph
15+ {
16+ public static class CopyPasteHelper
17+ {
18+ public static CopiedData LastCopy => _lastCopy ;
19+ private static CopiedData _lastCopy ;
20+
21+ public static void CacheCopiedData ( CopiedData copy )
22+ {
23+ _lastCopy = copy ;
24+ }
25+
26+ public static void ClearCopyCache ( )
27+ {
28+ _lastCopy = null ;
29+ }
30+
31+ public static void Copy ( StateGraphView graphView )
32+ {
33+ var selectedNodeModels = graphView . selection
34+ . OfType < BaseStateNodeView > ( )
35+ . Select ( node => node . NodeModel )
36+ . ToList ( ) ;
37+
38+ var selectedTransitions = graphView . selection
39+ . OfType < StateNodeEdge > ( )
40+ . Select ( edge => ( StateTransitionModel ) edge . userData )
41+ . ToList ( ) ;
42+
43+ var copy = new CopiedData ( selectedNodeModels , selectedTransitions ) ;
44+ CacheCopiedData ( copy ) ;
45+ }
46+
47+ public static void Paste ( StateGraphView graphView )
48+ {
49+ if ( LastCopy == null ) return ;
50+
51+ var model = graphView . StateManager . Model as StateMachineModel ;
52+ if ( ! model ) return ;
53+
54+ var clonedNodes = LastCopy . SelectedNodes . Select ( node => node . Clone ( ) ) . ToList ( ) ;
55+ var clonedTransition = LastCopy . SelectedTransitions . Select ( trans => trans . Clone ( ) ) . ToList ( ) ;
56+
57+ RenameClonedNodes ( model , clonedNodes ) ;
58+ OffsetNodes ( clonedNodes , new Vector2 ( 50 , 50 ) ) ;
59+
60+ //Remap transition nodes since their GUIDs have changed
61+ RemapTransitionNodes ( graphView , model , LastCopy , clonedTransition , clonedNodes ) ;
62+
63+ graphView . PopulateGraph ( model ) ;
64+
65+ //Wait a frame for the nodes to be created then select them
66+ EditorApplication . delayCall += ( ) => SelectNodes ( graphView , clonedNodes ) ;
67+ }
68+
69+ private static void SelectNodes ( StateGraphView graphView , List < StateNodeModel > nodes )
70+ {
71+ graphView . selection . Clear ( ) ;
72+
73+ foreach ( var node in nodes )
74+ {
75+ var originNode = graphView . contentViewContainer . Q < NodeView > ( node . Id ) ;
76+ originNode ? . Select ( graphView , true ) ;
77+ }
78+ }
79+
80+ private static void RenameClonedNodes ( StateMachineModel model , List < StateNodeModel > clonedNodes )
81+ {
82+ foreach ( var node in clonedNodes )
83+ {
84+ //Prevent copying entry nodes
85+ if ( node . State is EntryState ) continue ;
86+
87+ //Provide new GUIDs for the pasted node models
88+ node . Id = node . State . name = StateGraphNodeFactory . GenerateStateName ( node . State . GetType ( ) ) ;
89+
90+ model . AddState ( node ) ;
91+ }
92+ }
93+
94+ private static void OffsetNodes ( List < StateNodeModel > nodes , Vector2 offset )
95+ {
96+ foreach ( var node in nodes )
97+ {
98+ node . Position += offset ;
99+ }
100+ }
101+
102+ private static void RemapTransitionNodes ( StateGraphView graphView , StateMachineModel model , CopiedData copy , List < StateTransitionModel > clonedTransition , List < StateNodeModel > clonedNodes )
103+ {
104+ foreach ( var transition in clonedTransition )
105+ {
106+ UpdateNode ( graphView , copy , transition , clonedNodes , true ) ;
107+ UpdateNode ( graphView , copy , transition , clonedNodes , false ) ;
108+
109+ if ( transition . OriginNodeId != null && transition . DestinationNodeId != null )
110+ {
111+ model . AddTransition ( transition ) ;
112+ }
113+ }
114+ }
115+
116+ private static void UpdateNode ( StateGraphView graphView , CopiedData copy , TransitionModel transition , List < StateNodeModel > clonedNodes , bool updateOrigin )
117+ {
118+ // Determine the node type to update based on the parameter
119+ var nodeId = updateOrigin
120+ ? transition . OriginNodeId
121+ : transition . DestinationNodeId ;
122+
123+ Func < StateNodeModel , List < PortModel > > portSelector = updateOrigin
124+ ? node => node . OutputPorts
125+ : node => node . InputPorts ;
126+
127+ var nodeIndex = copy . SelectedNodes . FindIndex ( node => node . Id . Equals ( nodeId ) ) ;
128+ if ( nodeIndex > - 1 )
129+ {
130+ // Node was found in copied nodes, update from cloned nodes
131+ var clonedNode = clonedNodes [ nodeIndex ] ;
132+ if ( updateOrigin )
133+ {
134+ transition . OriginNodeId = clonedNode . Id ;
135+ transition . OriginPort = clonedNode . OutputPorts . FirstOrDefault ( port => port . Id . Equals ( transition . OriginPort . Id ) ) ;
136+ }
137+ else
138+ {
139+ transition . DestinationNodeId = clonedNode . Id ;
140+ transition . DestinationPort = clonedNode . InputPorts . FirstOrDefault ( port => port . Id . Equals ( transition . DestinationPort . Id ) ) ;
141+ }
142+ }
143+ else
144+ {
145+ // Node was not found in copied nodes, find in existing nodes
146+ var existingNodeView = graphView . contentViewContainer . Q < BaseStateNodeView > ( nodeId ) ;
147+ if ( existingNodeView == null ) return ;
148+
149+ if ( updateOrigin )
150+ {
151+ transition . OriginNodeId = existingNodeView . NodeModel . Id ;
152+ transition . OriginPort = existingNodeView . NodeModel . OutputPorts . FirstOrDefault ( port => port . Id . Equals ( transition . OriginPort . Id ) ) ;
153+ }
154+ else
155+ {
156+ transition . DestinationNodeId = existingNodeView . NodeModel . Id ;
157+ transition . DestinationPort = existingNodeView . NodeModel . InputPorts . FirstOrDefault ( port => port . Id . Equals ( transition . DestinationPort . Id ) ) ;
158+ }
159+ }
160+ }
161+ }
162+ }
0 commit comments