From 7f5ed8e0cfe0ec0bc93fa0d826f0c6514a4509c5 Mon Sep 17 00:00:00 2001 From: chrisgregan Date: Fri, 19 Sep 2014 14:07:01 +0100 Subject: [PATCH] Copy and paste support for commands --- .../FungusScript/Editor/CommandEditor.cs | 31 --- .../FungusScript/Editor/CommandListAdaptor.cs | 44 ++-- .../FungusScript/Editor/FungusScriptWindow.cs | 1 + .../FungusScript/Editor/SequenceEditor.cs | 215 ++++++++++++++++-- .../Editor/VariableListAdaptor.cs | 2 +- Assets/Fungus/FungusScript/Scripts/Command.cs | 6 +- .../FungusScript/Scripts/CommandCopyBuffer.cs | 64 ++++++ .../Scripts/CommandCopyBuffer.cs.meta | 8 + .../FungusScript/Scripts/FungusScript.cs | 6 - 9 files changed, 306 insertions(+), 71 deletions(-) create mode 100644 Assets/Fungus/FungusScript/Scripts/CommandCopyBuffer.cs create mode 100644 Assets/Fungus/FungusScript/Scripts/CommandCopyBuffer.cs.meta diff --git a/Assets/Fungus/FungusScript/Editor/CommandEditor.cs b/Assets/Fungus/FungusScript/Editor/CommandEditor.cs index d5e735e4..c5a862f9 100644 --- a/Assets/Fungus/FungusScript/Editor/CommandEditor.cs +++ b/Assets/Fungus/FungusScript/Editor/CommandEditor.cs @@ -86,14 +86,6 @@ namespace Fungus t.enabled = enabled; } - if (fungusScript != null) - { - if (GUILayout.Button("Copy", EditorStyles.miniButton)) - { - fungusScript.copyCommand = t; - } - } - GUILayout.EndHorizontal(); GUI.backgroundColor = Color.white; @@ -118,29 +110,6 @@ namespace Fungus DrawDefaultInspector(); } - static public Command PasteCommand(Command copyCommand, Sequence sequence) - { - if (sequence == null) - { - return null; - } - - System.Type type = copyCommand.GetType(); - Component copy = Undo.AddComponent(sequence.gameObject, type); - System.Reflection.FieldInfo[] fields = type.GetFields(); - foreach (System.Reflection.FieldInfo field in fields) - { - field.SetValue(copy, field.GetValue(copyCommand)); - } - - Undo.RecordObject(sequence, "Paste Command"); - - Command newCommand = copy as Command; - sequence.commandList.Add(newCommand); - - return newCommand; - } - static public void ObjectField(SerializedProperty property, GUIContent label, GUIContent nullLabel, List objectList) where T : MonoBehaviour { if (property == null) diff --git a/Assets/Fungus/FungusScript/Editor/CommandListAdaptor.cs b/Assets/Fungus/FungusScript/Editor/CommandListAdaptor.cs index 18a6d249..740c611b 100644 --- a/Assets/Fungus/FungusScript/Editor/CommandListAdaptor.cs +++ b/Assets/Fungus/FungusScript/Editor/CommandListAdaptor.cs @@ -106,7 +106,14 @@ namespace Fungus // Add the command as a new component Sequence parentSequence = command.GetComponent(); - Command newCommand = CommandEditor.PasteCommand(command, parentSequence); + + System.Type type = command.GetType(); + Command newCommand = Undo.AddComponent(parentSequence.gameObject, type) as Command; + System.Reflection.FieldInfo[] fields = type.GetFields(); + foreach (System.Reflection.FieldInfo field in fields) + { + field.SetValue(newCommand, field.GetValue(command)); + } _arrayProperty.InsertArrayElementAtIndex(index); _arrayProperty.GetArrayElementAtIndex(index).objectReferenceValue = newCommand; @@ -166,8 +173,8 @@ namespace Fungus error = true; } - bool selected = (Application.isPlaying && command.IsExecuting()) || - (!Application.isPlaying && fungusScript.selectedCommand == command); + bool highlight = (Application.isPlaying && command.IsExecuting()) || + (!Application.isPlaying && fungusScript.selectedCommand == command); float indentSize = 20; for (int i = 0; i < command.indentLevel; ++i) @@ -196,7 +203,7 @@ namespace Fungus Rect summaryRect = buttonRect; summaryRect.x += buttonWidth - 1; - summaryRect.width = position.width - buttonWidth - indentWidth - 15; + summaryRect.width = position.width - buttonWidth - indentWidth - 23; if (!Application.isPlaying && Event.current.type == EventType.MouseDown && @@ -214,7 +221,7 @@ namespace Fungus } Color summaryBackgroundColor = Color.white; - if (selected) + if (highlight) { summaryBackgroundColor = Color.green; buttonBackgroundColor = Color.green; @@ -234,7 +241,7 @@ namespace Fungus GUIStyle summaryStyle = new GUIStyle(EditorStyles.miniButtonRight); summaryStyle.alignment = TextAnchor.MiddleLeft; - if (error && !selected) + if (error && !highlight) { summaryStyle.normal.textColor = Color.white; } @@ -244,16 +251,23 @@ namespace Fungus GUI.backgroundColor = Color.white; - Rect menuRect = summaryRect; - menuRect.x += menuRect.width + 2; - menuRect.y = position.y + 1; - menuRect.width = 18; - menuRect.height = position.height; - - GUIStyle menuButtonStyle = new GUIStyle(EditorStyles.popup); - if (GUI.Button(menuRect, new GUIContent("", "Select command type"), menuButtonStyle)) + if (!Application.isPlaying) { - ShowCommandMenu(index, fungusScript.selectedSequence); + Rect menuRect = summaryRect; + menuRect.x += menuRect.width + 4; + menuRect.y = position.y + 1; + menuRect.width = 22; + menuRect.height = position.height; + GUIStyle menuButtonStyle = new GUIStyle("Foldout"); + if (GUI.Button(menuRect, new GUIContent("", "Select command type"), menuButtonStyle)) + { + ShowCommandMenu(index, fungusScript.selectedSequence); + } + + Rect selectRect = position; + selectRect.x -= 19; + selectRect.width = 20; + command.selected = EditorGUI.Toggle(selectRect, command.selected); } } diff --git a/Assets/Fungus/FungusScript/Editor/FungusScriptWindow.cs b/Assets/Fungus/FungusScript/Editor/FungusScriptWindow.cs index aa5b8e2c..64fe612c 100755 --- a/Assets/Fungus/FungusScript/Editor/FungusScriptWindow.cs +++ b/Assets/Fungus/FungusScript/Editor/FungusScriptWindow.cs @@ -37,6 +37,7 @@ namespace Fungus Sequence sequence = newSequenceGO.AddComponent(); fungusScript.startSequence = sequence; fungusScript.selectedSequence = sequence; + Undo.RegisterCreatedObjectUndo(newFungusScriptGO, "Create Fungus Script"); } // Implementing this method causes the padlock image to display on the window diff --git a/Assets/Fungus/FungusScript/Editor/SequenceEditor.cs b/Assets/Fungus/FungusScript/Editor/SequenceEditor.cs index 071d2738..36f5236e 100644 --- a/Assets/Fungus/FungusScript/Editor/SequenceEditor.cs +++ b/Assets/Fungus/FungusScript/Editor/SequenceEditor.cs @@ -48,37 +48,37 @@ namespace Fungus ReorderableListGUI.Title("Command Sequence"); SerializedProperty commandListProperty = serializedObject.FindProperty("commandList"); CommandListAdaptor adaptor = new CommandListAdaptor(commandListProperty, 0); - ReorderableListControl.DrawControlFromState(adaptor, null, 0); + ReorderableListControl.DrawControlFromState(adaptor, null, ReorderableListFlags.HideRemoveButtons); - if (Application.isPlaying) + if (!Application.isPlaying) { - serializedObject.ApplyModifiedProperties(); - return; - } - - EditorGUILayout.BeginHorizontal(); - - GUILayout.FlexibleSpace(); - - if (fungusScript.copyCommand != null) - { - if (GUILayout.Button("Paste")) + Rect copyMenuRect = GUILayoutUtility.GetLastRect(); + copyMenuRect.y += copyMenuRect.height - 17; + copyMenuRect.width = 24; + copyMenuRect.height = 18; + if (GUI.Button(copyMenuRect, "", new GUIStyle("DropDown"))) { - fungusScript.selectedCommand = CommandEditor.PasteCommand(fungusScript.copyCommand, fungusScript.selectedSequence); + ShowCopyMenu(); } } - EditorGUILayout.EndHorizontal(); + EditorGUILayout.BeginHorizontal(); if (fungusScript.selectedCommand != null) { CommandInfoAttribute infoAttr = CommandEditor.GetCommandInfo(fungusScript.selectedCommand.GetType()); if (infoAttr != null) { - EditorGUILayout.HelpBox(infoAttr.HelpText, MessageType.Info); + EditorGUILayout.HelpBox(infoAttr.CommandName + ":\n" + infoAttr.HelpText, MessageType.Info, true); } } + // Need to expand to fill space or else reorderable list width changes if no command is selected + GUILayout.FlexibleSpace(); + + EditorGUILayout.EndHorizontal(); + + serializedObject.ApplyModifiedProperties(); } @@ -168,6 +168,189 @@ namespace Fungus return result; } + + protected void ShowCopyMenu() + { + bool showCut = false; + bool showCopy = false; + bool showDelete = false; + bool showPaste = false; + + Sequence sequence = target as Sequence; + foreach (Command command in sequence.commandList) + { + if (command.selected) + { + showCut = true; + showCopy = true; + showDelete = true; + } + } + + CommandCopyBuffer commandCopyBuffer = CommandCopyBuffer.GetInstance(); + + if (commandCopyBuffer.HasCommands()) + { + showPaste = true; + } + + GenericMenu commandMenu = new GenericMenu(); + + commandMenu.AddItem (new GUIContent ("Select All"), false, SelectAll); + commandMenu.AddItem (new GUIContent ("Select None"), false, SelectNone); + commandMenu.AddSeparator(""); + + if (showCut) + { + commandMenu.AddItem (new GUIContent ("Cut"), false, Cut); + } + else + { + commandMenu.AddDisabledItem(new GUIContent ("Cut")); + } + + if (showCopy) + { + commandMenu.AddItem (new GUIContent ("Copy"), false, Copy); + } + else + { + commandMenu.AddDisabledItem(new GUIContent ("Copy")); + } + + if (showPaste) + { + commandMenu.AddItem (new GUIContent ("Paste"), false, Paste); + } + else + { + commandMenu.AddDisabledItem(new GUIContent ("Paste")); + } + + if (showDelete) + { + commandMenu.AddItem (new GUIContent ("Delete"), false, Delete); + } + else + { + commandMenu.AddDisabledItem(new GUIContent ("Delete")); + } + + commandMenu.ShowAsContext(); + } + + protected virtual void SelectAll() + { + Sequence sequence = target as Sequence; + foreach (Command command in sequence.commandList) + { + Undo.RecordObject(command, "Select All"); + command.selected = true; + } + } + + protected virtual void SelectNone() + { + Sequence sequence = target as Sequence; + FungusScript fungusScript = sequence.GetFungusScript(); + if (fungusScript != null) + { + Undo.RecordObject(fungusScript, "Select None"); + fungusScript.selectedCommand = null; + } + + foreach (Command command in sequence.commandList) + { + Undo.RecordObject(command, "Select None"); + command.selected = false; + } + } + + protected virtual void Cut() + { + Copy(); + Delete(); + } + + protected virtual void Copy() + { + Sequence sequence = target as Sequence; + + CommandCopyBuffer commandCopyBuffer = CommandCopyBuffer.GetInstance(); + + commandCopyBuffer.Clear(); + + foreach (Command command in sequence.commandList) + { + if (command.selected) + { + System.Type type = command.GetType(); + Command newCommand = Undo.AddComponent(commandCopyBuffer.gameObject, type) as Command; + System.Reflection.FieldInfo[] fields = type.GetFields(); + foreach (System.Reflection.FieldInfo field in fields) + { + field.SetValue(newCommand, field.GetValue(command)); + } + newCommand.selected = false; + } + } + } + + protected virtual void Paste() + { + CommandCopyBuffer commandCopyBuffer = CommandCopyBuffer.GetInstance(); + + Sequence sequence = target as Sequence; + FungusScript fungusScript = sequence.GetFungusScript(); + + // Find where to paste commands in sequence (either at end or after selected command) + int pasteIndex = sequence.commandList.Count; + if (fungusScript.selectedCommand != null) + { + for (int i = 0; i < sequence.commandList.Count; ++i) + { + Command command = sequence.commandList[i]; + + if (fungusScript.selectedCommand == command) + { + pasteIndex = i + 1; + break; + } + } + } + + foreach (Command command in commandCopyBuffer.GetCommands()) + { + System.Type type = command.GetType(); + Command newCommand = Undo.AddComponent(sequence.gameObject, type) as Command; + System.Reflection.FieldInfo[] fields = type.GetFields(); + foreach (System.Reflection.FieldInfo field in fields) + { + field.SetValue(newCommand, field.GetValue(command)); + } + newCommand.selected = false; + + Undo.RecordObject(sequence, "Paste"); + sequence.commandList.Insert(pasteIndex++, newCommand); + } + } + + protected virtual void Delete() + { + Sequence sequence = target as Sequence; + + for (int i = sequence.commandList.Count - 1; i >= 0; --i) + { + Command command = sequence.commandList[i]; + if (command != null && + command.selected) + { + Undo.DestroyObjectImmediate(command); + Undo.RecordObject(sequence, "Delete"); + sequence.commandList.RemoveAt(i); + } + } + } } } \ No newline at end of file diff --git a/Assets/Fungus/FungusScript/Editor/VariableListAdaptor.cs b/Assets/Fungus/FungusScript/Editor/VariableListAdaptor.cs index 8f6fbe73..3b02ea8b 100644 --- a/Assets/Fungus/FungusScript/Editor/VariableListAdaptor.cs +++ b/Assets/Fungus/FungusScript/Editor/VariableListAdaptor.cs @@ -210,7 +210,7 @@ namespace Fungus GUI.Label(valueRect, type); scope = (VariableScope)EditorGUI.EnumPopup(scopeRect, variable.scope); - // To access properties in a monobehavior, you have new a SerializedObject + // To access properties in a monobehavior, you have to new a SerializedObject // http://answers.unity3d.com/questions/629803/findrelativeproperty-never-worked-for-me-how-does.html SerializedObject variableObject = new SerializedObject(this[index].objectReferenceValue); SerializedProperty keyProp = variableObject.FindProperty("key"); diff --git a/Assets/Fungus/FungusScript/Scripts/Command.cs b/Assets/Fungus/FungusScript/Scripts/Command.cs index bb1a9793..fd81f9eb 100644 --- a/Assets/Fungus/FungusScript/Scripts/Command.cs +++ b/Assets/Fungus/FungusScript/Scripts/Command.cs @@ -19,8 +19,7 @@ namespace Fungus public string CommandName { get; set; } public string HelpText { get; set; } } - - [RequireComponent(typeof(Sequence))] + public class Command : MonoBehaviour { [HideInInspector] @@ -29,6 +28,9 @@ namespace Fungus [HideInInspector] public int indentLevel; + [HideInInspector] + public bool selected; + public virtual Sequence GetSequence() { return gameObject.GetComponent(); diff --git a/Assets/Fungus/FungusScript/Scripts/CommandCopyBuffer.cs b/Assets/Fungus/FungusScript/Scripts/CommandCopyBuffer.cs new file mode 100644 index 00000000..7ae0442f --- /dev/null +++ b/Assets/Fungus/FungusScript/Scripts/CommandCopyBuffer.cs @@ -0,0 +1,64 @@ +using UnityEngine; +using System.Collections; + +namespace Fungus +{ + + public class CommandCopyBuffer : Sequence + { + protected static CommandCopyBuffer instance; + + /** + * Returns the CommandCopyBuffer singleton instance. + * Will create a CommandCopyBuffer game object if none currently exists. + */ + static public CommandCopyBuffer GetInstance() + { + if (instance == null) + { + // Static variables are not serialized (e.g. when playing in the editor) + // We need to reaquire the static reference to the game object in this case + GameObject go = GameObject.Find("_CommandCopyBuffer"); + if (go != null) + { + instance = go.GetComponent(); + } + else + { + go = new GameObject("_CommandCopyBuffer"); + instance = go.AddComponent(); + go.hideFlags = HideFlags.HideInHierarchy; + } + } + + return instance; + } + + protected virtual void Start() + { + if (Application.isPlaying) + { + Destroy(this.gameObject); + } + } + + public virtual bool HasCommands() + { + return GetCommands().Length > 0; + } + + public virtual Command[] GetCommands() + { + return GetComponents(); + } + + public virtual void Clear() + { + foreach (Command command in GetCommands()) + { + DestroyImmediate(command); + } + } + } + +} \ No newline at end of file diff --git a/Assets/Fungus/FungusScript/Scripts/CommandCopyBuffer.cs.meta b/Assets/Fungus/FungusScript/Scripts/CommandCopyBuffer.cs.meta new file mode 100644 index 00000000..6960f818 --- /dev/null +++ b/Assets/Fungus/FungusScript/Scripts/CommandCopyBuffer.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 28859462f29464188bb29cc9f1f6e92c +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Assets/Fungus/FungusScript/Scripts/FungusScript.cs b/Assets/Fungus/FungusScript/Scripts/FungusScript.cs index b75d9bef..2869f53f 100644 --- a/Assets/Fungus/FungusScript/Scripts/FungusScript.cs +++ b/Assets/Fungus/FungusScript/Scripts/FungusScript.cs @@ -19,12 +19,6 @@ namespace Fungus [System.NonSerialized] public Sequence executingSequence; - /** - * Copy and paste buffer for command objects. - */ - [System.NonSerialized] - public Command copyCommand; - /** * Scroll position of Fungus Script editor window (map view). */