diff --git a/Source/Editor/Modules/SceneEditingModule.cs b/Source/Editor/Modules/SceneEditingModule.cs index 78ffd26b0..96aefe804 100644 --- a/Source/Editor/Modules/SceneEditingModule.cs +++ b/Source/Editor/Modules/SceneEditingModule.cs @@ -358,7 +358,15 @@ namespace FlaxEditor.Modules var pasteAction = PasteActorsAction.Paste(data, pasteTargetActor?.ID ?? Guid.Empty); if (pasteAction != null) { - OnPasteAction(pasteAction); + pasteAction.Do(out _, out var nodeParents); + + // Select spawned objects (parents only) + var selectAction = new SelectionChangeAction(Selection.ToArray(), nodeParents.Cast().ToArray(), OnSelectionUndo); + selectAction.Do(); + + // Build single compound undo action that pastes the actors and selects the created objects (parents only) + Undo.AddAction(new MultiUndoAction(pasteAction, selectAction)); + OnSelectionChanged(); } } @@ -377,12 +385,57 @@ namespace FlaxEditor.Modules public void Duplicate() { // Peek things that can be copied (copy all actors) - var objects = Selection.Where(x => x.CanCopyPaste).ToList().BuildAllNodes().Where(x => x.CanCopyPaste && x is ActorNode).ToList(); - if (objects.Count == 0) + var nodes = Selection.Where(x => x.CanDuplicate).ToList().BuildAllNodes(); + if (nodes.Count == 0) return; + var actors = new List(); + var newSelection = new List(); + List customUndoActions = null; + foreach (var node in nodes) + { + if (node.CanDuplicate) + { + if (node is ActorNode actorNode) + { + actors.Add(actorNode.Actor); + } + else + { + var customDuplicatedObject = node.Duplicate(out var customUndoAction); + if (customDuplicatedObject != null) + newSelection.Add(customDuplicatedObject); + if (customUndoAction != null) + { + if (customUndoActions == null) + customUndoActions = new List(); + customUndoActions.Add(customUndoAction); + } + } + } + } + if (actors.Count == 0) + { + // Duplicate custom scene graph nodes only without actors + if (newSelection.Count != 0) + { + // Select spawned objects (parents only) + var selectAction = new SelectionChangeAction(Selection.ToArray(), newSelection.ToArray(), OnSelectionUndo); + selectAction.Do(); + + // Build a single compound undo action that pastes the actors, pastes custom stuff (scene graph extension) and selects the created objects (parents only) + var customUndoActionsCount = customUndoActions?.Count ?? 0; + var undoActions = new IUndoAction[1 + customUndoActionsCount]; + for (int i = 0; i < customUndoActionsCount; i++) + undoActions[i] = customUndoActions[i]; + undoActions[undoActions.Length - 1] = selectAction; + + Undo.AddAction(new MultiUndoAction(undoActions)); + OnSelectionChanged(); + } + return; + } // Serialize actors - var actors = objects.ConvertAll(x => ((ActorNode)x).Actor); var data = Actor.ToBytes(actors.ToArray()); if (data == null) { @@ -394,22 +447,26 @@ namespace FlaxEditor.Modules var pasteAction = PasteActorsAction.Duplicate(data, Guid.Empty); if (pasteAction != null) { - OnPasteAction(pasteAction); + pasteAction.Do(out _, out var nodeParents); + + // Select spawned objects (parents only) + newSelection.AddRange(nodeParents); + var selectAction = new SelectionChangeAction(Selection.ToArray(), newSelection.ToArray(), OnSelectionUndo); + selectAction.Do(); + + // Build a single compound undo action that pastes the actors, pastes custom stuff (scene graph extension) and selects the created objects (parents only) + var customUndoActionsCount = customUndoActions?.Count ?? 0; + var undoActions = new IUndoAction[2 + customUndoActionsCount]; + undoActions[0] = pasteAction; + for (int i = 0; i < customUndoActionsCount; i++) + undoActions[i + 1] = customUndoActions[i]; + undoActions[undoActions.Length - 1] = selectAction; + + Undo.AddAction(new MultiUndoAction(undoActions)); + OnSelectionChanged(); } } - private void OnPasteAction(PasteActorsAction pasteAction) - { - pasteAction.Do(out _, out var nodeParents); - - // Select spawned objects - var selectAction = new SelectionChangeAction(Selection.ToArray(), nodeParents.Cast().ToArray(), OnSelectionUndo); - selectAction.Do(); - - Undo.AddAction(new MultiUndoAction(pasteAction, selectAction)); - OnSelectionChanged(); - } - /// /// Called when selection gets changed. Invokes the other events and updates editor. Call it when you manually modify selected objects collection. /// diff --git a/Source/Editor/SceneGraph/ActorChildNode.cs b/Source/Editor/SceneGraph/ActorChildNode.cs index 436984bc4..d514534ca 100644 --- a/Source/Editor/SceneGraph/ActorChildNode.cs +++ b/Source/Editor/SceneGraph/ActorChildNode.cs @@ -63,6 +63,9 @@ namespace FlaxEditor.SceneGraph /// public override bool CanCopyPaste => false; + /// + public override bool CanDuplicate => false; + /// public override bool CanDrag => false; diff --git a/Source/Editor/SceneGraph/ActorNode.cs b/Source/Editor/SceneGraph/ActorNode.cs index 5cc5f5337..07ff6a3b5 100644 --- a/Source/Editor/SceneGraph/ActorNode.cs +++ b/Source/Editor/SceneGraph/ActorNode.cs @@ -182,6 +182,9 @@ namespace FlaxEditor.SceneGraph /// public override bool CanCopyPaste => (_actor.HideFlags & HideFlags.HideInHierarchy) == 0; + /// + public override bool CanDuplicate => (_actor.HideFlags & HideFlags.HideInHierarchy) == 0; + /// public override bool IsActive => _actor.IsActive; diff --git a/Source/Editor/SceneGraph/Actors/SceneNode.cs b/Source/Editor/SceneGraph/Actors/SceneNode.cs index 359d3d207..35c71bf02 100644 --- a/Source/Editor/SceneGraph/Actors/SceneNode.cs +++ b/Source/Editor/SceneGraph/Actors/SceneNode.cs @@ -57,6 +57,9 @@ namespace FlaxEditor.SceneGraph.Actors /// public override bool CanCopyPaste => false; + /// + public override bool CanDuplicate => false; + /// public override bool CanDelete => false; diff --git a/Source/Editor/SceneGraph/RootNode.cs b/Source/Editor/SceneGraph/RootNode.cs index fec8f68ad..10af88489 100644 --- a/Source/Editor/SceneGraph/RootNode.cs +++ b/Source/Editor/SceneGraph/RootNode.cs @@ -56,6 +56,9 @@ namespace FlaxEditor.SceneGraph /// public override bool CanCopyPaste => false; + /// + public override bool CanDuplicate => false; + /// public override bool CanDelete => false; diff --git a/Source/Editor/SceneGraph/SceneGraphNode.cs b/Source/Editor/SceneGraph/SceneGraphNode.cs index c06c0d1c4..7ddf2d876 100644 --- a/Source/Editor/SceneGraph/SceneGraphNode.cs +++ b/Source/Editor/SceneGraph/SceneGraphNode.cs @@ -65,6 +65,11 @@ namespace FlaxEditor.SceneGraph /// public virtual bool CanCopyPaste => true; + /// + /// Gets a value indicating whether this instance can be duplicated by the user. + /// + public virtual bool CanDuplicate => true; + /// /// Gets a value indicating whether this node can be deleted by the user. /// @@ -359,6 +364,17 @@ namespace FlaxEditor.SceneGraph { } + /// + /// Duplicates this object. Valid only if returns true. + /// + /// The undo action that duplicated the object (already performed), null if skip it. + /// The duplicated object node. + public virtual SceneGraphNode Duplicate(out IUndoAction undoAction) + { + undoAction = null; + return null; + } + /// /// Releases the node and the child tree. Disposed all GUI parts and used resources. /// diff --git a/Source/Editor/Windows/Assets/PrefabWindow.Actions.cs b/Source/Editor/Windows/Assets/PrefabWindow.Actions.cs index fca4909f0..af568cd3f 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.Actions.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.Actions.cs @@ -159,7 +159,7 @@ namespace FlaxEditor.Windows.Assets public void Duplicate() { // Peek things that can be copied (copy all actors) - var objects = Selection.Where(x => x.CanCopyPaste && x != Graph.Main).ToList().BuildAllNodes().Where(x => x.CanCopyPaste && x is ActorNode).ToList(); + var objects = Selection.Where(x => x.CanDuplicate && x != Graph.Main).ToList().BuildAllNodes().Where(x => x.CanDuplicate && x is ActorNode).ToList(); if (objects.Count == 0) return;