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); //} } }