From 594469bd44181886d30a137e320e7c248b136189 Mon Sep 17 00:00:00 2001 From: desktop-maesty/steve Date: Sun, 29 Jul 2018 20:12:32 +1000 Subject: [PATCH 01/11] Event Searchable list WIP --- Assets/Fungus/Scripts/Editor/BlockEditor.cs | 84 +---- .../Editor/EventSelectorPopupWindowContent.cs | 299 ++++++++++++++++++ .../EventSelectorPopupWindowContent.cs.meta | 11 + 3 files changed, 321 insertions(+), 73 deletions(-) create mode 100644 Assets/Fungus/Scripts/Editor/EventSelectorPopupWindowContent.cs create mode 100644 Assets/Fungus/Scripts/Editor/EventSelectorPopupWindowContent.cs.meta diff --git a/Assets/Fungus/Scripts/Editor/BlockEditor.cs b/Assets/Fungus/Scripts/Editor/BlockEditor.cs index 055f9a9c..cb095f28 100644 --- a/Assets/Fungus/Scripts/Editor/BlockEditor.cs +++ b/Assets/Fungus/Scripts/Editor/BlockEditor.cs @@ -18,12 +18,6 @@ namespace Fungus.EditorUtils [CustomEditor(typeof(Block))] public class BlockEditor : Editor { - protected class SetEventHandlerOperation - { - public Block block; - public Type eventHandlerType; - } - protected class AddCommandOperation { public Type commandType; @@ -46,6 +40,8 @@ namespace Fungus.EditorUtils protected int filteredCommandPreviewSelectedItem = 0; protected Type commandSelectedByTextInput; + private Rect lastEventPopupPos; + static List commandTypes; static List eventHandlerTypes; @@ -467,49 +463,18 @@ namespace Fungus.EditorUtils } } + var pos = EditorGUILayout.GetControlRect(true, 0, EditorStyles.objectField); + if (pos.x != 0) + { + lastEventPopupPos = pos; + lastEventPopupPos.x += EditorGUIUtility.labelWidth; + lastEventPopupPos.y += EditorGUIUtility.singleLineHeight; + } EditorGUILayout.BeginHorizontal(); EditorGUILayout.PrefixLabel(new GUIContent("Execute On Event")); - if (GUILayout.Button(new GUIContent(currentHandlerName), EditorStyles.popup)) + if (EditorGUILayout.DropdownButton(new GUIContent(currentHandlerName),FocusType.Passive)) { - SetEventHandlerOperation noneOperation = new SetEventHandlerOperation(); - noneOperation.block = block; - noneOperation.eventHandlerType = null; - - GenericMenu eventHandlerMenu = new GenericMenu(); - eventHandlerMenu.AddItem(new GUIContent("None"), false, OnSelectEventHandler, noneOperation); - - // Add event handlers with no category first - foreach (System.Type type in eventHandlerTypes) - { - EventHandlerInfoAttribute info = EventHandlerEditor.GetEventHandlerInfo(type); - if (info != null && - info.Category.Length == 0) - { - SetEventHandlerOperation operation = new SetEventHandlerOperation(); - operation.block = block; - operation.eventHandlerType = type; - - eventHandlerMenu.AddItem(new GUIContent(info.EventHandlerName), false, OnSelectEventHandler, operation); - } - } - - // Add event handlers with a category afterwards - foreach (System.Type type in eventHandlerTypes) - { - EventHandlerInfoAttribute info = EventHandlerEditor.GetEventHandlerInfo(type); - if (info != null && - info.Category.Length > 0) - { - SetEventHandlerOperation operation = new SetEventHandlerOperation(); - operation.block = block; - operation.eventHandlerType = type; - string typeName = info.Category + "/" + info.EventHandlerName; - eventHandlerMenu.AddItem(new GUIContent(typeName), false, OnSelectEventHandler, operation); - } - } - - - eventHandlerMenu.ShowAsContext(); + EventSelectorPopupWindowContent.DoEventHandlerPopUp(lastEventPopupPos, currentHandlerName, block, eventHandlerTypes); } EditorGUILayout.EndHorizontal(); @@ -524,33 +489,6 @@ namespace Fungus.EditorUtils } } - protected void OnSelectEventHandler(object obj) - { - SetEventHandlerOperation operation = obj as SetEventHandlerOperation; - Block block = operation.block; - System.Type selectedType = operation.eventHandlerType; - if (block == null) - { - return; - } - - Undo.RecordObject(block, "Set Event Handler"); - - if (block._EventHandler != null) - { - Undo.DestroyObjectImmediate(block._EventHandler); - } - - if (selectedType != null) - { - EventHandler newHandler = Undo.AddComponent(block.gameObject, selectedType) as EventHandler; - newHandler.ParentBlock = block; - block._EventHandler = newHandler; - } - - // Because this is an async call, we need to force prefab instances to record changes - PrefabUtility.RecordPrefabInstancePropertyModifications(block); - } public static void BlockField(SerializedProperty property, GUIContent label, GUIContent nullLabel, Flowchart flowchart) { diff --git a/Assets/Fungus/Scripts/Editor/EventSelectorPopupWindowContent.cs b/Assets/Fungus/Scripts/Editor/EventSelectorPopupWindowContent.cs new file mode 100644 index 00000000..d805964d --- /dev/null +++ b/Assets/Fungus/Scripts/Editor/EventSelectorPopupWindowContent.cs @@ -0,0 +1,299 @@ +using UnityEditor; +using UnityEditorInternal; +using UnityEngine; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using Rotorz.ReorderableList; +using System.IO; +using System.Reflection; + +namespace Fungus.EditorUtils +{ + /// + /// Searchable Popup Window for selecting Event type, used by block editor + /// + /// Inspired by https://github.com/roboryantron/UnityEditorJunkie/blob/master/Assets/SearchableEnum/Code/Editor/SearchablePopup.cs + /// + public class EventSelectorPopupWindowContent : PopupWindowContent + { + protected class SetEventHandlerOperation + { + public Block block; + public Type eventHandlerType; + } + + private string currentHandlerName; + private Block block; + private List eventHandlerTypes; + private int hoverIndex; + private readonly string SEARCH_CONTROL_NAME = "PopupSearchControlName"; + private readonly float ROW_HEIGHT = EditorGUIUtility.singleLineHeight; + private List allItems = new List(), visibleItems = new List(); + private string currentFilter; + private Vector2 scroll; + private int scrollToIndex; + private float scrollOffset; + private int currentIndex; + + public EventSelectorPopupWindowContent(string currentHandlerName, Block block, List eventHandlerTypes) + { + this.currentHandlerName = currentHandlerName; + this.block = block; + this.eventHandlerTypes = eventHandlerTypes; + + foreach (System.Type type in eventHandlerTypes) + { + EventHandlerInfoAttribute info = EventHandlerEditor.GetEventHandlerInfo(type); + if (info != null) + { + allItems.Add(info.Category + "/" + info.EventHandlerName); + } + else + { + allItems.Add(type.Name); + } + } + visibleItems.AddRange(allItems); + } + + public override void OnGUI(Rect rect) + { + Rect searchRect = new Rect(0, 0, rect.width, EditorStyles.toolbar.fixedHeight); + Rect scrollRect = Rect.MinMaxRect(0, searchRect.yMax, rect.xMax, rect.yMax); + + HandleKeyboard(); + DrawSearch(searchRect); + DrawSelectionArea(scrollRect); + } + + private void DrawSearch(Rect rect) + { + if (Event.current.type == EventType.Repaint) + EditorStyles.toolbar.Draw(rect, false, false, false, false); + + Rect searchRect = new Rect(rect); + searchRect.xMin += 6; + searchRect.xMax -= 6; + searchRect.y += 2; + //searchRect.width -= CancelButton.fixedWidth; + + GUI.FocusControl(SEARCH_CONTROL_NAME); + GUI.SetNextControlName(SEARCH_CONTROL_NAME); + string newText = GUI.TextField(searchRect, "FilterBy"); + + //if (list.UpdateFilter(newText)) + //{ + // hoverIndex = 0; + // scroll = Vector2.zero; + //} + + //searchRect.x = searchRect.xMax; + //searchRect.width = CancelButton.fixedWidth; + + //if (string.IsNullOrEmpty(list.Filter)) + // GUI.Box(searchRect, GUIContent.none, DisabledCancelButton); + //else if (GUI.Button(searchRect, "x", CancelButton)) + //{ + // list.UpdateFilter(""); + // scroll = Vector2.zero; + //} + } + + private void DrawSelectionArea(Rect scrollRect) + { + Rect contentRect = new Rect(0, 0, + scrollRect.width - GUI.skin.verticalScrollbar.fixedWidth, + visibleItems.Count * ROW_HEIGHT); + + scroll = GUI.BeginScrollView(scrollRect, scroll, contentRect); + + Rect rowRect = new Rect(0, 0, scrollRect.width, ROW_HEIGHT); + + for (int i = 0; i < visibleItems.Count; i++) + { + if (scrollToIndex == i && + (Event.current.type == EventType.Repaint + || Event.current.type == EventType.Layout)) + { + Rect r = new Rect(rowRect); + r.y += scrollOffset; + GUI.ScrollTo(r); + scrollToIndex = -1; + scroll.x = 0; + } + + if (rowRect.Contains(Event.current.mousePosition)) + { + if (Event.current.type == EventType.MouseMove || + Event.current.type == EventType.ScrollWheel) + hoverIndex = i; + if (Event.current.type == EventType.MouseDown) + { + //onSelectionMade(list.Entries[i].Index); + SelectByName(visibleItems[i]); + EditorWindow.focusedWindow.Close(); + } + } + + DrawRow(rowRect, i); + + rowRect.y = rowRect.yMax; + } + + GUI.EndScrollView(); + } + + private static void DrawBox(Rect rect, Color tint) + { + Color c = GUI.color; + GUI.color = tint; + GUI.Box(rect, ""); + GUI.color = c; + } + + private void DrawRow(Rect rowRect, int i) + { + if (i == currentIndex) + DrawBox(rowRect, Color.cyan); + else if (i == hoverIndex) + DrawBox(rowRect, Color.white); + + Rect labelRect = new Rect(rowRect); + //labelRect.xMin += ROW_INDENT; + + GUI.Label(labelRect, visibleItems[i]); + } + + /// + /// Process keyboard input to navigate the choices or make a selection. + /// + private void HandleKeyboard() + { + if (Event.current.type == EventType.KeyDown) + { + if (Event.current.keyCode == KeyCode.DownArrow) + { + hoverIndex = Mathf.Min(visibleItems.Count - 1, hoverIndex + 1); + Event.current.Use(); + scrollToIndex = hoverIndex; + scrollOffset = ROW_HEIGHT; + } + + if (Event.current.keyCode == KeyCode.UpArrow) + { + hoverIndex = Mathf.Max(0, hoverIndex - 1); + Event.current.Use(); + scrollToIndex = hoverIndex; + scrollOffset = -ROW_HEIGHT; + } + + if (Event.current.keyCode == KeyCode.Return) + { + if (hoverIndex >= 0 && hoverIndex < visibleItems.Count) + { + SelectByName(visibleItems[hoverIndex]); + //onSelectionMade(list.Entries[hoverIndex].Index); + EditorWindow.focusedWindow.Close(); + } + } + + if (Event.current.keyCode == KeyCode.Escape) + { + EditorWindow.focusedWindow.Close(); + } + } + } + + static public void DoEventHandlerPopUp(Rect rect, string currentHandlerName, Block block, List eventHandlerTypes) + { + //new method + EventSelectorPopupWindowContent win = new EventSelectorPopupWindowContent(currentHandlerName, block, eventHandlerTypes); + PopupWindow.Show(rect, win); + + //old method + + SetEventHandlerOperation noneOperation = new SetEventHandlerOperation(); + noneOperation.block = block; + noneOperation.eventHandlerType = null; + + GenericMenu eventHandlerMenu = new GenericMenu(); + eventHandlerMenu.AddItem(new GUIContent("None"), false, OnSelectEventHandler, noneOperation); + + // Add event handlers with no category first + foreach (System.Type type in eventHandlerTypes) + { + EventHandlerInfoAttribute info = EventHandlerEditor.GetEventHandlerInfo(type); + if (info != null && + info.Category.Length == 0) + { + SetEventHandlerOperation operation = new SetEventHandlerOperation(); + operation.block = block; + operation.eventHandlerType = type; + + eventHandlerMenu.AddItem(new GUIContent(info.EventHandlerName), false, OnSelectEventHandler, operation); + } + } + + // Add event handlers with a category afterwards + foreach (System.Type type in eventHandlerTypes) + { + EventHandlerInfoAttribute info = EventHandlerEditor.GetEventHandlerInfo(type); + if (info != null && + info.Category.Length > 0) + { + SetEventHandlerOperation operation = new SetEventHandlerOperation(); + operation.block = block; + operation.eventHandlerType = type; + string typeName = info.Category + "/" + info.EventHandlerName; + eventHandlerMenu.AddItem(new GUIContent(typeName), false, OnSelectEventHandler, operation); + } + } + + + eventHandlerMenu.ShowAsContext(); + + + } + + + static protected void OnSelectEventHandler(object obj) + { + SetEventHandlerOperation operation = obj as SetEventHandlerOperation; + Block block = operation.block; + System.Type selectedType = operation.eventHandlerType; + if (block == null) + { + return; + } + + Undo.RecordObject(block, "Set Event Handler"); + + if (block._EventHandler != null) + { + Undo.DestroyObjectImmediate(block._EventHandler); + } + + if (selectedType != null) + { + EventHandler newHandler = Undo.AddComponent(block.gameObject, selectedType) as EventHandler; + newHandler.ParentBlock = block; + block._EventHandler = newHandler; + } + + // Because this is an async call, we need to force prefab instances to record changes + PrefabUtility.RecordPrefabInstancePropertyModifications(block); + } + + protected void SelectByName(string name) + { + var loc = allItems.IndexOf(name); + SetEventHandlerOperation operation = new SetEventHandlerOperation(); + operation.block = block; + operation.eventHandlerType = eventHandlerTypes[loc]; + OnSelectEventHandler(operation); + } + } +} \ No newline at end of file diff --git a/Assets/Fungus/Scripts/Editor/EventSelectorPopupWindowContent.cs.meta b/Assets/Fungus/Scripts/Editor/EventSelectorPopupWindowContent.cs.meta new file mode 100644 index 00000000..5abf409b --- /dev/null +++ b/Assets/Fungus/Scripts/Editor/EventSelectorPopupWindowContent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f9ca5de337e96bf4c8a414c5c374d629 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From f4adf025b0df20f0ee3542705c351c1482570706 Mon Sep 17 00:00:00 2001 From: desktop-maesty/steve Date: Mon, 30 Jul 2018 21:00:20 +1000 Subject: [PATCH 02/11] ExceuteOnEvent dropdown is searchable -can double click to circumvent the search drop down -can remove event by setting None -use search similar to add command method --- Assets/Fungus/Scripts/Editor/BlockEditor.cs | 4 +- .../Editor/EventSelectorPopupWindowContent.cs | 104 ++++++++++++------ 2 files changed, 74 insertions(+), 34 deletions(-) diff --git a/Assets/Fungus/Scripts/Editor/BlockEditor.cs b/Assets/Fungus/Scripts/Editor/BlockEditor.cs index cb095f28..2fa6266c 100644 --- a/Assets/Fungus/Scripts/Editor/BlockEditor.cs +++ b/Assets/Fungus/Scripts/Editor/BlockEditor.cs @@ -47,8 +47,8 @@ namespace Fungus.EditorUtils static void CacheEventHandlerTypes() { - eventHandlerTypes = EditorExtensions.FindDerivedTypes(typeof(EventHandler)).ToList(); - commandTypes = EditorExtensions.FindDerivedTypes(typeof(Command)).ToList(); + eventHandlerTypes = EditorExtensions.FindDerivedTypes(typeof(EventHandler)).Where(x=>!x.IsAbstract).ToList(); + commandTypes = EditorExtensions.FindDerivedTypes(typeof(Command)).Where(x => !x.IsAbstract).ToList(); } [UnityEditor.Callbacks.DidReloadScripts] diff --git a/Assets/Fungus/Scripts/Editor/EventSelectorPopupWindowContent.cs b/Assets/Fungus/Scripts/Editor/EventSelectorPopupWindowContent.cs index d805964d..a09b474c 100644 --- a/Assets/Fungus/Scripts/Editor/EventSelectorPopupWindowContent.cs +++ b/Assets/Fungus/Scripts/Editor/EventSelectorPopupWindowContent.cs @@ -25,38 +25,56 @@ namespace Fungus.EditorUtils public Type eventHandlerType; } + public class FilteredListItem + { + public FilteredListItem(int index, string str) + { + origIndex = index; + name = str; + lowerName = str.ToLowerInvariant(); + } + public int origIndex; + public string name, lowerName; + } + private string currentHandlerName; private Block block; private List eventHandlerTypes; private int hoverIndex; private readonly string SEARCH_CONTROL_NAME = "PopupSearchControlName"; private readonly float ROW_HEIGHT = EditorGUIUtility.singleLineHeight; - private List allItems = new List(), visibleItems = new List(); - private string currentFilter; + private List allItems = new List(), visibleItems = new List(); + private string currentFilter = string.Empty; private Vector2 scroll; private int scrollToIndex; private float scrollOffset; private int currentIndex; + static readonly char[] SEARCH_SPLITS = new char[]{ '/', ' ' }; + public EventSelectorPopupWindowContent(string currentHandlerName, Block block, List eventHandlerTypes) { this.currentHandlerName = currentHandlerName; this.block = block; this.eventHandlerTypes = eventHandlerTypes; + int i = 0; foreach (System.Type type in eventHandlerTypes) { EventHandlerInfoAttribute info = EventHandlerEditor.GetEventHandlerInfo(type); if (info != null) { - allItems.Add(info.Category + "/" + info.EventHandlerName); + allItems.Add(new FilteredListItem(i, (info.Category.Length > 0 ? info.Category + "/" : "") + info.EventHandlerName)); } else { - allItems.Add(type.Name); + allItems.Add(new FilteredListItem(i, type.Name)); } + + i++; } - visibleItems.AddRange(allItems); + allItems.Sort((lhs, rhs) => lhs.name.CompareTo(rhs.name)); + UpdateFilter(); } public override void OnGUI(Rect rect) @@ -82,24 +100,41 @@ namespace Fungus.EditorUtils GUI.FocusControl(SEARCH_CONTROL_NAME); GUI.SetNextControlName(SEARCH_CONTROL_NAME); - string newText = GUI.TextField(searchRect, "FilterBy"); - - //if (list.UpdateFilter(newText)) - //{ - // hoverIndex = 0; - // scroll = Vector2.zero; - //} - - //searchRect.x = searchRect.xMax; - //searchRect.width = CancelButton.fixedWidth; - - //if (string.IsNullOrEmpty(list.Filter)) - // GUI.Box(searchRect, GUIContent.none, DisabledCancelButton); - //else if (GUI.Button(searchRect, "x", CancelButton)) - //{ - // list.UpdateFilter(""); - // scroll = Vector2.zero; - //} + var prevFilter = currentFilter; + currentFilter = GUI.TextField(searchRect, currentFilter); + + if (prevFilter != currentFilter) + { + UpdateFilter(); + } + } + + private void UpdateFilter() + { + var curlower = currentFilter.ToLowerInvariant(); + var lowers = curlower.Split(SEARCH_SPLITS); + lowers = lowers.Where(x => x.Length > 0).ToArray(); + + if (lowers == null || lowers.Length == 0) + { + visibleItems.AddRange(allItems); + } + else + { + visibleItems = allItems.Where(x => + { + foreach (var item in lowers) + { + if (x.lowerName.Contains(currentFilter)) + return true; + } + return false; + }).ToList(); + } + + hoverIndex = 0; + scroll = Vector2.zero; + visibleItems.Insert(0, new FilteredListItem(-1, "None")); } private void DrawSelectionArea(Rect scrollRect) @@ -133,7 +168,7 @@ namespace Fungus.EditorUtils if (Event.current.type == EventType.MouseDown) { //onSelectionMade(list.Entries[i].Index); - SelectByName(visibleItems[i]); + SelectByOrigIndex(visibleItems[i].origIndex); EditorWindow.focusedWindow.Close(); } } @@ -164,7 +199,7 @@ namespace Fungus.EditorUtils Rect labelRect = new Rect(rowRect); //labelRect.xMin += ROW_INDENT; - GUI.Label(labelRect, visibleItems[i]); + GUI.Label(labelRect, visibleItems[i].name); } /// @@ -194,7 +229,7 @@ namespace Fungus.EditorUtils { if (hoverIndex >= 0 && hoverIndex < visibleItems.Count) { - SelectByName(visibleItems[hoverIndex]); + SelectByOrigIndex(visibleItems[hoverIndex].origIndex); //onSelectionMade(list.Entries[hoverIndex].Index); EditorWindow.focusedWindow.Close(); } @@ -214,7 +249,7 @@ namespace Fungus.EditorUtils PopupWindow.Show(rect, win); //old method - + SetEventHandlerOperation noneOperation = new SetEventHandlerOperation(); noneOperation.block = block; noneOperation.eventHandlerType = null; @@ -252,10 +287,7 @@ namespace Fungus.EditorUtils } } - eventHandlerMenu.ShowAsContext(); - - } @@ -289,10 +321,18 @@ namespace Fungus.EditorUtils protected void SelectByName(string name) { - var loc = allItems.IndexOf(name); + var loc = allItems.First(x => x.name == name); + SetEventHandlerOperation operation = new SetEventHandlerOperation(); + operation.block = block; + operation.eventHandlerType = eventHandlerTypes[loc.origIndex]; + OnSelectEventHandler(operation); + } + + protected void SelectByOrigIndex(int index) + { SetEventHandlerOperation operation = new SetEventHandlerOperation(); operation.block = block; - operation.eventHandlerType = eventHandlerTypes[loc]; + operation.eventHandlerType = (index >= 0 && index Date: Wed, 1 Aug 2018 19:47:19 +1000 Subject: [PATCH 03/11] EventSelector correct width and configurable heigth --- Assets/Fungus/Scripts/Editor/BlockEditor.cs | 2 +- .../Editor/EventSelectorPopupWindowContent.cs | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/Assets/Fungus/Scripts/Editor/BlockEditor.cs b/Assets/Fungus/Scripts/Editor/BlockEditor.cs index 2fa6266c..d739301d 100644 --- a/Assets/Fungus/Scripts/Editor/BlockEditor.cs +++ b/Assets/Fungus/Scripts/Editor/BlockEditor.cs @@ -474,7 +474,7 @@ namespace Fungus.EditorUtils EditorGUILayout.PrefixLabel(new GUIContent("Execute On Event")); if (EditorGUILayout.DropdownButton(new GUIContent(currentHandlerName),FocusType.Passive)) { - EventSelectorPopupWindowContent.DoEventHandlerPopUp(lastEventPopupPos, currentHandlerName, block, eventHandlerTypes); + EventSelectorPopupWindowContent.DoEventHandlerPopUp(lastEventPopupPos, currentHandlerName, block, eventHandlerTypes, (int)(EditorGUIUtility.currentViewWidth - lastEventPopupPos.x), 200); } EditorGUILayout.EndHorizontal(); diff --git a/Assets/Fungus/Scripts/Editor/EventSelectorPopupWindowContent.cs b/Assets/Fungus/Scripts/Editor/EventSelectorPopupWindowContent.cs index a09b474c..db7b4345 100644 --- a/Assets/Fungus/Scripts/Editor/EventSelectorPopupWindowContent.cs +++ b/Assets/Fungus/Scripts/Editor/EventSelectorPopupWindowContent.cs @@ -49,14 +49,16 @@ namespace Fungus.EditorUtils private int scrollToIndex; private float scrollOffset; private int currentIndex; + private Vector2 size; static readonly char[] SEARCH_SPLITS = new char[]{ '/', ' ' }; - public EventSelectorPopupWindowContent(string currentHandlerName, Block block, List eventHandlerTypes) + public EventSelectorPopupWindowContent(string currentHandlerName, Block block, List eventHandlerTypes, int width, int height) { this.currentHandlerName = currentHandlerName; this.block = block; this.eventHandlerTypes = eventHandlerTypes; + this.size = new Vector2(width, height); int i = 0; foreach (System.Type type in eventHandlerTypes) @@ -87,6 +89,11 @@ namespace Fungus.EditorUtils DrawSelectionArea(scrollRect); } + public override Vector2 GetWindowSize() + { + return size; + } + private void DrawSearch(Rect rect) { if (Event.current.type == EventType.Repaint) @@ -242,11 +249,11 @@ namespace Fungus.EditorUtils } } - static public void DoEventHandlerPopUp(Rect rect, string currentHandlerName, Block block, List eventHandlerTypes) + static public void DoEventHandlerPopUp(Rect position, string currentHandlerName, Block block, List eventHandlerTypes, int width, int height) { //new method - EventSelectorPopupWindowContent win = new EventSelectorPopupWindowContent(currentHandlerName, block, eventHandlerTypes); - PopupWindow.Show(rect, win); + EventSelectorPopupWindowContent win = new EventSelectorPopupWindowContent(currentHandlerName, block, eventHandlerTypes, width, height); + PopupWindow.Show(position, win); //old method From 785d6ab744111e16aada8b9d107d4d32131d9e71 Mon Sep 17 00:00:00 2001 From: desktop-maesty/steve Date: Wed, 1 Aug 2018 20:20:57 +1000 Subject: [PATCH 04/11] EventSelectorPopup chooses correct starting index based on given current event name WIP Move CommandSelector to its own PopupWindowContext --- Assets/Fungus/Scripts/Editor/BlockEditor.cs | 330 +++--------------- .../CommandSelectorPopupWindowContent.cs | 225 ++++++++++++ .../CommandSelectorPopupWindowContent.cs.meta | 11 + .../Editor/EventSelectorPopupWindowContent.cs | 30 +- 4 files changed, 301 insertions(+), 295 deletions(-) create mode 100644 Assets/Fungus/Scripts/Editor/CommandSelectorPopupWindowContent.cs create mode 100644 Assets/Fungus/Scripts/Editor/CommandSelectorPopupWindowContent.cs.meta diff --git a/Assets/Fungus/Scripts/Editor/BlockEditor.cs b/Assets/Fungus/Scripts/Editor/BlockEditor.cs index d739301d..c4e0544a 100644 --- a/Assets/Fungus/Scripts/Editor/BlockEditor.cs +++ b/Assets/Fungus/Scripts/Editor/BlockEditor.cs @@ -18,16 +18,6 @@ namespace Fungus.EditorUtils [CustomEditor(typeof(Block))] public class BlockEditor : Editor { - protected class AddCommandOperation - { - public Type commandType; - } - - - private static readonly char[] SPLIT_INPUT_ON = new char[] { ' ', '/', '\\' }; - private static readonly int MAX_PREVIEW_GRID = 7; - private static readonly string ELIPSIS = "..."; - public static List actionList = new List(); protected Texture2D upIcon; @@ -35,27 +25,9 @@ namespace Fungus.EditorUtils protected Texture2D addIcon; protected Texture2D duplicateIcon; protected Texture2D deleteIcon; - - protected string commandTextFieldContents = string.Empty; - protected int filteredCommandPreviewSelectedItem = 0; - protected Type commandSelectedByTextInput; - + private Rect lastEventPopupPos; - static List commandTypes; - static List eventHandlerTypes; - - static void CacheEventHandlerTypes() - { - eventHandlerTypes = EditorExtensions.FindDerivedTypes(typeof(EventHandler)).Where(x=>!x.IsAbstract).ToList(); - commandTypes = EditorExtensions.FindDerivedTypes(typeof(Command)).Where(x => !x.IsAbstract).ToList(); - } - - [UnityEditor.Callbacks.DidReloadScripts] - private static void OnScriptsReloaded() - { - CacheEventHandlerTypes(); - } protected virtual void OnEnable() { @@ -64,8 +36,6 @@ namespace Fungus.EditorUtils addIcon = FungusEditorResources.Add; duplicateIcon = FungusEditorResources.Duplicate; deleteIcon = FungusEditorResources.Delete; - - CacheEventHandlerTypes(); } public virtual void DrawBlockName(Flowchart flowchart) @@ -284,32 +254,6 @@ namespace Fungus.EditorUtils GUILayout.BeginHorizontal(); - - //handle movement along our selection grid before something else eats our inputs - if (Event.current.type == EventType.KeyDown) - { - //up down to change selection / esc to clear field - if (Event.current.keyCode == KeyCode.UpArrow) - { - filteredCommandPreviewSelectedItem--; - } - else if (Event.current.keyCode == KeyCode.DownArrow) - { - filteredCommandPreviewSelectedItem++; - } - - if (commandSelectedByTextInput != null && - Event.current.keyCode == KeyCode.Return || Event.current.keyCode == KeyCode.KeypadEnter) - { - AddCommandCallback(commandSelectedByTextInput); - commandSelectedByTextInput = null; - commandTextFieldContents = String.Empty; - //GUI.FocusControl("dummycontrol"); - Event.current.Use(); - filteredCommandPreviewSelectedItem = 0; - } - } - // Previous Command if ((Event.current.type == EventType.KeyDown) && (Event.current.keyCode == KeyCode.PageUp)) { @@ -338,13 +282,11 @@ namespace Fungus.EditorUtils GUILayout.FlexibleSpace(); - //should track if text actually changes and pass that to the ShowPartialMatches so it can cache - commandTextFieldContents = GUILayout.TextField(commandTextFieldContents, GUILayout.MinWidth(20), GUILayout.MaxWidth(200)); - + // Add Button if (GUILayout.Button(addIcon)) { - ShowCommandMenu(); + CommandSelectorPopupWindowContent.ShowCommandMenu(target as Block); } // Duplicate Button @@ -362,85 +304,10 @@ namespace Fungus.EditorUtils GUILayout.EndHorizontal(); - if (!string.IsNullOrEmpty(commandTextFieldContents)) - ShowPartialMatches(); } - //Handles showing partial matches against the text input next to the AddCommand button - // Splits and matches and can use up down arrows and return/enter/numenter to confirm - // TODO add sorting of results so we get best match at the not just just a match - // e.g. "if" should show Flow/If at the top not Flow/Else If - private void ShowPartialMatches() - { - var block = target as Block; - - var flowchart = (Flowchart)block.GetFlowchart(); - - //TODO this could be cached if input hasn't changed to avoid thrashing - var filteredAttributes = GetFilteredSupportedCommands(flowchart); - - var upperCommandText = commandTextFieldContents.ToUpper().Trim(); - - if (upperCommandText.Length == 0) - return; - - var tokens = upperCommandText.Split(SPLIT_INPUT_ON); - - //we want commands that have all the elements you have typed - filteredAttributes = filteredAttributes.Where((x) => - { - bool catAny = tokens.Any(x.Value.Category.ToUpper().Contains); - bool comAny = tokens.Any(x.Value.CommandName.ToUpper().Contains); - bool catAll = tokens.All(x.Value.Category.ToUpper().Contains); - bool comAll = tokens.All(x.Value.CommandName.ToUpper().Contains); - - //so if both category and command found something, then there are multiple tokens and they line up with category and command - if (catAny && comAny) - return true; - //or its a single token or a complex token that matches entirely in cat or com - else if (catAll || comAll) - return true; - //this setup avoids multiple bad suggestions due to a complex category name that gives many false matches on complex partials - - return false; - - }).ToList(); - - if (filteredAttributes == null || filteredAttributes.Count == 0) - return; - - //show results - GUILayout.Space(5); - - GUILayout.BeginHorizontal(); - - filteredCommandPreviewSelectedItem = Mathf.Clamp(filteredCommandPreviewSelectedItem, 0, filteredAttributes.Count - 1); - - var toShow = filteredAttributes.Select(x => x.Value.Category + "/" + x.Value.CommandName).ToArray(); - - //show the first x max that match our filters - if(toShow.Length > MAX_PREVIEW_GRID) - { - toShow = toShow.Take(MAX_PREVIEW_GRID).ToArray(); - toShow[MAX_PREVIEW_GRID - 1] = ELIPSIS; - } - - filteredCommandPreviewSelectedItem = GUILayout.SelectionGrid(filteredCommandPreviewSelectedItem, toShow, 1); - - if (toShow[filteredCommandPreviewSelectedItem] != ELIPSIS) - { - commandSelectedByTextInput = filteredAttributes[filteredCommandPreviewSelectedItem].Key; - } - else - { - commandSelectedByTextInput = null; - } - - GUILayout.EndHorizontal(); - - GUILayout.Space(5); - } + protected virtual void DrawEventHandlerGUI(Flowchart flowchart) { @@ -474,7 +341,7 @@ namespace Fungus.EditorUtils EditorGUILayout.PrefixLabel(new GUIContent("Execute On Event")); if (EditorGUILayout.DropdownButton(new GUIContent(currentHandlerName),FocusType.Passive)) { - EventSelectorPopupWindowContent.DoEventHandlerPopUp(lastEventPopupPos, currentHandlerName, block, eventHandlerTypes, (int)(EditorGUIUtility.currentViewWidth - lastEventPopupPos.x), 200); + EventSelectorPopupWindowContent.DoEventHandlerPopUp(lastEventPopupPos, currentHandlerName, block, (int)(EditorGUIUtility.currentViewWidth - lastEventPopupPos.x), 200); } EditorGUILayout.EndHorizontal(); @@ -566,17 +433,6 @@ namespace Fungus.EditorUtils return result; } - // Compare delegate for sorting the list of command attributes - protected static int CompareCommandAttributes(KeyValuePair x, KeyValuePair y) - { - int compare = (x.Value.Category.CompareTo(y.Value.Category)); - if (compare == 0) - { - compare = (x.Value.CommandName.CompareTo(y.Value.CommandName)); - } - return compare; - } - [MenuItem("Tools/Fungus/Utilities/Export Reference Docs")] protected static void ExportReferenceDocs() { @@ -588,6 +444,49 @@ namespace Fungus.EditorUtils FlowchartWindow.ShowNotification("Exported Reference Documentation"); } + public static List> GetFilteredCommandInfoAttribute(List menuTypes) + { + Dictionary> filteredAttributes = new Dictionary>(); + + 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 keyValuePair = new KeyValuePair(type, infoAttr); + filteredAttributes[dictionaryName] = keyValuePair; + } + } + } + } + return filteredAttributes.Values.ToList>(); + } + + + // Compare delegate for sorting the list of command attributes + public static int CompareCommandAttributes(KeyValuePair x, KeyValuePair y) + { + int compare = (x.Value.Category.CompareTo(y.Value.Category)); + if (compare == 0) + { + compare = (x.Value.CommandName.CompareTo(y.Value.CommandName)); + } + return compare; + } + protected static void ExportCommandInfo(string path) { // Dump command info @@ -713,140 +612,7 @@ namespace Fungus.EditorUtils return markdown; } - protected virtual void ShowCommandMenu() - { - var block = target as Block; - - var flowchart = (Flowchart)block.GetFlowchart(); - - GenericMenu commandMenu = new GenericMenu(); - - // Build menu list - var filteredAttributes = GetFilteredSupportedCommands(flowchart); - - foreach (var keyPair in filteredAttributes) - { - AddCommandOperation commandOperation = new AddCommandOperation(); - - commandOperation.commandType = keyPair.Key; - - 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(); - } - - protected static List> GetFilteredSupportedCommands(Flowchart flowchart) - { - List> filteredAttributes = GetFilteredCommandInfoAttribute(commandTypes); - - filteredAttributes.Sort(CompareCommandAttributes); - - filteredAttributes = filteredAttributes.Where(x => flowchart.IsCommandSupported(x.Value)).ToList(); - - return filteredAttributes; - } - - protected static List> GetFilteredCommandInfoAttribute(List menuTypes) - { - Dictionary> filteredAttributes = new Dictionary>(); - - 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 keyValuePair = new KeyValuePair(type, infoAttr); - filteredAttributes[dictionaryName] = keyValuePair; - } - } - } - } - return filteredAttributes.Values.ToList>(); - } - - //Used by GenericMenu Delegate - protected void AddCommandCallback(object obj) - { - AddCommandOperation commandOperation = obj as AddCommandOperation; - if (commandOperation != null) - { - AddCommandCallback(commandOperation.commandType); - } - } - protected void AddCommandCallback(Type commandType) - { - var block = target as Block; - if (block == null) - { - return; - } - - var flowchart = (Flowchart)block.GetFlowchart(); - - // Use index of last selected command in list, or end of list if nothing selected. - int index = -1; - foreach (var command in flowchart.SelectedCommands) - { - if (command.CommandIndex + 1 > index) - { - index = command.CommandIndex + 1; - } - } - if (index == -1) - { - index = block.CommandList.Count; - } - - var newCommand = Undo.AddComponent(block.gameObject, commandType) as Command; - block.GetFlowchart().AddSelectedCommand(newCommand); - newCommand.ParentBlock = block; - newCommand.ItemId = flowchart.NextItemId(); - - // Let command know it has just been added to the block - newCommand.OnCommandAdded(block); - - Undo.RecordObject(block, "Set command type"); - if (index < block.CommandList.Count - 1) - { - block.CommandList.Insert(index, newCommand); - } - else - { - block.CommandList.Add(newCommand); - } - - // Because this is an async call, we need to force prefab instances to record changes - PrefabUtility.RecordPrefabInstancePropertyModifications(block); - - flowchart.ClearSelectedCommands(); - - commandTextFieldContents = string.Empty; - } public virtual void ShowContextMenu() { diff --git a/Assets/Fungus/Scripts/Editor/CommandSelectorPopupWindowContent.cs b/Assets/Fungus/Scripts/Editor/CommandSelectorPopupWindowContent.cs new file mode 100644 index 00000000..12cb839c --- /dev/null +++ b/Assets/Fungus/Scripts/Editor/CommandSelectorPopupWindowContent.cs @@ -0,0 +1,225 @@ +using UnityEditor; +using UnityEngine; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Fungus.EditorUtils +{ + /// + /// Searchable Popup Window for adding a command to a block + /// + /// Inspired by https://github.com/roboryantron/UnityEditorJunkie/blob/master/Assets/SearchableEnum/Code/Editor/SearchablePopup.cs + /// + public class CommandSelectorPopupWindowContent //: PopupWindowContent + { + private static readonly char[] SPLIT_INPUT_ON = new char[] { ' ', '/', '\\' }; + private static readonly int MAX_PREVIEW_GRID = 7; + private static readonly string ELIPSIS = "..."; + + + static List commandTypes; + + static void CacheEventHandlerTypes() + { + commandTypes = EditorExtensions.FindDerivedTypes(typeof(Command)).Where(x => !x.IsAbstract).ToList(); + } + + [UnityEditor.Callbacks.DidReloadScripts] + private static void OnScriptsReloaded() + { + CacheEventHandlerTypes(); + } + + static Block curBlock; + + static public void ShowCommandMenu(Block block) + { + curBlock = block; + + var flowchart = (Flowchart)block.GetFlowchart(); + + GenericMenu commandMenu = new GenericMenu(); + + // Build menu list + var filteredAttributes = GetFilteredSupportedCommands(flowchart); + + foreach (var keyPair in filteredAttributes) + { + AddCommandOperation commandOperation = new AddCommandOperation(); + + commandOperation.commandType = keyPair.Key; + + 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(); + } + + protected static List> GetFilteredSupportedCommands(Flowchart flowchart) + { + List> filteredAttributes = BlockEditor.GetFilteredCommandInfoAttribute(commandTypes); + + filteredAttributes.Sort(BlockEditor.CompareCommandAttributes); + + filteredAttributes = filteredAttributes.Where(x => flowchart.IsCommandSupported(x.Value)).ToList(); + + return filteredAttributes; + } + + + + //Used by GenericMenu Delegate + static protected void AddCommandCallback(object obj) + { + AddCommandOperation commandOperation = obj as AddCommandOperation; + if (commandOperation != null) + { + AddCommandCallback(commandOperation.commandType); + } + } + + + static protected void AddCommandCallback(Type commandType) + { + var block = curBlock; + if (block == null) + { + return; + } + + var flowchart = (Flowchart)block.GetFlowchart(); + + // Use index of last selected command in list, or end of list if nothing selected. + int index = -1; + foreach (var command in flowchart.SelectedCommands) + { + if (command.CommandIndex + 1 > index) + { + index = command.CommandIndex + 1; + } + } + if (index == -1) + { + index = block.CommandList.Count; + } + + var newCommand = Undo.AddComponent(block.gameObject, commandType) as Command; + block.GetFlowchart().AddSelectedCommand(newCommand); + newCommand.ParentBlock = block; + newCommand.ItemId = flowchart.NextItemId(); + + // Let command know it has just been added to the block + newCommand.OnCommandAdded(block); + + Undo.RecordObject(block, "Set command type"); + if (index < block.CommandList.Count - 1) + { + block.CommandList.Insert(index, newCommand); + } + else + { + block.CommandList.Add(newCommand); + } + + // Because this is an async call, we need to force prefab instances to record changes + PrefabUtility.RecordPrefabInstancePropertyModifications(block); + + flowchart.ClearSelectedCommands(); + + //commandTextFieldContents = string.Empty; + } + + + protected class AddCommandOperation + { + public Type commandType; + } + + //Handles showing partial matches against the text input next to the AddCommand button + // Splits and matches and can use up down arrows and return/enter/numenter to confirm + // TODO add sorting of results so we get best match at the not just just a match + // e.g. "if" should show Flow/If at the top not Flow/Else If + //private void ShowPartialMatches() + //{ + // var block = curBlock; + + // var flowchart = (Flowchart)block.GetFlowchart(); + + // //TODO this could be cached if input hasn't changed to avoid thrashing + // var filteredAttributes = GetFilteredSupportedCommands(flowchart); + + // var upperCommandText = commandTextFieldContents.ToUpper().Trim(); + + // if (upperCommandText.Length == 0) + // return; + + // var tokens = upperCommandText.Split(SPLIT_INPUT_ON); + + // //we want commands that have all the elements you have typed + // filteredAttributes = filteredAttributes.Where((x) => + // { + // bool catAny = tokens.Any(x.Value.Category.ToUpper().Contains); + // bool comAny = tokens.Any(x.Value.CommandName.ToUpper().Contains); + // bool catAll = tokens.All(x.Value.Category.ToUpper().Contains); + // bool comAll = tokens.All(x.Value.CommandName.ToUpper().Contains); + + // //so if both category and command found something, then there are multiple tokens and they line up with category and command + // if (catAny && comAny) + // return true; + // //or its a single token or a complex token that matches entirely in cat or com + // else if (catAll || comAll) + // return true; + // //this setup avoids multiple bad suggestions due to a complex category name that gives many false matches on complex partials + + // return false; + + // }).ToList(); + + // if (filteredAttributes == null || filteredAttributes.Count == 0) + // return; + + // //show results + // GUILayout.Space(5); + + // GUILayout.BeginHorizontal(); + + // filteredCommandPreviewSelectedItem = Mathf.Clamp(filteredCommandPreviewSelectedItem, 0, filteredAttributes.Count - 1); + + // var toShow = filteredAttributes.Select(x => x.Value.Category + "/" + x.Value.CommandName).ToArray(); + + // //show the first x max that match our filters + // if (toShow.Length > MAX_PREVIEW_GRID) + // { + // toShow = toShow.Take(MAX_PREVIEW_GRID).ToArray(); + // toShow[MAX_PREVIEW_GRID - 1] = ELIPSIS; + // } + + // filteredCommandPreviewSelectedItem = GUILayout.SelectionGrid(filteredCommandPreviewSelectedItem, toShow, 1); + + // if (toShow[filteredCommandPreviewSelectedItem] != ELIPSIS) + // { + // commandSelectedByTextInput = filteredAttributes[filteredCommandPreviewSelectedItem].Key; + // } + // else + // { + // commandSelectedByTextInput = null; + // } + + // GUILayout.EndHorizontal(); + + // GUILayout.Space(5); + //} + } + +} \ No newline at end of file diff --git a/Assets/Fungus/Scripts/Editor/CommandSelectorPopupWindowContent.cs.meta b/Assets/Fungus/Scripts/Editor/CommandSelectorPopupWindowContent.cs.meta new file mode 100644 index 00000000..e09c36f2 --- /dev/null +++ b/Assets/Fungus/Scripts/Editor/CommandSelectorPopupWindowContent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0e2650550a8549143b9eaab2c2dad511 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Fungus/Scripts/Editor/EventSelectorPopupWindowContent.cs b/Assets/Fungus/Scripts/Editor/EventSelectorPopupWindowContent.cs index db7b4345..68d722ce 100644 --- a/Assets/Fungus/Scripts/Editor/EventSelectorPopupWindowContent.cs +++ b/Assets/Fungus/Scripts/Editor/EventSelectorPopupWindowContent.cs @@ -1,14 +1,8 @@ using UnityEditor; -using UnityEditorInternal; using UnityEngine; using System; -using System.Collections; using System.Collections.Generic; using System.Linq; -using System.Text.RegularExpressions; -using Rotorz.ReorderableList; -using System.IO; -using System.Reflection; namespace Fungus.EditorUtils { @@ -19,6 +13,19 @@ namespace Fungus.EditorUtils /// public class EventSelectorPopupWindowContent : PopupWindowContent { + static List eventHandlerTypes; + + static void CacheEventHandlerTypes() + { + eventHandlerTypes = EditorExtensions.FindDerivedTypes(typeof(EventHandler)).Where(x => !x.IsAbstract).ToList(); + } + + [UnityEditor.Callbacks.DidReloadScripts] + private static void OnScriptsReloaded() + { + CacheEventHandlerTypes(); + } + protected class SetEventHandlerOperation { public Block block; @@ -37,9 +44,7 @@ namespace Fungus.EditorUtils public string name, lowerName; } - private string currentHandlerName; private Block block; - private List eventHandlerTypes; private int hoverIndex; private readonly string SEARCH_CONTROL_NAME = "PopupSearchControlName"; private readonly float ROW_HEIGHT = EditorGUIUtility.singleLineHeight; @@ -53,11 +58,9 @@ namespace Fungus.EditorUtils static readonly char[] SEARCH_SPLITS = new char[]{ '/', ' ' }; - public EventSelectorPopupWindowContent(string currentHandlerName, Block block, List eventHandlerTypes, int width, int height) + public EventSelectorPopupWindowContent(string currentHandlerName, Block block, int width, int height) { - this.currentHandlerName = currentHandlerName; this.block = block; - this.eventHandlerTypes = eventHandlerTypes; this.size = new Vector2(width, height); int i = 0; @@ -77,6 +80,7 @@ namespace Fungus.EditorUtils } allItems.Sort((lhs, rhs) => lhs.name.CompareTo(rhs.name)); UpdateFilter(); + currentIndex = Mathf.Max(0, visibleItems.FindIndex(x=>x.name.Contains(currentHandlerName))); } public override void OnGUI(Rect rect) @@ -249,10 +253,10 @@ namespace Fungus.EditorUtils } } - static public void DoEventHandlerPopUp(Rect position, string currentHandlerName, Block block, List eventHandlerTypes, int width, int height) + static public void DoEventHandlerPopUp(Rect position, string currentHandlerName, Block block, int width, int height) { //new method - EventSelectorPopupWindowContent win = new EventSelectorPopupWindowContent(currentHandlerName, block, eventHandlerTypes, width, height); + EventSelectorPopupWindowContent win = new EventSelectorPopupWindowContent(currentHandlerName, block, width, height); PopupWindow.Show(position, win); //old method From 073c8937ede7be1254fe40582e1e49c01db6ecae Mon Sep 17 00:00:00 2001 From: desktop-maesty/steve Date: Sat, 4 Aug 2018 08:37:41 +1000 Subject: [PATCH 05/11] Refactor popup window content to a common base CommandMenu has popup selector and fallback to original in double click --- .../Scripts/Editor/BasePopupWindowContent.cs | 241 ++++++++++++++++++ .../Editor/BasePopupWindowContent.cs.meta | 11 + Assets/Fungus/Scripts/Editor/BlockEditor.cs | 14 +- .../CommandSelectorPopupWindowContent.cs | 176 +++++-------- .../Editor/EventSelectorPopupWindowContent.cs | 235 ++--------------- 5 files changed, 348 insertions(+), 329 deletions(-) create mode 100644 Assets/Fungus/Scripts/Editor/BasePopupWindowContent.cs create mode 100644 Assets/Fungus/Scripts/Editor/BasePopupWindowContent.cs.meta diff --git a/Assets/Fungus/Scripts/Editor/BasePopupWindowContent.cs b/Assets/Fungus/Scripts/Editor/BasePopupWindowContent.cs new file mode 100644 index 00000000..3073d7d3 --- /dev/null +++ b/Assets/Fungus/Scripts/Editor/BasePopupWindowContent.cs @@ -0,0 +1,241 @@ +using UnityEditor; +using UnityEngine; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Fungus.EditorUtils +{ + /// + /// Common base for PopupWindowContent that is a search filterable list a la AddComponent + /// + /// Inspired by https://github.com/roboryantron/UnityEditorJunkie/blob/master/Assets/SearchableEnum/Code/Editor/SearchablePopup.cs + /// + public abstract class BasePopupWindowContent : PopupWindowContent + { + /// + /// Called when the user has confirmed an item from the menu. + /// + /// Index of into the original list of items to show given to the popupcontent + abstract protected void SelectByOrigIndex(int index); + + /// + /// Called during Base Ctor, so that all + /// + abstract protected void PrepareAllItems(); + + public class FilteredListItem + { + public FilteredListItem(int index, string str) + { + origIndex = index; + name = str; + lowerName = str.ToLowerInvariant(); + } + public int origIndex; + public string name, lowerName; + } + + protected Block block; + protected int hoverIndex; + protected readonly string SEARCH_CONTROL_NAME = "PopupSearchControlName"; + protected readonly float ROW_HEIGHT = EditorGUIUtility.singleLineHeight; + protected List allItems = new List(), visibleItems = new List(); + protected string currentFilter = string.Empty; + protected Vector2 scroll; + protected int scrollToIndex; + protected float scrollOffset; + protected int currentIndex; + protected Vector2 size; + + static readonly char[] SEARCH_SPLITS = new char[]{ '/', ' ' }; + + public BasePopupWindowContent(string currentHandlerName, Block block, int width, int height) + { + this.block = block; + this.size = new Vector2(width, height); + + PrepareAllItems(); + + allItems.Sort((lhs, rhs) => lhs.name.CompareTo(rhs.name)); + UpdateFilter(); + currentIndex = Mathf.Max(0, visibleItems.FindIndex(x=>x.name.Contains(currentHandlerName))); + } + + public override void OnGUI(Rect rect) + { + Rect searchRect = new Rect(0, 0, rect.width, EditorStyles.toolbar.fixedHeight); + Rect scrollRect = Rect.MinMaxRect(0, searchRect.yMax, rect.xMax, rect.yMax); + + HandleKeyboard(); + DrawSearch(searchRect); + DrawSelectionArea(scrollRect); + } + + public override Vector2 GetWindowSize() + { + return size; + } + + private void DrawSearch(Rect rect) + { + if (Event.current.type == EventType.Repaint) + EditorStyles.toolbar.Draw(rect, false, false, false, false); + + Rect searchRect = new Rect(rect); + searchRect.xMin += 6; + searchRect.xMax -= 6; + searchRect.y += 2; + + GUI.FocusControl(SEARCH_CONTROL_NAME); + GUI.SetNextControlName(SEARCH_CONTROL_NAME); + var prevFilter = currentFilter; + currentFilter = GUI.TextField(searchRect, currentFilter); + + if (prevFilter != currentFilter) + { + UpdateFilter(); + } + } + + private void UpdateFilter() + { + var curlower = currentFilter.ToLowerInvariant(); + var lowers = curlower.Split(SEARCH_SPLITS); + lowers = lowers.Where(x => x.Length > 0).ToArray(); + + if (lowers == null || lowers.Length == 0) + { + visibleItems.AddRange(allItems); + } + else + { + visibleItems = allItems.Where(x => + { + foreach (var item in lowers) + { + if (x.lowerName.Contains(currentFilter)) + return true; + } + return false; + }).ToList(); + } + + hoverIndex = 0; + scroll = Vector2.zero; + visibleItems.Insert(0, new FilteredListItem(-1, "None")); + } + + private void DrawSelectionArea(Rect scrollRect) + { + Rect contentRect = new Rect(0, 0, + scrollRect.width - GUI.skin.verticalScrollbar.fixedWidth, + visibleItems.Count * ROW_HEIGHT); + + scroll = GUI.BeginScrollView(scrollRect, scroll, contentRect); + + Rect rowRect = new Rect(0, 0, scrollRect.width, ROW_HEIGHT); + + for (int i = 0; i < visibleItems.Count; i++) + { + if (scrollToIndex == i && + (Event.current.type == EventType.Repaint + || Event.current.type == EventType.Layout)) + { + Rect r = new Rect(rowRect); + r.y += scrollOffset; + GUI.ScrollTo(r); + scrollToIndex = -1; + scroll.x = 0; + } + + if (rowRect.Contains(Event.current.mousePosition)) + { + if (Event.current.type == EventType.MouseMove || + Event.current.type == EventType.ScrollWheel) + hoverIndex = i; + if (Event.current.type == EventType.MouseDown) + { + //onSelectionMade(list.Entries[i].Index); + SelectByOrigIndex(visibleItems[i].origIndex); + EditorWindow.focusedWindow.Close(); + } + } + + DrawRow(rowRect, i); + + rowRect.y = rowRect.yMax; + } + + GUI.EndScrollView(); + } + + private static void DrawBox(Rect rect, Color tint) + { + Color c = GUI.color; + GUI.color = tint; + GUI.Box(rect, ""); + GUI.color = c; + } + + private void DrawRow(Rect rowRect, int i) + { + if (i == currentIndex) + DrawBox(rowRect, Color.cyan); + else if (i == hoverIndex) + DrawBox(rowRect, Color.white); + + Rect labelRect = new Rect(rowRect); + //labelRect.xMin += ROW_INDENT; + + GUI.Label(labelRect, visibleItems[i].name); + } + + /// + /// Process keyboard input to navigate the choices or make a selection. + /// + private void HandleKeyboard() + { + if (Event.current.type == EventType.KeyDown) + { + if (Event.current.keyCode == KeyCode.DownArrow) + { + hoverIndex = Mathf.Min(visibleItems.Count - 1, hoverIndex + 1); + Event.current.Use(); + scrollToIndex = hoverIndex; + scrollOffset = ROW_HEIGHT; + } + + if (Event.current.keyCode == KeyCode.UpArrow) + { + hoverIndex = Mathf.Max(0, hoverIndex - 1); + Event.current.Use(); + scrollToIndex = hoverIndex; + scrollOffset = -ROW_HEIGHT; + } + + if (Event.current.keyCode == KeyCode.Return) + { + if (hoverIndex >= 0 && hoverIndex < visibleItems.Count) + { + SelectByOrigIndex(visibleItems[hoverIndex].origIndex); + EditorWindow.focusedWindow.Close(); + } + } + + if (Event.current.keyCode == KeyCode.Escape) + { + EditorWindow.focusedWindow.Close(); + } + } + } + + + protected void SelectByName(string name) + { + var loc = allItems.FindIndex(x => x.name == name); + SelectByOrigIndex(loc); + } + + } +} \ No newline at end of file diff --git a/Assets/Fungus/Scripts/Editor/BasePopupWindowContent.cs.meta b/Assets/Fungus/Scripts/Editor/BasePopupWindowContent.cs.meta new file mode 100644 index 00000000..8d7d432b --- /dev/null +++ b/Assets/Fungus/Scripts/Editor/BasePopupWindowContent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: be9eead8f19216444b4a87e94fe02ed3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Fungus/Scripts/Editor/BlockEditor.cs b/Assets/Fungus/Scripts/Editor/BlockEditor.cs index c4e0544a..040dc75b 100644 --- a/Assets/Fungus/Scripts/Editor/BlockEditor.cs +++ b/Assets/Fungus/Scripts/Editor/BlockEditor.cs @@ -26,7 +26,7 @@ namespace Fungus.EditorUtils protected Texture2D duplicateIcon; protected Texture2D deleteIcon; - private Rect lastEventPopupPos; + private Rect lastEventPopupPos, lastCMDpopupPos; protected virtual void OnEnable() @@ -282,11 +282,19 @@ namespace Fungus.EditorUtils GUILayout.FlexibleSpace(); - + var pos = EditorGUILayout.GetControlRect(true, 0, EditorStyles.objectField); + if (pos.x != 0) + { + lastCMDpopupPos = pos; + lastCMDpopupPos.x += EditorGUIUtility.labelWidth; + lastCMDpopupPos.y += EditorGUIUtility.singleLineHeight; + } // Add Button if (GUILayout.Button(addIcon)) { - CommandSelectorPopupWindowContent.ShowCommandMenu(target as Block); + CommandSelectorPopupWindowContent.ShowCommandMenu(lastCMDpopupPos, "", target as Block, + (int)(EditorGUIUtility.currentViewWidth), + (int)(EditorWindow.focusedWindow.position.height - lastCMDpopupPos.y - EditorGUIUtility.singleLineHeight*3)); } // Duplicate Button diff --git a/Assets/Fungus/Scripts/Editor/CommandSelectorPopupWindowContent.cs b/Assets/Fungus/Scripts/Editor/CommandSelectorPopupWindowContent.cs index 12cb839c..00f34d3f 100644 --- a/Assets/Fungus/Scripts/Editor/CommandSelectorPopupWindowContent.cs +++ b/Assets/Fungus/Scripts/Editor/CommandSelectorPopupWindowContent.cs @@ -11,7 +11,7 @@ namespace Fungus.EditorUtils /// /// Inspired by https://github.com/roboryantron/UnityEditorJunkie/blob/master/Assets/SearchableEnum/Code/Editor/SearchablePopup.cs /// - public class CommandSelectorPopupWindowContent //: PopupWindowContent + public class CommandSelectorPopupWindowContent : BasePopupWindowContent { private static readonly char[] SPLIT_INPUT_ON = new char[] { ' ', '/', '\\' }; private static readonly int MAX_PREVIEW_GRID = 7; @@ -20,7 +20,7 @@ namespace Fungus.EditorUtils static List commandTypes; - static void CacheEventHandlerTypes() + static void CacheCommandTypes() { commandTypes = EditorExtensions.FindDerivedTypes(typeof(Command)).Where(x => !x.IsAbstract).ToList(); } @@ -28,42 +28,52 @@ namespace Fungus.EditorUtils [UnityEditor.Callbacks.DidReloadScripts] private static void OnScriptsReloaded() { - CacheEventHandlerTypes(); + CacheCommandTypes(); } static Block curBlock; + static protected List> filteredAttributes; - static public void ShowCommandMenu(Block block) + public CommandSelectorPopupWindowContent(string currentHandlerName, Block block, int width, int height) + : base(currentHandlerName, block, width, height) { - curBlock = block; + } - var flowchart = (Flowchart)block.GetFlowchart(); + protected override void SelectByOrigIndex(int index) + { + var commandType = (index >= 0 && index < commandTypes.Count) ? commandTypes[index] : null; + AddCommandCallback(commandType); + } - GenericMenu commandMenu = new GenericMenu(); + protected override void PrepareAllItems() + { + if (commandTypes == null || commandTypes.Count == 0) + { + CacheCommandTypes(); + } - // Build menu list - var filteredAttributes = GetFilteredSupportedCommands(flowchart); - foreach (var keyPair in filteredAttributes) + filteredAttributes = GetFilteredSupportedCommands(block.GetFlowchart()); + + int i = 0; + foreach (var item in filteredAttributes) { - AddCommandOperation commandOperation = new AddCommandOperation(); + allItems.Add(new FilteredListItem(i, (item.Value.Category.Length > 0 ? item.Value.Category + "/" : "") + item.Value.CommandName)); - commandOperation.commandType = keyPair.Key; + i++; + } + } - GUIContent menuItem; - if (keyPair.Value.Category == "") - { - menuItem = new GUIContent(keyPair.Value.CommandName); - } - else - { - menuItem = new GUIContent(keyPair.Value.Category + "/" + keyPair.Value.CommandName); - } + static public void ShowCommandMenu(Rect position, string currentHandlerName, Block block, int width, int height) + { + curBlock = block; - commandMenu.AddItem(menuItem, false, AddCommandCallback, commandOperation); - } + var win = new CommandSelectorPopupWindowContent(currentHandlerName, block, width, height); + PopupWindow.Show(position, win); - commandMenu.ShowAsContext(); + + //old method + DoOlderMenu(); } protected static List> GetFilteredSupportedCommands(Flowchart flowchart) @@ -79,13 +89,40 @@ namespace Fungus.EditorUtils + + static protected void DoOlderMenu() + { + var flowchart = (Flowchart)curBlock.GetFlowchart(); + + GenericMenu commandMenu = new GenericMenu(); + + // Build menu list + + foreach (var keyPair in filteredAttributes) + { + 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, keyPair.Key); + } + + commandMenu.ShowAsContext(); + } + //Used by GenericMenu Delegate static protected void AddCommandCallback(object obj) { - AddCommandOperation commandOperation = obj as AddCommandOperation; - if (commandOperation != null) + Type command = obj as Type; + if (command != null) { - AddCommandCallback(commandOperation.commandType); + AddCommandCallback(command); } } @@ -93,7 +130,7 @@ namespace Fungus.EditorUtils static protected void AddCommandCallback(Type commandType) { var block = curBlock; - if (block == null) + if (block == null || commandType == null) { return; } @@ -137,89 +174,6 @@ namespace Fungus.EditorUtils flowchart.ClearSelectedCommands(); - //commandTextFieldContents = string.Empty; } - - - protected class AddCommandOperation - { - public Type commandType; - } - - //Handles showing partial matches against the text input next to the AddCommand button - // Splits and matches and can use up down arrows and return/enter/numenter to confirm - // TODO add sorting of results so we get best match at the not just just a match - // e.g. "if" should show Flow/If at the top not Flow/Else If - //private void ShowPartialMatches() - //{ - // var block = curBlock; - - // var flowchart = (Flowchart)block.GetFlowchart(); - - // //TODO this could be cached if input hasn't changed to avoid thrashing - // var filteredAttributes = GetFilteredSupportedCommands(flowchart); - - // var upperCommandText = commandTextFieldContents.ToUpper().Trim(); - - // if (upperCommandText.Length == 0) - // return; - - // var tokens = upperCommandText.Split(SPLIT_INPUT_ON); - - // //we want commands that have all the elements you have typed - // filteredAttributes = filteredAttributes.Where((x) => - // { - // bool catAny = tokens.Any(x.Value.Category.ToUpper().Contains); - // bool comAny = tokens.Any(x.Value.CommandName.ToUpper().Contains); - // bool catAll = tokens.All(x.Value.Category.ToUpper().Contains); - // bool comAll = tokens.All(x.Value.CommandName.ToUpper().Contains); - - // //so if both category and command found something, then there are multiple tokens and they line up with category and command - // if (catAny && comAny) - // return true; - // //or its a single token or a complex token that matches entirely in cat or com - // else if (catAll || comAll) - // return true; - // //this setup avoids multiple bad suggestions due to a complex category name that gives many false matches on complex partials - - // return false; - - // }).ToList(); - - // if (filteredAttributes == null || filteredAttributes.Count == 0) - // return; - - // //show results - // GUILayout.Space(5); - - // GUILayout.BeginHorizontal(); - - // filteredCommandPreviewSelectedItem = Mathf.Clamp(filteredCommandPreviewSelectedItem, 0, filteredAttributes.Count - 1); - - // var toShow = filteredAttributes.Select(x => x.Value.Category + "/" + x.Value.CommandName).ToArray(); - - // //show the first x max that match our filters - // if (toShow.Length > MAX_PREVIEW_GRID) - // { - // toShow = toShow.Take(MAX_PREVIEW_GRID).ToArray(); - // toShow[MAX_PREVIEW_GRID - 1] = ELIPSIS; - // } - - // filteredCommandPreviewSelectedItem = GUILayout.SelectionGrid(filteredCommandPreviewSelectedItem, toShow, 1); - - // if (toShow[filteredCommandPreviewSelectedItem] != ELIPSIS) - // { - // commandSelectedByTextInput = filteredAttributes[filteredCommandPreviewSelectedItem].Key; - // } - // else - // { - // commandSelectedByTextInput = null; - // } - - // GUILayout.EndHorizontal(); - - // GUILayout.Space(5); - //} } - } \ No newline at end of file diff --git a/Assets/Fungus/Scripts/Editor/EventSelectorPopupWindowContent.cs b/Assets/Fungus/Scripts/Editor/EventSelectorPopupWindowContent.cs index 68d722ce..2956ddef 100644 --- a/Assets/Fungus/Scripts/Editor/EventSelectorPopupWindowContent.cs +++ b/Assets/Fungus/Scripts/Editor/EventSelectorPopupWindowContent.cs @@ -11,7 +11,7 @@ namespace Fungus.EditorUtils /// /// Inspired by https://github.com/roboryantron/UnityEditorJunkie/blob/master/Assets/SearchableEnum/Code/Editor/SearchablePopup.cs /// - public class EventSelectorPopupWindowContent : PopupWindowContent + public class EventSelectorPopupWindowContent : BasePopupWindowContent { static List eventHandlerTypes; @@ -32,36 +32,18 @@ namespace Fungus.EditorUtils public Type eventHandlerType; } - public class FilteredListItem + + public EventSelectorPopupWindowContent(string currentHandlerName, Block block, int width, int height) + :base(currentHandlerName, block, width, height) { - public FilteredListItem(int index, string str) - { - origIndex = index; - name = str; - lowerName = str.ToLowerInvariant(); - } - public int origIndex; - public string name, lowerName; } - private Block block; - private int hoverIndex; - private readonly string SEARCH_CONTROL_NAME = "PopupSearchControlName"; - private readonly float ROW_HEIGHT = EditorGUIUtility.singleLineHeight; - private List allItems = new List(), visibleItems = new List(); - private string currentFilter = string.Empty; - private Vector2 scroll; - private int scrollToIndex; - private float scrollOffset; - private int currentIndex; - private Vector2 size; - - static readonly char[] SEARCH_SPLITS = new char[]{ '/', ' ' }; - - public EventSelectorPopupWindowContent(string currentHandlerName, Block block, int width, int height) + protected override void PrepareAllItems() { - this.block = block; - this.size = new Vector2(width, height); + if (eventHandlerTypes == null || eventHandlerTypes.Count == 0) + { + CacheEventHandlerTypes(); + } int i = 0; foreach (System.Type type in eventHandlerTypes) @@ -78,180 +60,16 @@ namespace Fungus.EditorUtils i++; } - allItems.Sort((lhs, rhs) => lhs.name.CompareTo(rhs.name)); - UpdateFilter(); - currentIndex = Mathf.Max(0, visibleItems.FindIndex(x=>x.name.Contains(currentHandlerName))); } - - public override void OnGUI(Rect rect) + + override protected void SelectByOrigIndex(int index) { - Rect searchRect = new Rect(0, 0, rect.width, EditorStyles.toolbar.fixedHeight); - Rect scrollRect = Rect.MinMaxRect(0, searchRect.yMax, rect.xMax, rect.yMax); - - HandleKeyboard(); - DrawSearch(searchRect); - DrawSelectionArea(scrollRect); - } - - public override Vector2 GetWindowSize() - { - return size; - } - - private void DrawSearch(Rect rect) - { - if (Event.current.type == EventType.Repaint) - EditorStyles.toolbar.Draw(rect, false, false, false, false); - - Rect searchRect = new Rect(rect); - searchRect.xMin += 6; - searchRect.xMax -= 6; - searchRect.y += 2; - //searchRect.width -= CancelButton.fixedWidth; - - GUI.FocusControl(SEARCH_CONTROL_NAME); - GUI.SetNextControlName(SEARCH_CONTROL_NAME); - var prevFilter = currentFilter; - currentFilter = GUI.TextField(searchRect, currentFilter); - - if (prevFilter != currentFilter) - { - UpdateFilter(); - } - } - - private void UpdateFilter() - { - var curlower = currentFilter.ToLowerInvariant(); - var lowers = curlower.Split(SEARCH_SPLITS); - lowers = lowers.Where(x => x.Length > 0).ToArray(); - - if (lowers == null || lowers.Length == 0) - { - visibleItems.AddRange(allItems); - } - else - { - visibleItems = allItems.Where(x => - { - foreach (var item in lowers) - { - if (x.lowerName.Contains(currentFilter)) - return true; - } - return false; - }).ToList(); - } - - hoverIndex = 0; - scroll = Vector2.zero; - visibleItems.Insert(0, new FilteredListItem(-1, "None")); - } - - private void DrawSelectionArea(Rect scrollRect) - { - Rect contentRect = new Rect(0, 0, - scrollRect.width - GUI.skin.verticalScrollbar.fixedWidth, - visibleItems.Count * ROW_HEIGHT); - - scroll = GUI.BeginScrollView(scrollRect, scroll, contentRect); - - Rect rowRect = new Rect(0, 0, scrollRect.width, ROW_HEIGHT); - - for (int i = 0; i < visibleItems.Count; i++) - { - if (scrollToIndex == i && - (Event.current.type == EventType.Repaint - || Event.current.type == EventType.Layout)) - { - Rect r = new Rect(rowRect); - r.y += scrollOffset; - GUI.ScrollTo(r); - scrollToIndex = -1; - scroll.x = 0; - } - - if (rowRect.Contains(Event.current.mousePosition)) - { - if (Event.current.type == EventType.MouseMove || - Event.current.type == EventType.ScrollWheel) - hoverIndex = i; - if (Event.current.type == EventType.MouseDown) - { - //onSelectionMade(list.Entries[i].Index); - SelectByOrigIndex(visibleItems[i].origIndex); - EditorWindow.focusedWindow.Close(); - } - } - - DrawRow(rowRect, i); - - rowRect.y = rowRect.yMax; - } - - GUI.EndScrollView(); - } - - private static void DrawBox(Rect rect, Color tint) - { - Color c = GUI.color; - GUI.color = tint; - GUI.Box(rect, ""); - GUI.color = c; - } - - private void DrawRow(Rect rowRect, int i) - { - if (i == currentIndex) - DrawBox(rowRect, Color.cyan); - else if (i == hoverIndex) - DrawBox(rowRect, Color.white); - - Rect labelRect = new Rect(rowRect); - //labelRect.xMin += ROW_INDENT; - - GUI.Label(labelRect, visibleItems[i].name); + SetEventHandlerOperation operation = new SetEventHandlerOperation(); + operation.block = block; + operation.eventHandlerType = (index >= 0 && index < eventHandlerTypes.Count) ? eventHandlerTypes[index] : null; + OnSelectEventHandler(operation); } - /// - /// Process keyboard input to navigate the choices or make a selection. - /// - private void HandleKeyboard() - { - if (Event.current.type == EventType.KeyDown) - { - if (Event.current.keyCode == KeyCode.DownArrow) - { - hoverIndex = Mathf.Min(visibleItems.Count - 1, hoverIndex + 1); - Event.current.Use(); - scrollToIndex = hoverIndex; - scrollOffset = ROW_HEIGHT; - } - - if (Event.current.keyCode == KeyCode.UpArrow) - { - hoverIndex = Mathf.Max(0, hoverIndex - 1); - Event.current.Use(); - scrollToIndex = hoverIndex; - scrollOffset = -ROW_HEIGHT; - } - - if (Event.current.keyCode == KeyCode.Return) - { - if (hoverIndex >= 0 && hoverIndex < visibleItems.Count) - { - SelectByOrigIndex(visibleItems[hoverIndex].origIndex); - //onSelectionMade(list.Entries[hoverIndex].Index); - EditorWindow.focusedWindow.Close(); - } - } - - if (Event.current.keyCode == KeyCode.Escape) - { - EditorWindow.focusedWindow.Close(); - } - } - } static public void DoEventHandlerPopUp(Rect position, string currentHandlerName, Block block, int width, int height) { @@ -260,6 +78,11 @@ namespace Fungus.EditorUtils PopupWindow.Show(position, win); //old method + DoOlderMenu(block); + } + + static protected void DoOlderMenu(Block block) + { SetEventHandlerOperation noneOperation = new SetEventHandlerOperation(); noneOperation.block = block; @@ -301,7 +124,6 @@ namespace Fungus.EditorUtils eventHandlerMenu.ShowAsContext(); } - static protected void OnSelectEventHandler(object obj) { SetEventHandlerOperation operation = obj as SetEventHandlerOperation; @@ -329,22 +151,5 @@ namespace Fungus.EditorUtils // Because this is an async call, we need to force prefab instances to record changes PrefabUtility.RecordPrefabInstancePropertyModifications(block); } - - protected void SelectByName(string name) - { - var loc = allItems.First(x => x.name == name); - SetEventHandlerOperation operation = new SetEventHandlerOperation(); - operation.block = block; - operation.eventHandlerType = eventHandlerTypes[loc.origIndex]; - OnSelectEventHandler(operation); - } - - protected void SelectByOrigIndex(int index) - { - SetEventHandlerOperation operation = new SetEventHandlerOperation(); - operation.block = block; - operation.eventHandlerType = (index >= 0 && index Date: Sun, 5 Aug 2018 19:25:29 +1000 Subject: [PATCH 06/11] PopupSelectors now show tooltips from Event and Command HelpText --- .../Scripts/Editor/BasePopupWindowContent.cs | 37 ++++++++++++++----- .../CommandSelectorPopupWindowContent.cs | 2 +- .../Editor/EventSelectorPopupWindowContent.cs | 4 +- 3 files changed, 30 insertions(+), 13 deletions(-) diff --git a/Assets/Fungus/Scripts/Editor/BasePopupWindowContent.cs b/Assets/Fungus/Scripts/Editor/BasePopupWindowContent.cs index 3073d7d3..786c95ec 100644 --- a/Assets/Fungus/Scripts/Editor/BasePopupWindowContent.cs +++ b/Assets/Fungus/Scripts/Editor/BasePopupWindowContent.cs @@ -20,27 +20,34 @@ namespace Fungus.EditorUtils abstract protected void SelectByOrigIndex(int index); /// - /// Called during Base Ctor, so that all + /// Called during Base Ctor, must fill allItems list so the ctor can continue to fill + /// the visible items and current selected index. /// abstract protected void PrepareAllItems(); + /// + /// Internal representation of 1 row of our popup list + /// public class FilteredListItem { - public FilteredListItem(int index, string str) + public FilteredListItem(int index, string str, string tip = "") { origIndex = index; name = str; lowerName = str.ToLowerInvariant(); + content = new GUIContent(str, tip); } public int origIndex; public string name, lowerName; + public GUIContent content; } protected Block block; protected int hoverIndex; protected readonly string SEARCH_CONTROL_NAME = "PopupSearchControlName"; protected readonly float ROW_HEIGHT = EditorGUIUtility.singleLineHeight; - protected List allItems = new List(), visibleItems = new List(); + protected List allItems = new List(), + visibleItems = new List(); protected string currentFilter = string.Empty; protected Vector2 scroll; protected int scrollToIndex; @@ -57,9 +64,10 @@ namespace Fungus.EditorUtils PrepareAllItems(); - allItems.Sort((lhs, rhs) => lhs.name.CompareTo(rhs.name)); + allItems.Sort((lhs, rhs) => lhs.lowerName.CompareTo(rhs.lowerName)); UpdateFilter(); currentIndex = Mathf.Max(0, visibleItems.FindIndex(x=>x.name.Contains(currentHandlerName))); + hoverIndex = currentIndex; } public override void OnGUI(Rect rect) @@ -153,7 +161,16 @@ namespace Fungus.EditorUtils { if (Event.current.type == EventType.MouseMove || Event.current.type == EventType.ScrollWheel) + { + //if new item force update so it's snappier + if (hoverIndex != 1) + { + this.editorWindow.Repaint(); + } + hoverIndex = i; + } + if (Event.current.type == EventType.MouseDown) { //onSelectionMade(list.Entries[i].Index); @@ -188,7 +205,7 @@ namespace Fungus.EditorUtils Rect labelRect = new Rect(rowRect); //labelRect.xMin += ROW_INDENT; - GUI.Label(labelRect, visibleItems[i].name); + GUI.Label(labelRect, visibleItems[i].content); } /// @@ -231,11 +248,11 @@ namespace Fungus.EditorUtils } - protected void SelectByName(string name) - { - var loc = allItems.FindIndex(x => x.name == name); - SelectByOrigIndex(loc); - } + //protected void SelectByName(string name) + //{ + // var loc = allItems.FindIndex(x => x.name == name); + // SelectByOrigIndex(loc); + //} } } \ No newline at end of file diff --git a/Assets/Fungus/Scripts/Editor/CommandSelectorPopupWindowContent.cs b/Assets/Fungus/Scripts/Editor/CommandSelectorPopupWindowContent.cs index 00f34d3f..4f2b41a2 100644 --- a/Assets/Fungus/Scripts/Editor/CommandSelectorPopupWindowContent.cs +++ b/Assets/Fungus/Scripts/Editor/CommandSelectorPopupWindowContent.cs @@ -58,7 +58,7 @@ namespace Fungus.EditorUtils int i = 0; foreach (var item in filteredAttributes) { - allItems.Add(new FilteredListItem(i, (item.Value.Category.Length > 0 ? item.Value.Category + "/" : "") + item.Value.CommandName)); + allItems.Add(new FilteredListItem(i, (item.Value.Category.Length > 0 ? item.Value.Category + "/" : "") + item.Value.CommandName, item.Value.HelpText)); i++; } diff --git a/Assets/Fungus/Scripts/Editor/EventSelectorPopupWindowContent.cs b/Assets/Fungus/Scripts/Editor/EventSelectorPopupWindowContent.cs index 2956ddef..77d5faf1 100644 --- a/Assets/Fungus/Scripts/Editor/EventSelectorPopupWindowContent.cs +++ b/Assets/Fungus/Scripts/Editor/EventSelectorPopupWindowContent.cs @@ -51,11 +51,11 @@ namespace Fungus.EditorUtils EventHandlerInfoAttribute info = EventHandlerEditor.GetEventHandlerInfo(type); if (info != null) { - allItems.Add(new FilteredListItem(i, (info.Category.Length > 0 ? info.Category + "/" : "") + info.EventHandlerName)); + allItems.Add(new FilteredListItem(i, (info.Category.Length > 0 ? info.Category + "/" : "") + info.EventHandlerName, info.HelpText)); } else { - allItems.Add(new FilteredListItem(i, type.Name)); + allItems.Add(new FilteredListItem(i, type.Name, info.HelpText)); } i++; From 9e5e76ad57aa8720fabfc60265876410c146d931 Mon Sep 17 00:00:00 2001 From: desktop-maesty/steve Date: Mon, 6 Aug 2018 19:45:16 +1000 Subject: [PATCH 07/11] CommandPopup position and hieight adjusted --- Assets/Fungus/Scripts/Editor/BlockEditor.cs | 4 ++-- .../Scripts/Editor/CommandSelectorPopupWindowContent.cs | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Assets/Fungus/Scripts/Editor/BlockEditor.cs b/Assets/Fungus/Scripts/Editor/BlockEditor.cs index 040dc75b..939c51d4 100644 --- a/Assets/Fungus/Scripts/Editor/BlockEditor.cs +++ b/Assets/Fungus/Scripts/Editor/BlockEditor.cs @@ -287,14 +287,14 @@ namespace Fungus.EditorUtils { lastCMDpopupPos = pos; lastCMDpopupPos.x += EditorGUIUtility.labelWidth; - lastCMDpopupPos.y += EditorGUIUtility.singleLineHeight; + lastCMDpopupPos.y += EditorGUIUtility.singleLineHeight*2; } // Add Button if (GUILayout.Button(addIcon)) { CommandSelectorPopupWindowContent.ShowCommandMenu(lastCMDpopupPos, "", target as Block, (int)(EditorGUIUtility.currentViewWidth), - (int)(EditorWindow.focusedWindow.position.height - lastCMDpopupPos.y - EditorGUIUtility.singleLineHeight*3)); + (int)(EditorWindow.focusedWindow.position.height - lastCMDpopupPos.y)); } // Duplicate Button diff --git a/Assets/Fungus/Scripts/Editor/CommandSelectorPopupWindowContent.cs b/Assets/Fungus/Scripts/Editor/CommandSelectorPopupWindowContent.cs index 4f2b41a2..62449ebe 100644 --- a/Assets/Fungus/Scripts/Editor/CommandSelectorPopupWindowContent.cs +++ b/Assets/Fungus/Scripts/Editor/CommandSelectorPopupWindowContent.cs @@ -68,7 +68,8 @@ namespace Fungus.EditorUtils { curBlock = block; - var win = new CommandSelectorPopupWindowContent(currentHandlerName, block, width, height); + var win = new CommandSelectorPopupWindowContent(currentHandlerName, block, + width, (int)(height - EditorGUIUtility.singleLineHeight * 3)); PopupWindow.Show(position, win); From 507eb490d091d4fa6674609aada5b0f657e8c845 Mon Sep 17 00:00:00 2001 From: desktop-maesty/steve Date: Tue, 7 Aug 2018 15:07:50 +1000 Subject: [PATCH 08/11] Add Variable moved from FlowchartEditor to popup --- .../Scripts/Editor/BasePopupWindowContent.cs | 33 ++--- .../Fungus/Scripts/Editor/FlowchartEditor.cs | 84 +----------- .../Fungus/Scripts/Editor/PopupContent.meta | 8 ++ .../CommandSelectorPopupWindowContent.cs | 23 +--- .../CommandSelectorPopupWindowContent.cs.meta | 0 .../EventSelectorPopupWindowContent.cs | 9 +- .../EventSelectorPopupWindowContent.cs.meta | 0 .../VariableSelectPopupWindowContent.cs | 125 ++++++++++++++++++ .../VariableSelectPopupWindowContent.cs.meta | 11 ++ 9 files changed, 173 insertions(+), 120 deletions(-) create mode 100644 Assets/Fungus/Scripts/Editor/PopupContent.meta rename Assets/Fungus/Scripts/Editor/{ => PopupContent}/CommandSelectorPopupWindowContent.cs (86%) rename Assets/Fungus/Scripts/Editor/{ => PopupContent}/CommandSelectorPopupWindowContent.cs.meta (100%) rename Assets/Fungus/Scripts/Editor/{ => PopupContent}/EventSelectorPopupWindowContent.cs (94%) rename Assets/Fungus/Scripts/Editor/{ => PopupContent}/EventSelectorPopupWindowContent.cs.meta (100%) create mode 100644 Assets/Fungus/Scripts/Editor/PopupContent/VariableSelectPopupWindowContent.cs create mode 100644 Assets/Fungus/Scripts/Editor/PopupContent/VariableSelectPopupWindowContent.cs.meta diff --git a/Assets/Fungus/Scripts/Editor/BasePopupWindowContent.cs b/Assets/Fungus/Scripts/Editor/BasePopupWindowContent.cs index 786c95ec..3c7339de 100644 --- a/Assets/Fungus/Scripts/Editor/BasePopupWindowContent.cs +++ b/Assets/Fungus/Scripts/Editor/BasePopupWindowContent.cs @@ -42,7 +42,6 @@ namespace Fungus.EditorUtils public GUIContent content; } - protected Block block; protected int hoverIndex; protected readonly string SEARCH_CONTROL_NAME = "PopupSearchControlName"; protected readonly float ROW_HEIGHT = EditorGUIUtility.singleLineHeight; @@ -55,16 +54,25 @@ namespace Fungus.EditorUtils protected int currentIndex; protected Vector2 size; - static readonly char[] SEARCH_SPLITS = new char[]{ '/', ' ' }; + static readonly char[] SEARCH_SPLITS = new char[]{ CATEGORY_CHAR, ' ' }; + protected static readonly char CATEGORY_CHAR = '/'; - public BasePopupWindowContent(string currentHandlerName, Block block, int width, int height) + public BasePopupWindowContent(string currentHandlerName, int width, int height) { - this.block = block; this.size = new Vector2(width, height); PrepareAllItems(); - allItems.Sort((lhs, rhs) => lhs.lowerName.CompareTo(rhs.lowerName)); + allItems.Sort((lhs, rhs) => + { + //order root level objects first + var islhsRoot = lhs.lowerName.IndexOf(CATEGORY_CHAR) != -1; + var isrhsRoot = rhs.lowerName.IndexOf(CATEGORY_CHAR) != -1; + + if(islhsRoot == isrhsRoot) + return lhs.lowerName.CompareTo(rhs.lowerName); + return islhsRoot ? 1 : -1; + }); UpdateFilter(); currentIndex = Mathf.Max(0, visibleItems.FindIndex(x=>x.name.Contains(currentHandlerName))); hoverIndex = currentIndex; @@ -120,12 +128,13 @@ namespace Fungus.EditorUtils { visibleItems = allItems.Where(x => { + //we want all tokens foreach (var item in lowers) { - if (x.lowerName.Contains(currentFilter)) - return true; + if (!x.lowerName.Contains(item)) + return false; } - return false; + return true; }).ToList(); } @@ -246,13 +255,5 @@ namespace Fungus.EditorUtils } } } - - - //protected void SelectByName(string name) - //{ - // var loc = allItems.FindIndex(x => x.name == name); - // SelectByOrigIndex(loc); - //} - } } \ No newline at end of file diff --git a/Assets/Fungus/Scripts/Editor/FlowchartEditor.cs b/Assets/Fungus/Scripts/Editor/FlowchartEditor.cs index 52d79d61..337d8c24 100644 --- a/Assets/Fungus/Scripts/Editor/FlowchartEditor.cs +++ b/Assets/Fungus/Scripts/Editor/FlowchartEditor.cs @@ -13,11 +13,6 @@ namespace Fungus.EditorUtils [CustomEditor (typeof(Flowchart))] public class FlowchartEditor : Editor { - protected class AddVariableInfo - { - public Flowchart flowchart; - public System.Type variableType; - } protected SerializedProperty descriptionProp; protected SerializedProperty colorCommandsProp; @@ -179,90 +174,15 @@ namespace Fungus.EditorUtils if (!Application.isPlaying && GUI.Button(plusRect, addTexture)) { - GenericMenu menu = new GenericMenu (); - List types = FindAllDerivedTypes(); - - // Add variable types without a category - foreach (var type in types) - { - VariableInfoAttribute variableInfo = VariableEditor.GetVariableInfo(type); - if (variableInfo == null || - variableInfo.Category != "") - { - continue; - } - - AddVariableInfo addVariableInfo = new AddVariableInfo(); - addVariableInfo.flowchart = t; - addVariableInfo.variableType = type; - - GUIContent typeName = new GUIContent(variableInfo.VariableType); - - menu.AddItem(typeName, false, AddVariable, addVariableInfo); - } - - // Add types with a category - foreach (var type in types) - { - VariableInfoAttribute variableInfo = VariableEditor.GetVariableInfo(type); - if (variableInfo == null || - variableInfo.Category == "") - { - continue; - } - - AddVariableInfo info = new AddVariableInfo(); - info.flowchart = t; - info.variableType = type; - - GUIContent typeName = new GUIContent(variableInfo.Category + "/" + variableInfo.VariableType); - - menu.AddItem(typeName, false, AddVariable, info); - } - - menu.ShowAsContext (); + Rect popRect = plusRect; + VariableSelectPopupWindowContent.DoAddVariable(popRect, "", t); } } serializedObject.ApplyModifiedProperties(); } - protected virtual void AddVariable(object obj) - { - AddVariableInfo addVariableInfo = obj as AddVariableInfo; - if (addVariableInfo == null) - { - return; - } - - var flowchart = addVariableInfo.flowchart; - System.Type variableType = addVariableInfo.variableType; - - Undo.RecordObject(flowchart, "Add Variable"); - Variable newVariable = flowchart.gameObject.AddComponent(variableType) as Variable; - newVariable.Key = flowchart.GetUniqueVariableKey(""); - flowchart.Variables.Add(newVariable); - - // Because this is an async call, we need to force prefab instances to record changes - PrefabUtility.RecordPrefabInstancePropertyModifications(flowchart); - } - - public static List FindAllDerivedTypes() - { - return FindAllDerivedTypes(Assembly.GetAssembly(typeof(T))); - } - public static List FindAllDerivedTypes(Assembly assembly) - { - var derivedType = typeof(T); - return assembly - .GetTypes() - .Where(t => - t != derivedType && - derivedType.IsAssignableFrom(t) - ).ToList(); - - } /// /// When modifying custom editor code you can occasionally end up with orphaned editor instances. diff --git a/Assets/Fungus/Scripts/Editor/PopupContent.meta b/Assets/Fungus/Scripts/Editor/PopupContent.meta new file mode 100644 index 00000000..d6f72bbb --- /dev/null +++ b/Assets/Fungus/Scripts/Editor/PopupContent.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b99cf5f1ea08b914c94ee372bc4d76a7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Fungus/Scripts/Editor/CommandSelectorPopupWindowContent.cs b/Assets/Fungus/Scripts/Editor/PopupContent/CommandSelectorPopupWindowContent.cs similarity index 86% rename from Assets/Fungus/Scripts/Editor/CommandSelectorPopupWindowContent.cs rename to Assets/Fungus/Scripts/Editor/PopupContent/CommandSelectorPopupWindowContent.cs index 62449ebe..c3ca20b8 100644 --- a/Assets/Fungus/Scripts/Editor/CommandSelectorPopupWindowContent.cs +++ b/Assets/Fungus/Scripts/Editor/PopupContent/CommandSelectorPopupWindowContent.cs @@ -8,16 +8,9 @@ namespace Fungus.EditorUtils { /// /// Searchable Popup Window for adding a command to a block - /// - /// Inspired by https://github.com/roboryantron/UnityEditorJunkie/blob/master/Assets/SearchableEnum/Code/Editor/SearchablePopup.cs /// public class CommandSelectorPopupWindowContent : BasePopupWindowContent { - private static readonly char[] SPLIT_INPUT_ON = new char[] { ' ', '/', '\\' }; - private static readonly int MAX_PREVIEW_GRID = 7; - private static readonly string ELIPSIS = "..."; - - static List commandTypes; static void CacheCommandTypes() @@ -34,8 +27,8 @@ namespace Fungus.EditorUtils static Block curBlock; static protected List> filteredAttributes; - public CommandSelectorPopupWindowContent(string currentHandlerName, Block block, int width, int height) - : base(currentHandlerName, block, width, height) + public CommandSelectorPopupWindowContent(string currentHandlerName, int width, int height) + : base(currentHandlerName, width, height) { } @@ -53,12 +46,12 @@ namespace Fungus.EditorUtils } - filteredAttributes = GetFilteredSupportedCommands(block.GetFlowchart()); + filteredAttributes = GetFilteredSupportedCommands(curBlock.GetFlowchart()); int i = 0; foreach (var item in filteredAttributes) { - allItems.Add(new FilteredListItem(i, (item.Value.Category.Length > 0 ? item.Value.Category + "/" : "") + item.Value.CommandName, item.Value.HelpText)); + allItems.Add(new FilteredListItem(i, (item.Value.Category.Length > 0 ? item.Value.Category + CATEGORY_CHAR : "") + item.Value.CommandName, item.Value.HelpText)); i++; } @@ -68,7 +61,7 @@ namespace Fungus.EditorUtils { curBlock = block; - var win = new CommandSelectorPopupWindowContent(currentHandlerName, block, + var win = new CommandSelectorPopupWindowContent(currentHandlerName, width, (int)(height - EditorGUIUtility.singleLineHeight * 3)); PopupWindow.Show(position, win); @@ -87,14 +80,10 @@ namespace Fungus.EditorUtils return filteredAttributes; } - - static protected void DoOlderMenu() { - var flowchart = (Flowchart)curBlock.GetFlowchart(); - GenericMenu commandMenu = new GenericMenu(); // Build menu list @@ -108,7 +97,7 @@ namespace Fungus.EditorUtils } else { - menuItem = new GUIContent(keyPair.Value.Category + "/" + keyPair.Value.CommandName); + menuItem = new GUIContent(keyPair.Value.Category + CATEGORY_CHAR + keyPair.Value.CommandName); } commandMenu.AddItem(menuItem, false, AddCommandCallback, keyPair.Key); diff --git a/Assets/Fungus/Scripts/Editor/CommandSelectorPopupWindowContent.cs.meta b/Assets/Fungus/Scripts/Editor/PopupContent/CommandSelectorPopupWindowContent.cs.meta similarity index 100% rename from Assets/Fungus/Scripts/Editor/CommandSelectorPopupWindowContent.cs.meta rename to Assets/Fungus/Scripts/Editor/PopupContent/CommandSelectorPopupWindowContent.cs.meta diff --git a/Assets/Fungus/Scripts/Editor/EventSelectorPopupWindowContent.cs b/Assets/Fungus/Scripts/Editor/PopupContent/EventSelectorPopupWindowContent.cs similarity index 94% rename from Assets/Fungus/Scripts/Editor/EventSelectorPopupWindowContent.cs rename to Assets/Fungus/Scripts/Editor/PopupContent/EventSelectorPopupWindowContent.cs index 77d5faf1..af9562ab 100644 --- a/Assets/Fungus/Scripts/Editor/EventSelectorPopupWindowContent.cs +++ b/Assets/Fungus/Scripts/Editor/PopupContent/EventSelectorPopupWindowContent.cs @@ -8,8 +8,6 @@ namespace Fungus.EditorUtils { /// /// Searchable Popup Window for selecting Event type, used by block editor - /// - /// Inspired by https://github.com/roboryantron/UnityEditorJunkie/blob/master/Assets/SearchableEnum/Code/Editor/SearchablePopup.cs /// public class EventSelectorPopupWindowContent : BasePopupWindowContent { @@ -32,10 +30,11 @@ namespace Fungus.EditorUtils public Type eventHandlerType; } - + protected Block block; public EventSelectorPopupWindowContent(string currentHandlerName, Block block, int width, int height) - :base(currentHandlerName, block, width, height) + :base(currentHandlerName, width, height) { + this.block = block; } protected override void PrepareAllItems() @@ -51,7 +50,7 @@ namespace Fungus.EditorUtils EventHandlerInfoAttribute info = EventHandlerEditor.GetEventHandlerInfo(type); if (info != null) { - allItems.Add(new FilteredListItem(i, (info.Category.Length > 0 ? info.Category + "/" : "") + info.EventHandlerName, info.HelpText)); + allItems.Add(new FilteredListItem(i, (info.Category.Length > 0 ? info.Category + CATEGORY_CHAR : "") + info.EventHandlerName, info.HelpText)); } else { diff --git a/Assets/Fungus/Scripts/Editor/EventSelectorPopupWindowContent.cs.meta b/Assets/Fungus/Scripts/Editor/PopupContent/EventSelectorPopupWindowContent.cs.meta similarity index 100% rename from Assets/Fungus/Scripts/Editor/EventSelectorPopupWindowContent.cs.meta rename to Assets/Fungus/Scripts/Editor/PopupContent/EventSelectorPopupWindowContent.cs.meta diff --git a/Assets/Fungus/Scripts/Editor/PopupContent/VariableSelectPopupWindowContent.cs b/Assets/Fungus/Scripts/Editor/PopupContent/VariableSelectPopupWindowContent.cs new file mode 100644 index 00000000..38630489 --- /dev/null +++ b/Assets/Fungus/Scripts/Editor/PopupContent/VariableSelectPopupWindowContent.cs @@ -0,0 +1,125 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using System.Linq; + +namespace Fungus.EditorUtils +{ + public class VariableSelectPopupWindowContent : BasePopupWindowContent + { + static readonly int POPUP_WIDTH = 200, POPUP_HEIGHT = 200; + static List types; + + static void CacheVariableTypes() + { + var derivedType = typeof(Variable); + types = EditorExtensions.FindDerivedTypes(derivedType) + .Where(x => !x.IsAbstract && derivedType.IsAssignableFrom(x)) + .ToList(); + } + + [UnityEditor.Callbacks.DidReloadScripts] + private static void OnScriptsReloaded() + { + CacheVariableTypes(); + } + + protected override void PrepareAllItems() + { + if(types == null || types.Count == 0) + { + CacheVariableTypes(); + } + + int i = 0; + foreach (var item in types) + { + VariableInfoAttribute variableInfo = VariableEditor.GetVariableInfo(item); + if (variableInfo != null) + { + allItems.Add(new FilteredListItem(i, (variableInfo.Category.Length > 0 ? variableInfo.Category + CATEGORY_CHAR : "") + variableInfo.VariableType)); + } + + i++; + } + } + + protected override void SelectByOrigIndex(int index) + { + AddVariable(types[index]); + } + + static public void DoAddVariable(Rect position, string currentHandlerName, Flowchart flowchart) + { + curFlowchart = flowchart; + //new method + VariableSelectPopupWindowContent win = new VariableSelectPopupWindowContent(currentHandlerName, POPUP_WIDTH, POPUP_HEIGHT); + PopupWindow.Show(position, win); + + //old method + DoOlderMenu(flowchart); + } + + static protected void DoOlderMenu(Flowchart flowchart) + { + GenericMenu menu = new GenericMenu(); + + // Add variable types without a category + foreach (var type in types) + { + VariableInfoAttribute variableInfo = VariableEditor.GetVariableInfo(type); + if (variableInfo == null || + variableInfo.Category != "") + { + continue; + } + + GUIContent typeName = new GUIContent(variableInfo.VariableType); + + menu.AddItem(typeName, false, AddVariable, type); + } + + // Add types with a category + foreach (var type in types) + { + VariableInfoAttribute variableInfo = VariableEditor.GetVariableInfo(type); + if (variableInfo == null || + variableInfo.Category == "") + { + continue; + } + + GUIContent typeName = new GUIContent(variableInfo.Category + CATEGORY_CHAR + variableInfo.VariableType); + + menu.AddItem(typeName, false, AddVariable, type); + } + + menu.ShowAsContext(); + } + + private static Flowchart curFlowchart; + + public VariableSelectPopupWindowContent(string currentHandlerName, int width, int height) + : base(currentHandlerName, width, height) + { + } + + protected static void AddVariable(object obj) + { + System.Type t = obj as System.Type; + if (t == null) + { + return; + } + + Undo.RecordObject(curFlowchart, "Add Variable"); + Variable newVariable = curFlowchart.gameObject.AddComponent(t) as Variable; + newVariable.Key = curFlowchart.GetUniqueVariableKey(""); + curFlowchart.Variables.Add(newVariable); + + // Because this is an async call, we need to force prefab instances to record changes + PrefabUtility.RecordPrefabInstancePropertyModifications(curFlowchart); + } + } +} \ No newline at end of file diff --git a/Assets/Fungus/Scripts/Editor/PopupContent/VariableSelectPopupWindowContent.cs.meta b/Assets/Fungus/Scripts/Editor/PopupContent/VariableSelectPopupWindowContent.cs.meta new file mode 100644 index 00000000..bbf8feb1 --- /dev/null +++ b/Assets/Fungus/Scripts/Editor/PopupContent/VariableSelectPopupWindowContent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 933b6fc31e5eac04ea3b2a1fa9a3b418 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From bee577d945dacacd17339ffaf52f1525586ec557 Mon Sep 17 00:00:00 2001 From: desktop-maesty/steve Date: Tue, 7 Aug 2018 20:59:58 +1000 Subject: [PATCH 09/11] BasePopupWindowContent now has None as optional --- Assets/Fungus/Scripts/Editor/BasePopupWindowContent.cs | 7 +++++-- .../Editor/PopupContent/EventSelectorPopupWindowContent.cs | 2 +- .../PopupContent/VariableSelectPopupWindowContent.cs | 3 +++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Assets/Fungus/Scripts/Editor/BasePopupWindowContent.cs b/Assets/Fungus/Scripts/Editor/BasePopupWindowContent.cs index 3c7339de..5bf7406e 100644 --- a/Assets/Fungus/Scripts/Editor/BasePopupWindowContent.cs +++ b/Assets/Fungus/Scripts/Editor/BasePopupWindowContent.cs @@ -53,13 +53,15 @@ namespace Fungus.EditorUtils protected float scrollOffset; protected int currentIndex; protected Vector2 size; + protected bool hasNoneOption = false; static readonly char[] SEARCH_SPLITS = new char[]{ CATEGORY_CHAR, ' ' }; protected static readonly char CATEGORY_CHAR = '/'; - public BasePopupWindowContent(string currentHandlerName, int width, int height) + public BasePopupWindowContent(string currentHandlerName, int width, int height, bool showNoneOption = false) { this.size = new Vector2(width, height); + hasNoneOption = showNoneOption; PrepareAllItems(); @@ -140,7 +142,8 @@ namespace Fungus.EditorUtils hoverIndex = 0; scroll = Vector2.zero; - visibleItems.Insert(0, new FilteredListItem(-1, "None")); + if(hasNoneOption) + visibleItems.Insert(0, new FilteredListItem(-1, "None")); } private void DrawSelectionArea(Rect scrollRect) diff --git a/Assets/Fungus/Scripts/Editor/PopupContent/EventSelectorPopupWindowContent.cs b/Assets/Fungus/Scripts/Editor/PopupContent/EventSelectorPopupWindowContent.cs index af9562ab..d5c28624 100644 --- a/Assets/Fungus/Scripts/Editor/PopupContent/EventSelectorPopupWindowContent.cs +++ b/Assets/Fungus/Scripts/Editor/PopupContent/EventSelectorPopupWindowContent.cs @@ -32,7 +32,7 @@ namespace Fungus.EditorUtils protected Block block; public EventSelectorPopupWindowContent(string currentHandlerName, Block block, int width, int height) - :base(currentHandlerName, width, height) + :base(currentHandlerName, width, height, true) { this.block = block; } diff --git a/Assets/Fungus/Scripts/Editor/PopupContent/VariableSelectPopupWindowContent.cs b/Assets/Fungus/Scripts/Editor/PopupContent/VariableSelectPopupWindowContent.cs index 38630489..4a72e1d2 100644 --- a/Assets/Fungus/Scripts/Editor/PopupContent/VariableSelectPopupWindowContent.cs +++ b/Assets/Fungus/Scripts/Editor/PopupContent/VariableSelectPopupWindowContent.cs @@ -6,6 +6,9 @@ using System.Linq; namespace Fungus.EditorUtils { + /// + /// Show the variable selection window as a searchable popup + /// public class VariableSelectPopupWindowContent : BasePopupWindowContent { static readonly int POPUP_WIDTH = 200, POPUP_HEIGHT = 200; From 878ea043d933d9d061ddc0d71c07c7467a6ffe73 Mon Sep 17 00:00:00 2001 From: desktop-maesty/steve Date: Fri, 14 Sep 2018 18:04:45 +1000 Subject: [PATCH 10/11] Add Option to enable Searchable PopupMenus --- .../Scripts/Editor/FungusEditorPreferences.cs | 10 ++++++++-- .../CommandSelectorPopupWindowContent.cs | 14 +++++++++++--- .../EventSelectorPopupWindowContent.cs | 10 ++++++---- .../VariableSelectPopupWindowContent.cs | 10 ++++++---- 4 files changed, 31 insertions(+), 13 deletions(-) diff --git a/Assets/Fungus/Scripts/Editor/FungusEditorPreferences.cs b/Assets/Fungus/Scripts/Editor/FungusEditorPreferences.cs index 14312634..bf4bae54 100644 --- a/Assets/Fungus/Scripts/Editor/FungusEditorPreferences.cs +++ b/Assets/Fungus/Scripts/Editor/FungusEditorPreferences.cs @@ -16,8 +16,11 @@ namespace Fungus { // Have we loaded the prefs yet private static bool prefsLoaded = false; + const string HIDE_MUSH_KEY = "hideMushroomInHierarchy"; + const string USE_EXP_MENUS = "useExperimentalMenus"; public static bool hideMushroomInHierarchy; + public static bool useExperimentalMenus; static FungusEditorPreferences() { @@ -36,17 +39,20 @@ namespace Fungus // Preferences GUI hideMushroomInHierarchy = EditorGUILayout.Toggle("Hide Mushroom Flowchart Icon", hideMushroomInHierarchy); + useExperimentalMenus = EditorGUILayout.Toggle(new GUIContent("Experimental Searchable Menus", "Experimental menus replace the Event, Add Variable and Add Command menus with a searchable menu more like the Unity AddComponent menu."), useExperimentalMenus); // Save the preferences if (GUI.changed) { - EditorPrefs.SetBool("hideMushroomInHierarchy", hideMushroomInHierarchy); + EditorPrefs.SetBool(HIDE_MUSH_KEY, hideMushroomInHierarchy); + EditorPrefs.SetBool(USE_EXP_MENUS, useExperimentalMenus); } } public static void LoadOnScriptLoad() { - hideMushroomInHierarchy = EditorPrefs.GetBool("hideMushroomInHierarchy", false); + hideMushroomInHierarchy = EditorPrefs.GetBool(HIDE_MUSH_KEY, false); + useExperimentalMenus = EditorPrefs.GetBool(USE_EXP_MENUS, false); prefsLoaded = true; } } diff --git a/Assets/Fungus/Scripts/Editor/PopupContent/CommandSelectorPopupWindowContent.cs b/Assets/Fungus/Scripts/Editor/PopupContent/CommandSelectorPopupWindowContent.cs index c3ca20b8..6e8fe765 100644 --- a/Assets/Fungus/Scripts/Editor/PopupContent/CommandSelectorPopupWindowContent.cs +++ b/Assets/Fungus/Scripts/Editor/PopupContent/CommandSelectorPopupWindowContent.cs @@ -61,10 +61,18 @@ namespace Fungus.EditorUtils { curBlock = block; - var win = new CommandSelectorPopupWindowContent(currentHandlerName, - width, (int)(height - EditorGUIUtility.singleLineHeight * 3)); - PopupWindow.Show(position, win); + if (FungusEditorPreferences.useExperimentalMenus) + { + var win = new CommandSelectorPopupWindowContent(currentHandlerName, + width, (int)(height - EditorGUIUtility.singleLineHeight * 3)); + PopupWindow.Show(position, win); + } + else + { + //need to ensure we have filtered data + filteredAttributes = GetFilteredSupportedCommands(curBlock.GetFlowchart()); + } //old method DoOlderMenu(); diff --git a/Assets/Fungus/Scripts/Editor/PopupContent/EventSelectorPopupWindowContent.cs b/Assets/Fungus/Scripts/Editor/PopupContent/EventSelectorPopupWindowContent.cs index d5c28624..b295f3a0 100644 --- a/Assets/Fungus/Scripts/Editor/PopupContent/EventSelectorPopupWindowContent.cs +++ b/Assets/Fungus/Scripts/Editor/PopupContent/EventSelectorPopupWindowContent.cs @@ -72,10 +72,12 @@ namespace Fungus.EditorUtils static public void DoEventHandlerPopUp(Rect position, string currentHandlerName, Block block, int width, int height) { - //new method - EventSelectorPopupWindowContent win = new EventSelectorPopupWindowContent(currentHandlerName, block, width, height); - PopupWindow.Show(position, win); - + if (FungusEditorPreferences.useExperimentalMenus) + { + //new method + EventSelectorPopupWindowContent win = new EventSelectorPopupWindowContent(currentHandlerName, block, width, height); + PopupWindow.Show(position, win); + } //old method DoOlderMenu(block); } diff --git a/Assets/Fungus/Scripts/Editor/PopupContent/VariableSelectPopupWindowContent.cs b/Assets/Fungus/Scripts/Editor/PopupContent/VariableSelectPopupWindowContent.cs index 4a72e1d2..4e0d0384 100644 --- a/Assets/Fungus/Scripts/Editor/PopupContent/VariableSelectPopupWindowContent.cs +++ b/Assets/Fungus/Scripts/Editor/PopupContent/VariableSelectPopupWindowContent.cs @@ -56,10 +56,12 @@ namespace Fungus.EditorUtils static public void DoAddVariable(Rect position, string currentHandlerName, Flowchart flowchart) { curFlowchart = flowchart; - //new method - VariableSelectPopupWindowContent win = new VariableSelectPopupWindowContent(currentHandlerName, POPUP_WIDTH, POPUP_HEIGHT); - PopupWindow.Show(position, win); - + if (FungusEditorPreferences.useExperimentalMenus) + { + //new method + VariableSelectPopupWindowContent win = new VariableSelectPopupWindowContent(currentHandlerName, POPUP_WIDTH, POPUP_HEIGHT); + PopupWindow.Show(position, win); + } //old method DoOlderMenu(flowchart); } From 4e2231a92df7af15faa444410b929807b28dafa7 Mon Sep 17 00:00:00 2001 From: desktop-maesty/steve Date: Sun, 23 Sep 2018 15:12:59 +1000 Subject: [PATCH 11/11] Correct width calc on smaller width inspectors Correctly calc index in command menu popup when list is being filtered or reordered by the flowchart. --- Assets/Fungus/Scripts/Editor/BlockEditor.cs | 3 ++- .../PopupContent/CommandSelectorPopupWindowContent.cs | 7 +++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Assets/Fungus/Scripts/Editor/BlockEditor.cs b/Assets/Fungus/Scripts/Editor/BlockEditor.cs index 8a1cc31a..5850dfd6 100644 --- a/Assets/Fungus/Scripts/Editor/BlockEditor.cs +++ b/Assets/Fungus/Scripts/Editor/BlockEditor.cs @@ -288,7 +288,8 @@ namespace Fungus.EditorUtils GUILayout.FlexibleSpace(); - var pos = EditorGUILayout.GetControlRect(true, 0, EditorStyles.objectField); + //using false to prevent forcing a longer row than will fit on smallest inspector + var pos = EditorGUILayout.GetControlRect(false, 0, EditorStyles.objectField); if (pos.x != 0) { lastCMDpopupPos = pos; diff --git a/Assets/Fungus/Scripts/Editor/PopupContent/CommandSelectorPopupWindowContent.cs b/Assets/Fungus/Scripts/Editor/PopupContent/CommandSelectorPopupWindowContent.cs index 6e8fe765..ea3361ec 100644 --- a/Assets/Fungus/Scripts/Editor/PopupContent/CommandSelectorPopupWindowContent.cs +++ b/Assets/Fungus/Scripts/Editor/PopupContent/CommandSelectorPopupWindowContent.cs @@ -48,12 +48,11 @@ namespace Fungus.EditorUtils filteredAttributes = GetFilteredSupportedCommands(curBlock.GetFlowchart()); - int i = 0; foreach (var item in filteredAttributes) { - allItems.Add(new FilteredListItem(i, (item.Value.Category.Length > 0 ? item.Value.Category + CATEGORY_CHAR : "") + item.Value.CommandName, item.Value.HelpText)); - - i++; + //force lookup to orig index here to account for commmand lists being filtered by users + var newFilteredItem = new FilteredListItem(commandTypes.IndexOf(item.Key), (item.Value.Category.Length > 0 ? item.Value.Category + CATEGORY_CHAR : "") + item.Value.CommandName, item.Value.HelpText); + allItems.Add(newFilteredItem); } }