#if UNITY_EDITOR using MonKey.Editor.Internal; using MonKey.Extensions; using System; using System.Collections.Generic; using System.Linq; using System.Text; using UnityEditor; using UnityEngine; using Object = UnityEngine.Object; namespace MonKey.Editor.Commands { public enum ActionConfirmationMode { CLICK, ENTER, ESCAPE, ENTER_AND_CLICK, ENTER_AND_ESCAPE, NONE } /// /// A scene command is an interactive commands on the scene that updates automatically and stops its logic when required. /// It can be started and stopped using the MonkeyEditorUtils class. /// public abstract class SceneCommand { internal bool Hidden = false; public bool HideGUI = false; internal double TimeSinceLastUpdate; public double TimeBetweenUpdate = 0; public float GUIHeight = 280; private int lineNumber; public bool ShouldRepaintSceneView = true; /// /// Should the scene command stops when the editor enters or exit play /// public bool StopOnPlayStateChange = true; /// /// The name of the command that will be displayed in the interface on MonKey /// public string SceneCommandName = ""; /// /// An event raised when the Scene Command has just stopped /// public event Action OnActionDone; /// /// Any logic that should happen when the command stops (for instance registering undo) can be done here. /// public virtual void Stop() { OnActionDone?.Invoke(this); } /// /// Called on every Unity Update in the editor /// public abstract void Update(); /// /// Called when the scene is focused and the MonKey GUI is called. /// Unity Requires some logic to be called on GUI, and will thorw en error message if you don't. /// public virtual void OnSceneGUI() { } private Vector2 scrollPosition; protected bool DisplayedInWindow = false; public virtual void DisplayCommandPanel(float width, float height, bool inWindow = false) { if (Hidden) return; this.DisplayedInWindow = inWindow; Color normalText = EditorStyles.label.normal.textColor; Color activeText = EditorStyles.label.active.textColor; Color hoverText = EditorStyles.label.hover.textColor; Color focusedText = EditorStyles.label.focused.textColor; if (!DisplayedInWindow) { EditorStyles.label.normal.textColor = MonkeyStyle.Instance.SearchResultTextColor; EditorStyles.label.active.textColor = MonkeyStyle.Instance.WarningColor; EditorStyles.label.hover.textColor = MonkeyStyle.Instance.WarningColor; EditorStyles.label.focused.textColor = MonkeyStyle.Instance.WarningColor; } if (!DisplayedInWindow) GUILayout.BeginArea(new Rect(0, SceneView.currentDrawingSceneView.position.height - height - 20, width * 2, height), new GUIStyle() { normal = { background = MonkeyStyle.Instance.WindowBackgroundTex }, margin = new RectOffset(2, 2, 5, 5), }); if (!DisplayedInWindow) GUILayout.Label(SceneCommandName.Colored(MonkeyStyle.Instance.SearchResultTextColor).Bold(), new GUIStyle() { richText = true }); if (!DisplayedInWindow) scrollPosition = GUILayout.BeginScrollView(scrollPosition, new GUIStyle() { normal = { background = MonkeyStyle.Instance.ResultFieldTex }, margin = new RectOffset(0, 0, 5, 5), }); else { scrollPosition = GUILayout.BeginScrollView(scrollPosition); } GUILayout.BeginVertical(); lineNumber = 0; DisplayParameters(); PostDisplayParameters(); GUILayout.EndVertical(); GUILayout.EndScrollView(); if (!DisplayedInWindow) GUILayout.EndArea(); if (!DisplayedInWindow) { EditorStyles.label.normal.textColor = normalText; EditorStyles.label.active.textColor = activeText; EditorStyles.label.hover.textColor = hoverText; EditorStyles.label.focused.textColor = focusedText; } } public virtual void DisplayParameters() { } public virtual void PostDisplayParameters() { } public void DisplayMessage(string message) { ParamStartIndex(); if (!DisplayedInWindow) GUILayout.Label(message.Colored(MonkeyStyle.Instance.SearchResultTextColor) , MonkeyStyle.Instance.CommandConfirmationStyle); else { GUILayout.Label(message); } GUILayout.EndHorizontal(); } private void ParamStartIndex() { if (DisplayedInWindow) { GUILayout.BeginHorizontal(); return; } if (!MonkeyStyle.Instance) { MonkeyStyle.FindInstance(); GUILayout.BeginHorizontal(); return; } GUILayout.BeginHorizontal(lineNumber % 2 == 0 ? MonkeyStyle.Instance.ParameterLayoutForcedHighlightStyle : MonkeyStyle.Instance.ParameterLayoutNoHighlightStyle); lineNumber++; } public void DisplayBoolOption(string message, ref bool condition) { ParamStartIndex(); if (!DisplayedInWindow) GUILayout.Label(message.Colored(MonkeyStyle.Instance.SearchResultTextColor), new GUIStyle() { margin = new RectOffset(5, 0, 0, 0), richText = true, }); else { GUILayout.Label(message); } GUILayout.FlexibleSpace(); condition = GUILayout.Toggle(condition, ""); GUILayout.EndHorizontal(); } private Dictionary foldoutStatuses = new Dictionary(); public int DisplayObjectListOption(string name, List list) where T : Object { int changedID = -1; Type type = typeof(T); ParamStartIndex(); GUILayout.BeginVertical(); if (!foldoutStatuses.ContainsKey(name)) foldoutStatuses.Add(name, false); bool foldStatus = foldoutStatuses[name]; if (!DisplayedInWindow) foldStatus = EditorGUILayout.Foldout(foldStatus, name.Colored(MonkeyStyle.Instance.SearchResultTextColor), true, new GUIStyle(EditorStyles.foldout) { margin = new RectOffset(5, 0, 0, 0), richText = true, }); else { foldStatus = EditorGUILayout.Foldout(foldStatus, name, true); } foldoutStatuses[name] = foldStatus; if (foldStatus) { T[] array = list.ToArray(); for (int i = 0; i < array.Length; i++) { GUILayout.BeginHorizontal(); var member = array[i]; EditorGUILayout.LabelField(" "); list[i] = (T)EditorGUILayout.ObjectField("", array[i], type, true); if (GUILayout.Button("S")) { if (Selection.activeObject is T) list[i] = (T)Selection.activeObject; } if (member != list[i]) changedID = i; GUILayout.EndHorizontal(); } GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); if (!DisplayedInWindow) GUILayout.Label("Add".Colored(MonkeyStyle.Instance.SearchResultTextColor), new GUIStyle() { margin = new RectOffset(5, 0, 0, 0), richText = true, }); else { GUILayout.Label("Add"); } T newT = null; if (GUILayout.Button("S")) { if (Selection.activeObject is T) newT = (T)Selection.activeObject; } bool wasNull = !newT; newT = (T)EditorGUILayout.ObjectField("", newT, type, true); GUILayout.EndHorizontal(); if (newT) { if (wasNull) { if (Selection.objects.Contains(newT)) { list.AddRange(Selection.objects.Where(_ => _ is T && !list.Contains(_)).Convert(_ => (T)_)); } else { list.Add(newT); } } changedID = list.Count - 1; } list.RemoveAll(_ => !_); } GUILayout.EndVertical(); GUILayout.EndHorizontal(); return changedID; } public int DisplayVector2ArrayOption(string name, ref Vector2[] array) { int changedID = -1; ParamStartIndex(); if (!DisplayedInWindow) GUILayout.Label(name.Colored(MonkeyStyle.Instance.SearchResultTextColor), new GUIStyle() { margin = new RectOffset(5, 0, 0, 0), richText = true, }); else { GUILayout.Label(name); } GUILayout.FlexibleSpace(); GUILayout.BeginVertical(); for (int i = array.Length - 1; i >= 0; i--) { GUILayout.BeginHorizontal(); var member = array[i]; array[i] = EditorGUILayout.Vector2Field("", array[i]); if (member != array[i]) changedID = i; GUILayout.EndHorizontal(); } GUILayout.EndVertical(); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); return changedID; } public void DisplayObjectOption(string name, ref T obj) where T : Object { Type type = typeof(T); ParamStartIndex(); if (!DisplayedInWindow) GUILayout.Label(name.Colored(MonkeyStyle.Instance.SearchResultTextColor), new GUIStyle() { margin = new RectOffset(5, 0, 0, 0), richText = true, }); else { GUILayout.Label(name); } GUILayout.FlexibleSpace(); obj = (T)EditorGUILayout.ObjectField("", obj, type, true); if (GUILayout.Button("S")) { if (Selection.activeObject is T) obj = (T)Selection.activeObject; } GUILayout.EndHorizontal(); } public virtual void DisplayEnumOption(string name, ref Enum currentEnumValue) { ParamStartIndex(); if (!DisplayedInWindow) GUILayout.Label(name.Colored(MonkeyStyle.Instance.SearchResultTextColor), new GUIStyle() { margin = new RectOffset(5, 0, 0, 0), richText = true, }); else { GUILayout.Label(name); } GUILayout.FlexibleSpace(); currentEnumValue = EditorGUILayout.EnumPopup(currentEnumValue); GUILayout.EndHorizontal(); } public void DisplayIntOption(string name, ref int value) { ParamStartIndex(); /* GUILayout.Label(name.Colored(MonkeyStyle.Instance.SearchResultTextColor), new GUIStyle() { margin = new RectOffset(5, 0, 0, 0), richText = true, }); GUILayout.FlexibleSpace();*/ /* if (GUILayout.Button("-")) { value--; }*/ if (!DisplayedInWindow) value = EditorGUILayout.IntField(name, value, new GUIStyle(EditorStyles.numberField) { margin = new RectOffset(5, 5, 0, 0), }); else { value = EditorGUILayout.IntField(name, value); } /* if (GUILayout.Button("+")) { value++; }*/ GUILayout.EndHorizontal(); } public void DisplayDoubleOption(string name, ref double value) { ParamStartIndex(); var style = new GUIStyle(EditorStyles.numberField) { margin = new RectOffset(5, 0, 0, 0), richText = true, }; if (!DisplayedInWindow) value = EditorGUILayout.DoubleField(name, value, style); else { value = EditorGUILayout.DoubleField(name, value); } GUILayout.EndHorizontal(); } /// /// Displays a Float parameter on the scene interface /// /// /// /// true if teh value was changed public bool DisplayFloatOption(string name, ref float value) { float prevValue=value; ParamStartIndex(); var style = new GUIStyle(EditorStyles.numberField) { margin = new RectOffset(5, 0, 0, 0), richText = true, }; if (!DisplayedInWindow) value = EditorGUILayout.FloatField(name, value, style); else { value = EditorGUILayout.FloatField(name, value); } GUILayout.EndHorizontal(); return !Mathf.Approximately(prevValue, value); } public void DisplayFloatPercentOption(string name, ref float value) { ParamStartIndex(); /* var style = new GUIStyle(EditorStyles.numberField) { margin = new RectOffset(5, 0, 0, 0), richText = true, };*/ value = EditorGUILayout.Slider(name, value, 0, 1); GUILayout.EndHorizontal(); } public void DisplayVectorOption(string name, ref Vector3 vector) { ParamStartIndex(); /* GUILayout.Label(name.Colored(MonkeyStyle.Instance.SearchResultTextColor), new GUIStyle() { margin = new RectOffset(5, 0, 0, 0), richText = true, }); GUILayout.FlexibleSpace();*/ vector = EditorGUILayout.Vector3Field(name, vector); GUILayout.EndHorizontal(); } public void DisplayVector2Option(string name, ref Vector2 vector) { ParamStartIndex(); /*GUILayout.Label(name.Colored(MonkeyStyle.Instance.SearchResultTextColor), new GUIStyle() { margin = new RectOffset(5, 0, 0, 0), richText = true, }); GUILayout.FlexibleSpace();*/ vector = EditorGUILayout.Vector2Field(name, vector); GUILayout.EndHorizontal(); } public void DisplayButton(string name, Action action) { if (GUILayout.Button(name)) { action(); } } } public abstract class ConfirmedCommand : SceneCommand { public bool ShowActionButton = true; public ActionConfirmationMode ConfirmationMode = ActionConfirmationMode.ENTER_AND_ESCAPE; public virtual string ConfirmationMessage() { StringBuilder builder = new StringBuilder(); switch (ConfirmationMode) { case ActionConfirmationMode.CLICK: builder.Append("Click "); break; case ActionConfirmationMode.ENTER: builder.Append("Press ENTER "); break; case ActionConfirmationMode.ENTER_AND_CLICK: builder.Append("Press ENTER or Click "); break; case ActionConfirmationMode.ESCAPE: builder.Append("Press ESCAPE "); break; case ActionConfirmationMode.ENTER_AND_ESCAPE: builder.Append("Press ESCAPE or ENTER "); break; case ActionConfirmationMode.NONE: break; default: throw new ArgumentOutOfRangeException(); } builder.Append("To Execute '"); builder.Append(SceneCommandName); builder.Append("'"); return builder.ToString(); } public override void DisplayParameters() { DisplayMessage(ConfirmationMessage()); base.DisplayParameters(); } public override void PostDisplayParameters() { base.PostDisplayParameters(); if (ShowActionButton) DisplayButton("Apply", ApplyFunction); } public virtual void ApplyFunction() { Stop(); } public override void OnSceneGUI() { base.OnSceneGUI(); switch (ConfirmationMode) { case ActionConfirmationMode.CLICK: if (Event.current.type == EventType.MouseDown && Event.current.button == 0) Stop(); break; case ActionConfirmationMode.ENTER: if (MonkeyEditorUtils.IsKeyDown(KeyCode.KeypadEnter) || MonkeyEditorUtils.IsKeyDown(KeyCode.Return)) Stop(); break; case ActionConfirmationMode.ENTER_AND_CLICK: if ((Event.current.type == EventType.MouseDown && Event.current.button == 0) || MonkeyEditorUtils.IsKeyDown(KeyCode.KeypadEnter) || MonkeyEditorUtils.IsKeyDown(KeyCode.Return)) Stop(); break; case ActionConfirmationMode.ESCAPE: if (MonkeyEditorUtils.IsKeyDown(KeyCode.Escape)) Stop(); break; case ActionConfirmationMode.ENTER_AND_ESCAPE: if (MonkeyEditorUtils.IsKeyDown(KeyCode.Escape) || MonkeyEditorUtils.IsKeyDown(KeyCode.KeypadEnter) || MonkeyEditorUtils.IsKeyDown(KeyCode.Return)) Stop(); break; case ActionConfirmationMode.NONE: break; default: throw new ArgumentOutOfRangeException(); } } public override void Update() { if (!MonkeyEditorUtils.CurrentSceneView) MonkeyEditorUtils.CurrentSceneView = SceneView.currentDrawingSceneView; if (EditorWindow.mouseOverWindow != null && MonkeyEditorUtils.CurrentSceneView && EditorWindow.mouseOverWindow == MonkeyEditorUtils.CurrentSceneView) MonkeyEditorUtils.CurrentSceneView.Focus(); } } public abstract class TimedSceneCommand : SceneCommand { public float Duration = -1; public float CurrentTime = 0; protected TimedSceneCommand(float duration) { Duration = duration; } public override void Update() { CurrentTime += Time.deltaTime; if (CurrentTime >= Duration && Duration > 0) Stop(); } } public abstract class InteractiveCommand : ConfirmedCommand { protected string ActionOnSpace; public InteractiveCommand() { ConfirmationMode = ActionConfirmationMode.ENTER_AND_ESCAPE; } public override string ConfirmationMessage() { return "Press SPACE to " + ActionOnSpace + ", ESCAPE to stop"; } public override void OnSceneGUI() { base.OnSceneGUI(); if (Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Space) { OnSpaceDownAction(); } } protected virtual void OnSpaceDownAction() { } } internal class DelegateSceneCommand : SceneCommand { private Action delegateAction; public DelegateSceneCommand(Action delegateAction) { Hidden = true; this.delegateAction = delegateAction; } public override void Update() { delegateAction(); } } public class TimedMultiSceneCommand : TimedSceneCommand { private readonly SceneCommand[] commands; public TimedMultiSceneCommand(SceneCommand[] commands, float duration) : base(duration) { this.commands = commands; } public override void Update() { base.Update(); foreach (SceneCommand command in commands) { command.Update(); } } public override void OnSceneGUI() { base.OnSceneGUI(); foreach (SceneCommand command in commands) { command.OnSceneGUI(); } } public override void Stop() { base.Stop(); foreach (SceneCommand sceneCommand in commands) { sceneCommand.Stop(); } } } } #endif