// UltEvents // Copyright 2020 Kybernetik //
// Copied from Kybernetik.Core.
#if UNITY_EDITOR
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using UnityEditor;
using UnityEngine;
namespace UltEvents.Editor
{
/// [Editor-Only] Allows you to draw GUI fields which can be used to pick an object from a list.
public static class ObjectPicker
{
/************************************************************************************************************************/
#region Main Drawing Methods
/************************************************************************************************************************/
/// Draws a field which lets you pick an object from a list and returns the selected object.
public static T Draw(Rect area, T selected, Func> getOptions, int suggestions, Func getLabel, Func getDragAndDrop,
GUIStyle style)
{
var id = CheckCommand(ref selected);
if (GUI.Button(area, getLabel(selected), style))
ObjectPickerWindow.Show(id, selected, getOptions(), suggestions, getLabel);
CheckDragAndDrop(area, ref selected, getOptions, getDragAndDrop);
return selected;
}
/// Draws a field which lets you pick an object from a list and returns the selected object.
public static T Draw(Rect area, T selected, Func> getOptions, int suggestions, Func getLabel, Func getDragAndDrop)
{
return Draw(area, selected, getOptions, suggestions, getLabel, getDragAndDrop, InternalGUI.TypeButtonStyle);
}
/************************************************************************************************************************/
/// Draws a field (using GUILayout) which lets you pick an object from a list and returns the selected object.
public static T DrawLayout(T selected, Func> getOptions, int suggestions, Func getLabel, Func getDragAndDrop,
GUIStyle style, params GUILayoutOption[] layoutOptions)
{
var id = CheckCommand(ref selected);
if (GUILayout.Button(getLabel(selected), style, layoutOptions))
ObjectPickerWindow.Show(id, selected, getOptions(), suggestions, getLabel);
CheckDragAndDrop(GUILayoutUtility.GetLastRect(), ref selected, getOptions, getDragAndDrop);
return selected;
}
/// Draws a field (using GUILayout) which lets you pick an object from a list and returns the selected object.
public static T DrawLayout(T selected, Func> getOptions, int suggestions, Func getLabel, Func getDragAndDrop,
params GUILayoutOption[] layoutOptions)
{
return DrawLayout(selected, getOptions, suggestions, getLabel, getDragAndDrop, InternalGUI.TypeButtonStyle, layoutOptions);
}
/************************************************************************************************************************/
///
/// Draws a field (as an inspector field using GUILayout) which lets you pick an object from a list and returns
/// the selected object.
///
public static T DrawEditorLayout(GUIContent label, T selected, Func> getOptions, int suggestions,
Func getLabel, Func getDragAndDrop, GUIStyle style, params GUILayoutOption[] layoutOptions)
{
GUILayout.BeginHorizontal();
{
GUILayout.Label(label, GUILayout.Width(EditorGUIUtility.labelWidth - 4));
selected = DrawLayout(selected, getOptions, suggestions, getLabel, getDragAndDrop, style, layoutOptions);
}
GUILayout.EndHorizontal();
return selected;
}
///
/// Draws a field (as an inspector field using GUILayout) which lets you pick an object from a list and returns
/// the selected object.
///
public static T DrawEditorLayout(GUIContent label, T selected, Func> getOptions, int suggestions,
Func getLabel, Func getDragAndDrop, params GUILayoutOption[] options)
{
return DrawEditorLayout(label, selected, getOptions, suggestions, getLabel, getDragAndDrop, InternalGUI.TypeButtonStyle, options);
}
/************************************************************************************************************************/
#endregion
/************************************************************************************************************************/
#region Type Field
/************************************************************************************************************************/
/// Draws a field which lets you pick a from a list and returns the selected type.
public static Type DrawTypeField(Rect area, Type selected, Func> getOptions, int suggestions, GUIStyle style)
{
return Draw(area, selected, getOptions, suggestions,
getLabel: (type) => new GUIContent(type != null ? type.GetNameCS() : "null"),
getDragAndDrop: () => DragAndDrop.objectReferences[0].GetType(),
style: style);
}
/************************************************************************************************************************/
/// Draws a field which lets you pick an asset from a list and returns the selected type.
public static Type DrawAssetTypeField(Rect area, Type selected, Func> getOptions, int suggestions, GUIStyle style)
{
return Draw(area, selected, getOptions, suggestions,
getLabel: (type) => new GUIContent(type != null ? type.GetNameCS() : "null", AssetPreview.GetMiniTypeThumbnail(type)),
getDragAndDrop: () => DragAndDrop.objectReferences[0].GetType(),
style: style);
}
/************************************************************************************************************************/
/// Draws a field which lets you pick a from a list and returns the selected .
public static string DrawTypeField(Rect area, string selectedTypeName, Func> getOptions, int suggestions, GUIStyle style)
{
var selected = Type.GetType(selectedTypeName);
selected = Draw(area, selected, getOptions, suggestions,
getLabel: (type) => new GUIContent(type != null ? type.GetNameCS() : "No Type Selected"),
getDragAndDrop: () => DragAndDrop.objectReferences[0].GetType(),
style: style);
return selected != null ? selected.AssemblyQualifiedName : null;
}
/************************************************************************************************************************/
#endregion
/************************************************************************************************************************/
#region Utils
/************************************************************************************************************************/
///
/// Removes any duplicates of the first few elements in `options` (from 0 to `suggestions`) from anywhere later
/// in the list.
///
public static void RemoveDuplicateSuggestions(List options, int suggestions) where T : class
{
for (int i = options.Count - 1; i >= suggestions; i--)
{
var obj = options[i];
for (int j = 0; j < suggestions; j++)
{
if (obj == options[j])
{
options.RemoveAt(j);
break;
}
}
}
}
/************************************************************************************************************************/
private static int CheckCommand(ref T selected)
{
var id = GUIUtility.GetControlID(FocusType.Passive);
ObjectPickerWindow.TryGetPickedObject(id, ref selected);
return id;
}
/************************************************************************************************************************/
private static void CheckDragAndDrop(Rect area, ref T selected, Func> getOptions, Func getDragAndDrop)
{
var currentEvent = Event.current;
if (DragAndDrop.objectReferences.Length == 1 && area.Contains(currentEvent.mousePosition))
{
var drop = getDragAndDrop();
// If the dragged object is a valid type, continue.
if (!getOptions().Contains(drop))
return;
if (currentEvent.type == EventType.DragUpdated || currentEvent.type == EventType.MouseDrag)
{
DragAndDrop.visualMode = DragAndDropVisualMode.Link;
}
else if (currentEvent.type == EventType.DragPerform)
{
selected = drop;
DragAndDrop.AcceptDrag();
GUI.changed = true;
}
}
}
/************************************************************************************************************************/
private static class InternalGUI
{
public static readonly GUIStyle
TypeButtonStyle;
static InternalGUI()
{
TypeButtonStyle = new GUIStyle(EditorStyles.miniButton)
{
alignment = TextAnchor.MiddleLeft
};
}
}
/************************************************************************************************************************/
#endregion
/************************************************************************************************************************/
}
/************************************************************************************************************************/
internal sealed class ObjectPickerWindow : EditorWindow
{
/************************************************************************************************************************/
private static int _FieldID;
private static bool _HasPickedObject;
private static object _PickedObject;
private readonly List
Labels = new List(),
SearchedLabels = new List();
private readonly List