|
|
|
using UnityEditor;
|
|
|
|
using UnityEngine;
|
|
|
|
using System;
|
|
|
|
using System.Collections;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Linq;
|
|
|
|
using Rotorz.ReorderableList;
|
|
|
|
|
|
|
|
namespace Fungus
|
|
|
|
{
|
|
|
|
|
|
|
|
[CustomEditor (typeof(Sequence))]
|
|
|
|
public class SequenceEditor : Editor
|
|
|
|
{
|
|
|
|
protected class AddCommandOperation
|
|
|
|
{
|
|
|
|
public Sequence sequence;
|
|
|
|
public Type commandType;
|
|
|
|
public int index;
|
|
|
|
}
|
|
|
|
|
|
|
|
public virtual void DrawInspectorGUI(FungusScript fungusScript)
|
|
|
|
{
|
|
|
|
serializedObject.Update();
|
|
|
|
|
|
|
|
// We acquire the serialized properties in the draw methods instead of in OnEnable as otherwise
|
|
|
|
// deleting or renaming a command class would generate a bunch of null reference exceptions.
|
|
|
|
SerializedProperty sequenceNameProp = serializedObject.FindProperty("sequenceName");
|
|
|
|
EditorGUILayout.PropertyField(sequenceNameProp);
|
|
|
|
|
|
|
|
SerializedProperty nodeRectProp = serializedObject.FindProperty("nodeRect");
|
|
|
|
EditorGUI.BeginChangeCheck();
|
|
|
|
float width = EditorGUILayout.FloatField(new GUIContent("Node Width"), nodeRectProp.rectValue.width);
|
|
|
|
if (EditorGUI.EndChangeCheck())
|
|
|
|
{
|
|
|
|
Rect nodeRect = nodeRectProp.rectValue;
|
|
|
|
nodeRect.width = Mathf.Max(width, 100);
|
|
|
|
nodeRectProp.rectValue = nodeRect;
|
|
|
|
}
|
|
|
|
|
|
|
|
EditorGUILayout.Separator();
|
|
|
|
|
|
|
|
serializedObject.ApplyModifiedProperties();
|
|
|
|
}
|
|
|
|
|
|
|
|
public virtual void DrawCommandListGUI(FungusScript fungusScript)
|
|
|
|
{
|
|
|
|
serializedObject.Update();
|
|
|
|
|
|
|
|
Sequence sequence = target as Sequence;
|
|
|
|
UpdateIndentLevels(sequence);
|
|
|
|
|
|
|
|
SerializedProperty commandListProperty = serializedObject.FindProperty("commandList");
|
|
|
|
|
|
|
|
ReorderableListGUI.Title(sequence.sequenceName);
|
|
|
|
CommandListAdaptor adaptor = new CommandListAdaptor(commandListProperty, 0);
|
|
|
|
adaptor.nodeRect = sequence.nodeRect;
|
|
|
|
|
|
|
|
ReorderableListFlags flags = ReorderableListFlags.HideAddButton | ReorderableListFlags.HideRemoveButtons | ReorderableListFlags.DisableContextMenu;
|
|
|
|
ReorderableListControl.DrawControlFromState(adaptor, null, flags);
|
|
|
|
|
|
|
|
Rect bottomBoxRect = GUILayoutUtility.GetRect(sequence.nodeRect.width, 20);
|
|
|
|
bottomBoxRect.y -= 7;
|
|
|
|
bottomBoxRect.x += 5;
|
|
|
|
bottomBoxRect.width -= 10;
|
|
|
|
if (sequence.commandList.Count == 0)
|
|
|
|
{
|
|
|
|
bottomBoxRect.y -= 16;
|
|
|
|
}
|
|
|
|
|
|
|
|
GUIStyle boxStyle = new GUIStyle();
|
|
|
|
boxStyle.border = new RectOffset(2, 2, 2, 1);
|
|
|
|
boxStyle.margin = new RectOffset(5, 5, 5, 0);
|
|
|
|
boxStyle.padding = new RectOffset(5, 5, 0, 0);
|
|
|
|
boxStyle.alignment = TextAnchor.MiddleLeft;
|
|
|
|
boxStyle.normal.background = FungusEditorResources.texTitleBackground;
|
|
|
|
boxStyle.normal.textColor = EditorGUIUtility.isProSkin
|
|
|
|
? new Color(0.8f, 0.8f, 0.8f)
|
|
|
|
: new Color(0.2f, 0.2f, 0.2f);
|
|
|
|
|
|
|
|
GUI.Box(bottomBoxRect, "", boxStyle);
|
|
|
|
|
|
|
|
if (!Application.isPlaying &&
|
|
|
|
sequence == fungusScript.selectedSequence)
|
|
|
|
{
|
|
|
|
// Show add command button
|
|
|
|
{
|
|
|
|
Rect plusRect = bottomBoxRect;
|
|
|
|
plusRect.x = plusRect.width - 19;
|
|
|
|
plusRect.y += 2;
|
|
|
|
plusRect.width = 16;
|
|
|
|
plusRect.height = 16;
|
|
|
|
|
|
|
|
if (GUI.Button(plusRect, FungusEditorResources.texAddButton, new GUIStyle()))
|
|
|
|
{
|
|
|
|
ShowCommandMenu();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Show delete sequence button
|
|
|
|
{
|
|
|
|
Rect deleteRect = new Rect();
|
|
|
|
deleteRect.x = sequence.nodeRect.width - 28;
|
|
|
|
deleteRect.y = 7;
|
|
|
|
deleteRect.width = 16;
|
|
|
|
deleteRect.height = 16;
|
|
|
|
|
|
|
|
if (GUI.Button(deleteRect, "", new GUIStyle("WinBtnCloseWin")))
|
|
|
|
{
|
|
|
|
FungusScriptWindow.DeleteSequence();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove any null entries in the command list.
|
|
|
|
// This can happen when a command class is deleted or renamed.
|
|
|
|
for (int i = commandListProperty.arraySize - 1; i >= 0; --i)
|
|
|
|
{
|
|
|
|
SerializedProperty commandProperty = commandListProperty.GetArrayElementAtIndex(i);
|
|
|
|
if (commandProperty.objectReferenceValue == null)
|
|
|
|
{
|
|
|
|
commandListProperty.DeleteArrayElementAtIndex(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
serializedObject.ApplyModifiedProperties();
|
|
|
|
|
|
|
|
if (!Application.isPlaying)
|
|
|
|
{
|
|
|
|
if (Event.current.button == 1 &&
|
|
|
|
Event.current.type == EventType.MouseDown)
|
|
|
|
{
|
|
|
|
// Set a flag to show the context menu on the new display frame
|
|
|
|
// This gives the sequence list display a chance to update
|
|
|
|
FungusScriptWindow.showContextMenu = true;
|
|
|
|
Event.current.Use();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected virtual void UpdateIndentLevels(Sequence sequence)
|
|
|
|
{
|
|
|
|
int indentLevel = 0;
|
|
|
|
foreach(Command command in sequence.commandList)
|
|
|
|
{
|
|
|
|
if (command == null)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
indentLevel += command.GetPreIndent();
|
|
|
|
command.indentLevel = Math.Max(indentLevel, 0);
|
|
|
|
indentLevel += command.GetPostIndent();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static public void SequenceField(SerializedProperty property, GUIContent label, GUIContent nullLabel, FungusScript fungusScript)
|
|
|
|
{
|
|
|
|
if (fungusScript == null)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Sequence sequence = property.objectReferenceValue as Sequence;
|
|
|
|
|
|
|
|
// Build dictionary of child sequences
|
|
|
|
List<GUIContent> sequenceNames = new List<GUIContent>();
|
|
|
|
|
|
|
|
int selectedIndex = 0;
|
|
|
|
sequenceNames.Add(nullLabel);
|
|
|
|
Sequence[] sequences = fungusScript.GetComponentsInChildren<Sequence>(true);
|
|
|
|
for (int i = 0; i < sequences.Length; ++i)
|
|
|
|
{
|
|
|
|
sequenceNames.Add(new GUIContent(sequences[i].sequenceName));
|
|
|
|
|
|
|
|
if (sequence == sequences[i])
|
|
|
|
{
|
|
|
|
selectedIndex = i + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
selectedIndex = EditorGUILayout.Popup(label, selectedIndex, sequenceNames.ToArray());
|
|
|
|
if (selectedIndex == 0)
|
|
|
|
{
|
|
|
|
sequence = null; // Option 'None'
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
sequence = sequences[selectedIndex - 1];
|
|
|
|
}
|
|
|
|
|
|
|
|
property.objectReferenceValue = sequence;
|
|
|
|
}
|
|
|
|
|
|
|
|
static public Sequence SequenceField(Rect position, GUIContent nullLabel, FungusScript fungusScript, Sequence sequence)
|
|
|
|
{
|
|
|
|
if (fungusScript == null)
|
|
|
|
{
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
Sequence result = sequence;
|
|
|
|
|
|
|
|
// Build dictionary of child sequences
|
|
|
|
List<GUIContent> sequenceNames = new List<GUIContent>();
|
|
|
|
|
|
|
|
int selectedIndex = 0;
|
|
|
|
sequenceNames.Add(nullLabel);
|
|
|
|
Sequence[] sequences = fungusScript.GetComponentsInChildren<Sequence>();
|
|
|
|
for (int i = 0; i < sequences.Length; ++i)
|
|
|
|
{
|
|
|
|
sequenceNames.Add(new GUIContent(sequences[i].name));
|
|
|
|
|
|
|
|
if (sequence == sequences[i])
|
|
|
|
{
|
|
|
|
selectedIndex = i + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
selectedIndex = EditorGUI.Popup(position, selectedIndex, sequenceNames.ToArray());
|
|
|
|
if (selectedIndex == 0)
|
|
|
|
{
|
|
|
|
result = null; // Option 'None'
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
result = sequences[selectedIndex - 1];
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Compare delegate for sorting the list of command attributes
|
|
|
|
static int CompareCommandAttributes(KeyValuePair<System.Type, CommandInfoAttribute> x, KeyValuePair<System.Type, CommandInfoAttribute> y)
|
|
|
|
{
|
|
|
|
int compare = (x.Value.Category.CompareTo(y.Value.Category));
|
|
|
|
if (compare == 0)
|
|
|
|
{
|
|
|
|
compare = (x.Value.CommandName.CompareTo(y.Value.CommandName));
|
|
|
|
}
|
|
|
|
return compare;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ShowCommandMenu()
|
|
|
|
{
|
|
|
|
Sequence sequence = target as Sequence;
|
|
|
|
int index = sequence.commandList.Count;
|
|
|
|
|
|
|
|
GenericMenu commandMenu = new GenericMenu();
|
|
|
|
|
|
|
|
// Build menu list
|
|
|
|
List<System.Type> menuTypes = EditorExtensions.FindDerivedTypes(typeof(Command)).ToList();
|
|
|
|
List<KeyValuePair<System.Type, CommandInfoAttribute>> filteredAttributes = GetFilteredCommandInfoAttribute(menuTypes);
|
|
|
|
|
|
|
|
filteredAttributes.Sort( CompareCommandAttributes );
|
|
|
|
|
|
|
|
foreach(var keyPair in filteredAttributes)
|
|
|
|
{
|
|
|
|
AddCommandOperation commandOperation = new AddCommandOperation();
|
|
|
|
|
|
|
|
commandOperation.sequence = sequence;
|
|
|
|
commandOperation.commandType = keyPair.Key;
|
|
|
|
commandOperation.index = index;
|
|
|
|
|
|
|
|
GUIContent menuItem;
|
|
|
|
if (keyPair.Value.Category == "")
|
|
|
|
{
|
|
|
|
menuItem = new GUIContent(keyPair.Value.CommandName);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
menuItem = new GUIContent (keyPair.Value.Category + "/" + keyPair.Value.CommandName);
|
|
|
|
}
|
|
|
|
|
|
|
|
commandMenu.AddItem(menuItem, false, AddCommandCallback, commandOperation);
|
|
|
|
}
|
|
|
|
|
|
|
|
commandMenu.ShowAsContext();
|
|
|
|
}
|
|
|
|
|
|
|
|
List<KeyValuePair<System.Type,CommandInfoAttribute>> GetFilteredCommandInfoAttribute(List<System.Type> menuTypes)
|
|
|
|
{
|
|
|
|
Dictionary<string, KeyValuePair<System.Type, CommandInfoAttribute>> filteredAttributes = new Dictionary<string, KeyValuePair<System.Type, CommandInfoAttribute>>();
|
|
|
|
|
|
|
|
foreach (System.Type type in menuTypes)
|
|
|
|
{
|
|
|
|
object[] attributes = type.GetCustomAttributes(false);
|
|
|
|
foreach (object obj in attributes)
|
|
|
|
{
|
|
|
|
CommandInfoAttribute infoAttr = obj as CommandInfoAttribute;
|
|
|
|
if (infoAttr != null)
|
|
|
|
{
|
|
|
|
string dictionaryName = string.Format("{0}/{1}", infoAttr.Category, infoAttr.CommandName);
|
|
|
|
|
|
|
|
int existingItemPriority = -1;
|
|
|
|
if (filteredAttributes.ContainsKey(dictionaryName))
|
|
|
|
{
|
|
|
|
existingItemPriority = filteredAttributes[dictionaryName].Value.Priority;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (infoAttr.Priority > existingItemPriority)
|
|
|
|
{
|
|
|
|
KeyValuePair<System.Type, CommandInfoAttribute> keyValuePair = new KeyValuePair<System.Type, CommandInfoAttribute>(type, infoAttr);
|
|
|
|
filteredAttributes[dictionaryName] = keyValuePair;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return filteredAttributes.Values.ToList<KeyValuePair<System.Type,CommandInfoAttribute>>();
|
|
|
|
}
|
|
|
|
|
|
|
|
void AddCommandCallback(object obj)
|
|
|
|
{
|
|
|
|
AddCommandOperation commandOperation = obj as AddCommandOperation;
|
|
|
|
|
|
|
|
Sequence sequence = commandOperation.sequence;
|
|
|
|
if (sequence == null)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
sequence.GetFungusScript().ClearSelectedCommands();
|
|
|
|
|
|
|
|
Command newCommand = Undo.AddComponent(sequence.gameObject, commandOperation.commandType) as Command;
|
|
|
|
sequence.GetFungusScript().AddSelectedCommand(newCommand);
|
|
|
|
|
|
|
|
// Let command know it has just been added to the sequence
|
|
|
|
newCommand.OnCommandAdded(sequence);
|
|
|
|
|
|
|
|
Undo.RecordObject(sequence, "Set command type");
|
|
|
|
if (commandOperation.index < sequence.commandList.Count - 1)
|
|
|
|
{
|
|
|
|
sequence.commandList[commandOperation.index] = newCommand;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
sequence.commandList.Add(newCommand);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|