LMQT/Assets/TouchScript/Editor/Gestures/GestureEditor.cs
2024-12-10 09:03:45 +08:00

420 lines
15 KiB
C#

/*
* @author Valentin Simonov / http://va.lent.in/
*/
using System;
using TouchScript.Editor.EditorUI;
using TouchScript.Gestures;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
using System.Reflection;
namespace TouchScript.Editor.Gestures
{
[CustomEditor(typeof(Gesture), true)]
internal class GestureEditor : UnityEditor.Editor
{
private const string FRIENDLY_GESTURES_PROP = "friendlyGestures";
public static readonly GUIContent TEXT_GENERAL_HEADER = new GUIContent("General settings", "General settings.");
public static readonly GUIContent TEXT_LIMITS_HEADER = new GUIContent("Limits", "Properties that limit the gesture.");
public static readonly GUIContent TEXT_GESTURES_HEADER = new GUIContent("Interaction with other Gestures", "Settings which allow this gesture to interact with other gestures.");
public static readonly GUIContent TEXT_ADVANCED_HEADER = new GUIContent("Advanced", "Advanced properties.");
public static readonly GUIContent TEXT_USE_SEND_MESSAGE_HEADER = new GUIContent("Use SendMessage", "Enables sending events through SendMessage. Warnning: this method is slow!");
public static readonly GUIContent TEXT_USE_UNITY_EVENTS_HEADER = new GUIContent("Use Unity Events", "Enables sending events through Unity Events.");
public static readonly GUIContent TEXT_FRIENDLY = new GUIContent("Friendly Gestures", "List of gestures which can work together with this gesture.");
public static readonly GUIContent TEXT_DEBUG_MODE = new GUIContent("Debug", "Turns on gesture debug mode.");
public static readonly GUIContent TEXT_SEND_STATE_CHANGE_MESSAGES = new GUIContent("Send State Change Messages", "If checked, the gesture will send a message for every state change. Gestures usually have their own more specific messages, so you should keep this toggle unchecked unless you really want state change messages.");
public static readonly GUIContent TEXT_SEND_MESSAGE_TARGET = new GUIContent("Target", "The GameObject target of Unity Messages. If null, host GameObject is used.");
public static readonly GUIContent TEXT_SEND_STATE_CHANGE_EVENTS = new GUIContent("Send State Change Events", "If checked, the gesture will send a events for every state change. Gestures usually have their own more specific messages, so you should keep this toggle unchecked unless you really want state change events.");
public static readonly GUIContent TEXT_REQUIRE_GESTURE_TO_FAIL = new GUIContent("Require Other Gesture to Fail", "Another gesture must fail for this gesture to start.");
public static readonly GUIContent TEXT_LIMIT_POINTERS = new GUIContent(" Limit Pointers", "");
protected bool shouldDrawAdvanced = false;
protected bool shouldDrawGeneral = true;
private Gesture instance;
private SerializedProperty basicEditor;
private SerializedProperty debugMode, friendlyGestures, requireGestureToFail,
minPointers, maxPointers,
useSendMessage, sendMessageTarget, sendStateChangeMessages,
useUnityEvents, sendStateChangeEvents;
private SerializedProperty OnStateChange;
private SerializedProperty advancedProps, limitsProps, generalProps;
private PropertyInfo useUnityEvents_prop, useSendMessage_prop;
private ReorderableList friendlyGesturesList;
private int indexToRemove = -1;
private float minPointersFloat, maxPointersFloat;
protected virtual void OnEnable()
{
instance = target as Gesture;
advancedProps = serializedObject.FindProperty("advancedProps");
limitsProps = serializedObject.FindProperty("limitsProps");
generalProps = serializedObject.FindProperty("generalProps");
basicEditor = serializedObject.FindProperty("basicEditor");
debugMode = serializedObject.FindProperty("debugMode");
friendlyGestures = serializedObject.FindProperty("friendlyGestures");
requireGestureToFail = serializedObject.FindProperty("requireGestureToFail");
useSendMessage = serializedObject.FindProperty("useSendMessage");
sendMessageTarget = serializedObject.FindProperty("sendMessageTarget");
sendStateChangeMessages = serializedObject.FindProperty("sendStateChangeMessages");
useUnityEvents = serializedObject.FindProperty("useUnityEvents");
sendStateChangeEvents = serializedObject.FindProperty("sendStateChangeEvents");
minPointers = serializedObject.FindProperty("minPointers");
maxPointers = serializedObject.FindProperty("maxPointers");
OnStateChange = serializedObject.FindProperty("OnStateChange");
var type = instance.GetType();
useUnityEvents_prop = type.GetProperty("UseUnityEvents", BindingFlags.Instance | BindingFlags.Public);
useSendMessage_prop = type.GetProperty("UseSendMessage", BindingFlags.Instance | BindingFlags.Public);
minPointersFloat = minPointers.intValue;
maxPointersFloat = maxPointers.intValue;
friendlyGesturesList = new ReorderableList(serializedObject, friendlyGestures, false, true, false, true);
friendlyGesturesList.drawHeaderCallback += (rect) => GUI.Label(rect, TEXT_FRIENDLY);
friendlyGesturesList.drawElementCallback += (rect, index, active, focused) =>
{
rect.height = 16;
var gesture = friendlyGestures.GetArrayElementAtIndex(index).objectReferenceValue as Gesture;
if (gesture == null)
{
// Killing null elements.
indexToRemove = index;
EditorGUI.LabelField(rect, GUIContent.none);
return;
}
EditorGUI.LabelField(rect, string.Format("{0} @ {1}", gesture.GetType().Name, gesture.name), GUIElements.BoxLabel);
};
friendlyGesturesList.onRemoveCallback += list => { indexToRemove = list.index; };
}
public override void OnInspectorGUI()
{
#if UNITY_5_6_OR_NEWER
serializedObject.UpdateIfRequiredOrScript();
#else
serializedObject.UpdateIfDirtyOrScript();
#endif
GUILayout.Space(5);
bool display;
if (basicEditor.boolValue)
{
drawBasic();
if (GUIElements.BasicHelpBox(getHelpText()))
{
basicEditor.boolValue = false;
Repaint();
}
}
else
{
if (shouldDrawGeneral)
{
display = GUIElements.Header(TEXT_GENERAL_HEADER, generalProps);
if (display)
{
EditorGUI.indentLevel++;
drawGeneral();
EditorGUI.indentLevel--;
}
}
drawOtherGUI();
display = GUIElements.Header(TEXT_LIMITS_HEADER, limitsProps);
if (display)
{
EditorGUI.indentLevel++;
drawLimits();
EditorGUI.indentLevel--;
}
display = GUIElements.Header(TEXT_GESTURES_HEADER, friendlyGestures);
if (display)
{
EditorGUI.indentLevel++;
drawFriendlyGestures();
drawRequireToFail();
GUILayout.Space(5);
EditorGUI.indentLevel--;
}
display = GUIElements.Header(TEXT_USE_UNITY_EVENTS_HEADER, useUnityEvents, useUnityEvents, useUnityEvents_prop);
if (display)
{
EditorGUI.indentLevel++;
using (new EditorGUI.DisabledGroupScope(!useUnityEvents.boolValue))
{
drawUnityEvents();
}
EditorGUI.indentLevel--;
}
display = GUIElements.Header(TEXT_USE_SEND_MESSAGE_HEADER, useSendMessage, useSendMessage, useSendMessage_prop);
if (display)
{
EditorGUI.indentLevel++;
using (new EditorGUI.DisabledGroupScope(!useSendMessage.boolValue))
{
drawSendMessage();
}
EditorGUI.indentLevel--;
}
if (shouldDrawAdvanced)
{
display = GUIElements.Header(TEXT_ADVANCED_HEADER, advancedProps);
if (display)
{
EditorGUI.indentLevel++;
drawAdvanced();
EditorGUI.indentLevel--;
}
}
drawDebug();
}
serializedObject.ApplyModifiedProperties();
}
protected virtual void drawBasic()
{
}
protected virtual GUIContent getHelpText()
{
return new GUIContent("");
}
protected virtual void drawOtherGUI()
{
}
protected virtual void drawGeneral()
{
}
protected virtual void drawLimits()
{
var limitPointers = (minPointers.intValue > 0) || (maxPointers.intValue > 0);
var newLimitPointers = EditorGUILayout.ToggleLeft(TEXT_LIMIT_POINTERS, limitPointers);
if (newLimitPointers)
{
if (!limitPointers)
{
minPointersFloat = 0;
maxPointersFloat = 10;
}
else
{
minPointersFloat = (float) minPointers.intValue;
maxPointersFloat = (float) maxPointers.intValue;
}
//or this values doesn't change from script properly
EditorGUI.indentLevel++;
EditorGUILayout.LabelField("Min: " + (int)minPointersFloat + ", Max: " + (int)maxPointersFloat);
EditorGUILayout.MinMaxSlider(ref minPointersFloat, ref maxPointersFloat, 0, 10, GUILayout.MaxWidth(150));
EditorGUI.indentLevel--;
}
else
{
if (limitPointers)
{
minPointersFloat = 0;
maxPointersFloat = 0;
}
}
minPointers.intValue = (int)minPointersFloat;
maxPointers.intValue = (int)maxPointersFloat;
}
protected virtual void drawFriendlyGestures()
{
GUILayout.Space(5);
drawGestureList(friendlyGestures, addFriendlyGesture);
GUILayout.Space(5);
}
protected virtual void drawUnityEvents()
{
EditorGUILayout.PropertyField(OnStateChange);
EditorGUILayout.PropertyField(sendStateChangeEvents, TEXT_SEND_STATE_CHANGE_EVENTS);
}
protected virtual void drawSendMessage()
{
EditorGUILayout.PropertyField(sendMessageTarget, TEXT_SEND_MESSAGE_TARGET);
EditorGUILayout.PropertyField(sendStateChangeMessages, TEXT_SEND_STATE_CHANGE_MESSAGES);
}
protected virtual void drawAdvanced()
{
}
protected virtual void drawDebug()
{
if (debugMode == null) return;
EditorGUILayout.PropertyField(debugMode, TEXT_DEBUG_MODE);
}
protected virtual void drawRequireToFail()
{
EditorGUILayout.PropertyField(requireGestureToFail, TEXT_REQUIRE_GESTURE_TO_FAIL);
}
#region Gesture List
private void drawGestureList(SerializedProperty prop, Action<SerializedProperty, Gesture> addGesture)
{
indexToRemove = -1;
// Rect listRect = EditorGUI.IndentedRect(GUILayoutUtility.GetRect(0.0f, (prop.arraySize == 0 ? 0 : prop.arraySize - 1) * 16 + 60, GUILayout.ExpandWidth(true)));
// friendlyGesturesList.DoList(listRect);
friendlyGesturesList.DoLayoutList();
GUILayout.Space(9);
Rect dropArea = GUILayoutUtility.GetRect(0.0f, 50.0f, GUIElements.Box, GUILayout.ExpandWidth(true));
GUI.Box(dropArea, "Drag a Gesture Here", GUIElements.Box);
switch (Event.current.type)
{
case EventType.DragUpdated:
if (dropArea.Contains(Event.current.mousePosition))
DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
break;
case EventType.DragPerform:
if (dropArea.Contains(Event.current.mousePosition))
{
DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
DragAndDrop.AcceptDrag();
foreach (UnityEngine.Object obj in DragAndDrop.objectReferences)
{
if (obj is GameObject)
{
var go = obj as GameObject;
Gesture[] gestures = go.GetComponents<Gesture>();
foreach (Gesture gesture in gestures)
{
addGesture(prop, gesture);
}
}
else if (obj is Gesture)
{
addGesture(prop, obj as Gesture);
}
}
Event.current.Use();
}
break;
}
if (indexToRemove > -1)
{
removeFriendlyGestureAt(prop, indexToRemove);
}
}
private void addFriendlyGesture(SerializedProperty prop, Gesture value)
{
if (value == null || value == target) return;
// Adding that gesture to this gesture.
var shouldAdd = true;
for (int i = 0; i < prop.arraySize; i++)
{
if (prop.GetArrayElementAtIndex(i).objectReferenceValue == value)
{
shouldAdd = false;
break;
}
}
if (shouldAdd)
{
prop.arraySize++;
prop.GetArrayElementAtIndex(prop.arraySize - 1).objectReferenceValue = value;
}
// Adding this gesture to that gesture.
shouldAdd = true;
var so = new SerializedObject(value);
so.Update();
SerializedProperty p = so.FindProperty(FRIENDLY_GESTURES_PROP);
for (int i = 0; i < p.arraySize; i++)
{
if (p.GetArrayElementAtIndex(i).objectReferenceValue == target)
{
shouldAdd = false;
break;
}
}
if (shouldAdd)
{
p.arraySize++;
p.GetArrayElementAtIndex(p.arraySize - 1).objectReferenceValue = target;
so.ApplyModifiedProperties();
EditorUtility.SetDirty(value);
}
}
private Gesture removeFriendlyGestureAt(SerializedProperty prop, int index)
{
// Removing that gesture from this gesture.
var gesture = prop.GetArrayElementAtIndex(index).objectReferenceValue as Gesture;
removeFromArray(prop, index);
if (gesture == null) return null;
// Removing this gesture from that gesture.
var so = new SerializedObject(gesture);
so.Update();
SerializedProperty p = so.FindProperty(FRIENDLY_GESTURES_PROP);
for (int j = 0; j < p.arraySize; j++)
{
if (p.GetArrayElementAtIndex(j).objectReferenceValue == target)
{
removeFromArray(p, j);
break;
}
}
so.ApplyModifiedProperties();
EditorUtility.SetDirty(gesture);
return gesture;
}
// A hack to remove a gesture from a list.
// Was needed because array.DeleteArrayElementAtIndex() wasn't actually deleting an item.
private void removeFromArray(SerializedProperty array, int index)
{
if (index != array.arraySize - 1)
{
array.GetArrayElementAtIndex(index).objectReferenceValue =
array.GetArrayElementAtIndex(array.arraySize - 1).objectReferenceValue;
}
array.arraySize--;
}
#endregion
}
}