diff --git a/Source/Editor/CustomEditors/CustomEditorPresenter.cs b/Source/Editor/CustomEditors/CustomEditorPresenter.cs index 1030abfda..ccf712904 100644 --- a/Source/Editor/CustomEditors/CustomEditorPresenter.cs +++ b/Source/Editor/CustomEditors/CustomEditorPresenter.cs @@ -63,6 +63,11 @@ namespace FlaxEditor.CustomEditors /// Indication of if the properties window is locked on specific objects. /// public bool LockSelection { get; set; } + + /// + /// Gets the scene editing context. + /// + public ISceneEditingContext SceneContext { get; } } /// diff --git a/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs b/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs index 62985fe30..ecdc1f7db 100644 --- a/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs @@ -1,8 +1,5 @@ // Copyright (c) Wojciech Figat. All rights reserved. -using System; -using System.Collections.Generic; -using System.Linq; using FlaxEditor.Actions; using FlaxEditor.CustomEditors.Editors; using FlaxEditor.CustomEditors.Elements; @@ -10,10 +7,14 @@ using FlaxEditor.GUI; using FlaxEditor.GUI.ContextMenu; using FlaxEditor.GUI.Tree; using FlaxEditor.Scripting; +using FlaxEditor.Windows.Assets; using FlaxEngine; using FlaxEngine.GUI; using FlaxEngine.Json; using FlaxEngine.Utilities; +using System; +using System.Collections.Generic; +using System.Linq; namespace FlaxEditor.CustomEditors.Dedicated { @@ -238,6 +239,12 @@ namespace FlaxEditor.CustomEditors.Dedicated node.TextColor = Color.OrangeRed; node.Text = Utilities.Utils.GetPropertyNameUI(removed.PrefabObject.GetType().Name); } + // Removed Actor + else if (editor is RemovedActorDummy removedActor) + { + node.TextColor = Color.OrangeRed; + node.Text = $"{removedActor.PrefabObject.Name} ({Utilities.Utils.GetPropertyNameUI(removedActor.PrefabObject.GetType().Name)})"; + } // Actor or Script else if (editor.Values[0] is SceneObject sceneObject) { @@ -293,11 +300,35 @@ namespace FlaxEditor.CustomEditors.Dedicated // Not used } } + + private class RemovedActorDummy : CustomEditor + { + /// + /// The removed prefab object (from the prefab default instance). + /// + public Actor PrefabObject; + + /// + /// The prefab instance's parent. + /// + public Actor ParentActor; + + /// + /// The order of the removed actor in the parent. + /// + public int OrderInParent; + + /// + public override void Initialize(LayoutElementsContainer layout) + { + // Not used + } + } private TreeNode ProcessDiff(CustomEditor editor, bool skipIfNotModified = true) { - // Special case for new Script added to actor - if (editor.Values[0] is Script script && !script.HasPrefabLink) + // Special case for new Script or child actor added to actor + if ((editor.Values[0] is Script script && !script.HasPrefabLink) || (editor.Values[0] is Actor a && !a.HasPrefabLink)) return CreateDiffNode(editor); // Skip if no change detected @@ -357,6 +388,41 @@ namespace FlaxEditor.CustomEditors.Dedicated } } + // Compare child actors for removed actors. + if (editor is ActorEditor && editor.Values.HasReferenceValue && editor.Values.ReferenceValue is Actor prefabObjectActor) + { + var thisActor = editor.Values[0] as Actor; + for (int i = 0; i < prefabObjectActor.ChildrenCount; i++) + { + var prefabActorChild = prefabObjectActor.Children[i]; + if (thisActor == null) + continue; + bool isRemoved = true; + for (int j = 0; j < thisActor.ChildrenCount; j++) + { + var actorChild = thisActor.Children[j]; + if (actorChild.PrefabObjectID == prefabActorChild.PrefabObjectID) + { + isRemoved = false; + break; + } + } + if (isRemoved) + { + var dummy = new RemovedActorDummy + { + PrefabObject = prefabActorChild, + ParentActor = thisActor, + OrderInParent = prefabActorChild.OrderInParent, + }; + var child = CreateDiffNode(dummy); + if (result == null) + result = CreateDiffNode(editor); + result.AddChild(child); + } + } + } + return result; } @@ -466,6 +532,22 @@ namespace FlaxEditor.CustomEditors.Dedicated return; } + // Special case for reverting removed Actors + if (editor is RemovedActorDummy removedActor) + { + Editor.Log("Reverting removed actor changes to prefab (adding it)"); + + var parentActor = removedActor.ParentActor; + var restored = parentActor.AddChild(removedActor.PrefabObject.GetType()); + var prefabId = parentActor.PrefabID; + var prefabObjectId = removedActor.PrefabObject.PrefabObjectID; + string data = JsonSerializer.Serialize(removedActor.PrefabObject); + JsonSerializer.Deserialize(restored, data); + Presenter.Owner.SceneContext.Spawn(restored, parentActor, removedActor.OrderInParent); + Actor.Internal_LinkPrefab(FlaxEngine.Object.GetUnmanagedPtr(restored), ref prefabId, ref prefabObjectId); + return; + } + // Special case for new Script added to actor if (editor.Values[0] is Script script && !script.HasPrefabLink) { @@ -477,6 +559,19 @@ namespace FlaxEditor.CustomEditors.Dedicated return; } + + // Special case for new Actor added to actor + if (editor.Values[0] is Actor a && !a.HasPrefabLink) + { + Editor.Log("Reverting added actor changes to prefab (removing it)"); + + // TODO: Keep previous selection. + var context = Presenter.Owner.SceneContext; + context.Select(SceneGraph.SceneGraphFactory.FindNode(a.ID)); + context.DeleteSelection(); + + return; + } if (Presenter.Undo != null && Presenter.Undo.Enabled) { diff --git a/Source/Editor/SceneGraph/ISceneEditingContext.cs b/Source/Editor/SceneGraph/ISceneEditingContext.cs index d480ceba5..cc750d854 100644 --- a/Source/Editor/SceneGraph/ISceneEditingContext.cs +++ b/Source/Editor/SceneGraph/ISceneEditingContext.cs @@ -1,8 +1,9 @@ // Copyright (c) Wojciech Figat. All rights reserved. -using System.Collections.Generic; using FlaxEditor.SceneGraph; using FlaxEditor.Viewport; +using FlaxEngine; +using System.Collections.Generic; namespace FlaxEditor { @@ -39,9 +40,23 @@ namespace FlaxEditor /// void RenameSelection(); + /// + /// Deletes selected objects. + /// + void DeleteSelection(); + /// /// Focuses selected objects. /// void FocusSelection(); + + /// + /// Spawns the specified actor to the game (with undo). + /// + /// The actor. + /// The parent actor. Set null as default. + /// The order under the parent to put the spawned actor. + /// True if automatically select the spawned actor, otherwise false. + void Spawn(Actor actor, Actor parent = null, int orderInParent = -1, bool autoSelect = true); } } diff --git a/Source/Editor/SceneGraph/RootNode.cs b/Source/Editor/SceneGraph/RootNode.cs index 1a3e47be8..71a9dc83a 100644 --- a/Source/Editor/SceneGraph/RootNode.cs +++ b/Source/Editor/SceneGraph/RootNode.cs @@ -175,7 +175,7 @@ namespace FlaxEditor.SceneGraph public List Selection => SceneContext.Selection; /// - /// Gets the list of selected scene graph nodes in the editor context. + /// Gets the scene editing context. /// public abstract ISceneEditingContext SceneContext { get; } } diff --git a/Source/Editor/Windows/Assets/BehaviorTreeWindow.cs b/Source/Editor/Windows/Assets/BehaviorTreeWindow.cs index 2b86664f2..03ba26fc8 100644 --- a/Source/Editor/Windows/Assets/BehaviorTreeWindow.cs +++ b/Source/Editor/Windows/Assets/BehaviorTreeWindow.cs @@ -641,5 +641,8 @@ namespace FlaxEditor.Windows.Assets /// public bool LockSelection { get; set; } + + /// + public ISceneEditingContext SceneContext => null; } } diff --git a/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs b/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs index 24854afb9..0eecc4861 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs @@ -430,6 +430,12 @@ namespace FlaxEditor.Windows.Assets } } + /// + public void DeleteSelection() + { + Delete(); + } + /// public void FocusSelection() { @@ -488,7 +494,8 @@ namespace FlaxEditor.Windows.Assets /// The actor. /// The parent. /// The order of the actor under the parent. - public void Spawn(Actor actor, Actor parent, int orderInParent = -1) + /// True if automatically select the spawned actor, otherwise false. + public void Spawn(Actor actor, Actor parent, int orderInParent = -1, bool autoSelect = true) { if (actor == null) throw new ArgumentNullException(nameof(actor)); @@ -514,8 +521,11 @@ namespace FlaxEditor.Windows.Assets // Create undo action var action = new CustomDeleteActorsAction(new List(1) { actorNode }, true); Undo.AddAction(action); - Focus(); - Select(actorNode); + if (autoSelect) + { + Focus(); + Select(actorNode); + } } private void OnTreeRightClick(TreeNode node, Float2 location) diff --git a/Source/Editor/Windows/Assets/PrefabWindow.cs b/Source/Editor/Windows/Assets/PrefabWindow.cs index 44c21d863..7f17053ac 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.cs @@ -91,6 +91,9 @@ namespace FlaxEditor.Windows.Assets } } + /// + public ISceneEditingContext SceneContext => this; + /// /// Gets or sets a value indicating whether use live reloading for the prefab changes (applies prefab changes on modification by auto). /// diff --git a/Source/Editor/Windows/PropertiesWindow.cs b/Source/Editor/Windows/PropertiesWindow.cs index e90003038..03316a221 100644 --- a/Source/Editor/Windows/PropertiesWindow.cs +++ b/Source/Editor/Windows/PropertiesWindow.cs @@ -58,6 +58,9 @@ namespace FlaxEditor.Windows } } + /// + public ISceneEditingContext SceneContext => Editor.Windows.EditWin; + /// /// Initializes a new instance of the class. /// diff --git a/Source/Editor/Windows/SceneEditorWindow.cs b/Source/Editor/Windows/SceneEditorWindow.cs index e3c72d069..888cb0888 100644 --- a/Source/Editor/Windows/SceneEditorWindow.cs +++ b/Source/Editor/Windows/SceneEditorWindow.cs @@ -26,12 +26,24 @@ namespace FlaxEditor.Windows FlaxEditor.Utilities.Utils.SetupCommonInputActions(this); } + /// + public void DeleteSelection() + { + Editor.SceneEditing.Delete(); + } + /// public void FocusSelection() { Editor.Windows.EditWin.Viewport.FocusSelection(); } + /// + public void Spawn(Actor actor, Actor parent = null, int orderInParent = -1, bool autoSelect = true) + { + Editor.SceneEditing.Spawn(actor, parent, orderInParent, autoSelect); + } + /// public EditorViewport Viewport => Editor.Windows.EditWin.Viewport;