diff --git a/Source/Editor/CustomEditors/Dedicated/MissingScriptEditor.cs b/Source/Editor/CustomEditors/Dedicated/MissingScriptEditor.cs index a6c4e6623..8c8c76561 100644 --- a/Source/Editor/CustomEditors/Dedicated/MissingScriptEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/MissingScriptEditor.cs @@ -1,6 +1,11 @@ -using FlaxEditor.CustomEditors.Editors; +using FlaxEditor.Actions; +using FlaxEditor.CustomEditors.Editors; +using FlaxEditor.GUI; +using FlaxEditor.GUI.ContextMenu; +using FlaxEditor.Scripting; using FlaxEngine; using FlaxEngine.GUI; +using System.Collections.Generic; namespace FlaxEditor.CustomEditors.Dedicated; @@ -10,6 +15,11 @@ namespace FlaxEditor.CustomEditors.Dedicated; [CustomEditor(typeof(MissingScript)), DefaultEditor] public class MissingScriptEditor : GenericEditor { + DropPanel _dropPanel; + Button _replaceScriptButton; + CheckBox _shouldReplaceAllCheckbox; + CustomEditor _propertiesEditor; + /// public override void Initialize(LayoutElementsContainer layout) { @@ -18,9 +28,157 @@ public class MissingScriptEditor : GenericEditor base.Initialize(layout); return; } + _dropPanel = dropPanel; + _dropPanel.HeaderTextColor = Color.OrangeRed; - dropPanel.HeaderTextColor = Color.OrangeRed; + Panel replaceScriptPanel = new Panel + { + Parent = _dropPanel, + Height = 64, + }; + + _replaceScriptButton = new Button + { + Text = "Replace Script", + AnchorPreset = AnchorPresets.TopCenter, + Width = 240, + Height = 24, + X = -120, + Y = 0, + Parent = replaceScriptPanel, + }; + _replaceScriptButton.Clicked += OnReplaceScriptButtonClicked; + + Label replaceAllLabel = new Label + { + Text = "Replace All of Same Type", + AnchorPreset = AnchorPresets.BottomCenter, + Y = -34, + Parent = replaceScriptPanel, + }; + replaceAllLabel.X -= FlaxEngine.GUI.Style.Current.FontSmall.MeasureText(replaceAllLabel.Text).X; + + _shouldReplaceAllCheckbox = new CheckBox + { + TooltipText = "Wether or not to apply this script change to all scripts missing the same type.", + AnchorPreset = AnchorPresets.BottomCenter, + Y = -34, + Parent = replaceScriptPanel, + }; + + float centerDifference = (_shouldReplaceAllCheckbox.Right - replaceAllLabel.Left) / 2; + replaceAllLabel.X += centerDifference; + _shouldReplaceAllCheckbox.X += centerDifference; base.Initialize(layout); } + + private List FindActorsWithMatchingMissingScript() + { + List missingScripts = new List(); + + foreach (Actor actor in Level.GetActors()) + { + for (int scriptIndex = 0; scriptIndex < actor.ScriptsCount; scriptIndex++) + { + Script actorScript = actor.Scripts[scriptIndex]; + if (actorScript is not MissingScript missingActorScript) + { + continue; + } + + MissingScript currentMissing = Values[0] as MissingScript; + if (missingActorScript.MissingTypeName != currentMissing.MissingTypeName) + { + continue; + } + + // Matching MissingScript. + missingScripts.Add(missingActorScript); + } + } + + return missingScripts; + } + + private void RunReplacementMulticast(List actions) + { + if (actions.Count == 0) + { + Editor.LogWarning("Failed to replace scripts!"); + return; + } + + var multiAction = new MultiUndoAction(actions); + multiAction.Do(); + var presenter = ParentEditor.Presenter; + if (presenter != null) + { + presenter.Undo.AddAction(multiAction); + presenter.Control.Focus(); + } + } + + private void ReplaceScript(ScriptType script, bool replaceAllInScene) + { + var actions = new List(4); + + List missingScripts = new List(); + if (!replaceAllInScene) + { + missingScripts.Add(Values[0] as MissingScript); + } else + { + missingScripts = FindActorsWithMatchingMissingScript(); + } + + foreach (MissingScript missingScript in missingScripts) + { + AddRemoveScript addReplacementScriptAction = AddRemoveScript.Add(missingScript.Actor, script); + actions.Add(addReplacementScriptAction); + } + RunReplacementMulticast(actions); + + for (int actionIdx = 0; actionIdx < actions.Count; actionIdx++) + { + AddRemoveScript addRemoveScriptAction = (AddRemoveScript) actions[actionIdx]; + int orderInParent = addRemoveScriptAction.GetOrderInParent(); + + Script newScript = missingScripts[actionIdx].Actor.Scripts[orderInParent]; + missingScripts[actionIdx].ReferenceScript = newScript; + } + actions.Clear(); + + foreach (MissingScript missingScript in missingScripts) + { + actions.Add(AddRemoveScript.Remove(missingScript)); + } + RunReplacementMulticast(actions); + } + + private void OnReplaceScriptButtonClicked() + { + var scripts = Editor.Instance.CodeEditing.Scripts.Get(); + if (scripts.Count == 0) + { + // No scripts + var cm1 = new ContextMenu(); + cm1.AddButton("No scripts in project"); + cm1.Show(_dropPanel, _replaceScriptButton.BottomLeft); + return; + } + + // Show context menu with list of scripts to add + var cm = new ItemsListContextMenu(180); + for (int i = 0; i < scripts.Count; i++) + { + cm.AddItem(new TypeSearchPopup.TypeItemView(scripts[i])); + } + // Get the parent (actor properties editor) of the parent (Scripts Editor) of our editor. + _propertiesEditor = ParentEditor.ParentEditor; + + cm.ItemClicked += item => ReplaceScript((ScriptType)item.Tag, _shouldReplaceAllCheckbox.Checked); + cm.SortItems(); + cm.Show(_dropPanel, _replaceScriptButton.BottomLeft - new Float2((cm.Width - _replaceScriptButton.Width) / 2, 0)); + } } diff --git a/Source/Editor/Undo/Actions/AddRemoveScriptAction.cs b/Source/Editor/Undo/Actions/AddRemoveScriptAction.cs index a5710494c..86afd4c0b 100644 --- a/Source/Editor/Undo/Actions/AddRemoveScriptAction.cs +++ b/Source/Editor/Undo/Actions/AddRemoveScriptAction.cs @@ -75,6 +75,11 @@ namespace FlaxEditor.Actions _enabled = true; } + public int GetOrderInParent() + { + return _orderInParent; + } + /// /// Creates a new added script undo action. /// @@ -184,6 +189,7 @@ namespace FlaxEditor.Actions script.Parent = parentActor; if (_orderInParent != -1) script.OrderInParent = _orderInParent; + _orderInParent = script.OrderInParent; // Ensure _orderInParent is correct for script that want to use it. if (_prefabObjectId != Guid.Empty) SceneObject.Internal_LinkPrefab(Object.GetUnmanagedPtr(script), ref _prefabId, ref _prefabObjectId); Editor.Instance.Scene.MarkSceneEdited(parentActor.Scene);