426 lines
No EOL
18 KiB
C#
Executable file
426 lines
No EOL
18 KiB
C#
Executable file
#if UNITY_EDITOR
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using ABI.CCK.Scripts.Editor.Tools;
|
|
using UnityEditor;
|
|
using UnityEditor.IMGUI.Controls;
|
|
using UnityEditorInternal;
|
|
using UnityEngine;
|
|
|
|
namespace ABI.CCK.Scripts.Editor
|
|
{
|
|
public static partial class EditorGUIExtensions
|
|
{
|
|
private const float DEFAULT_DROPDOWN_BUTTON_WIDTH = 35;
|
|
private static readonly Dictionary<string, GUIContent> s_iconContentCache = new();
|
|
private static int s_lastSelectedValue = -1;
|
|
|
|
#region Specialized EditorGUI
|
|
|
|
public delegate void AppendAdditionalMenuItemsDelegate(GenericMenuBuilder genericMenuBuilder, ReorderableList list);
|
|
public static void UtilityMenu(Rect rect, ReorderableList list, SerializedProperty property = null, AppendAdditionalMenuItemsDelegate appendAdditionalMenuItems = null)
|
|
{
|
|
Rect dropdownButtonRect = new(rect.x + rect.width - 21, rect.y, 20, EditorGUIUtility.singleLineHeight);
|
|
if (!EditorGUI.DropdownButton(dropdownButtonRect, GUIContent.none, FocusType.Passive))
|
|
return;
|
|
|
|
GenericMenuBuilder genericMenuBuilder = new();
|
|
ClipboardUtils.AppendToMenu(genericMenuBuilder, list, property); // default for all uses atm
|
|
if (appendAdditionalMenuItems != null)
|
|
{
|
|
genericMenuBuilder.AddSeparator();
|
|
appendAdditionalMenuItems.Invoke(genericMenuBuilder, list);
|
|
}
|
|
genericMenuBuilder.DropDown(dropdownButtonRect);
|
|
}
|
|
|
|
public static string AdvancedDropdownInput(
|
|
Rect rect,
|
|
string currentValue,
|
|
List<string> dropdownItems,
|
|
string label,
|
|
string defaultTitle,
|
|
GUIContent buttonContent = null)
|
|
{
|
|
if (buttonContent == null)
|
|
{
|
|
buttonContent = dropdownItems.Contains(currentValue)
|
|
? GetCachedIconContent("d_Search Icon")
|
|
: GetCachedIconContent("console.erroricon.sml");
|
|
}
|
|
|
|
string title = (dropdownItems.Count == 0 ? defaultTitle : currentValue);
|
|
return CustomDropdown(rect, label, currentValue, dropdownItems.ToArray(), buttonContent, DEFAULT_DROPDOWN_BUTTON_WIDTH, title);
|
|
}
|
|
|
|
public static string AdvancedDropdownInput(
|
|
Rect rect,
|
|
string currentValue,
|
|
List<string> dropdownItems,
|
|
string title,
|
|
GUIContent buttonContent = null)
|
|
{
|
|
if (buttonContent == null)
|
|
{
|
|
buttonContent = dropdownItems.Contains(currentValue)
|
|
? GetCachedIconContent("d_Search Icon")
|
|
: GetCachedIconContent("console.erroricon.sml");
|
|
}
|
|
|
|
return CustomDropdown(rect, currentValue, dropdownItems.ToArray(), buttonContent, DEFAULT_DROPDOWN_BUTTON_WIDTH, title);
|
|
}
|
|
|
|
public static string AdvancedDropdownInput(
|
|
Rect rect,
|
|
string currentValue,
|
|
string[] dropdownItems,
|
|
string title,
|
|
GUIContent buttonContent = null)
|
|
{
|
|
if (buttonContent == null)
|
|
{
|
|
buttonContent = dropdownItems.Contains(currentValue)
|
|
? GetCachedIconContent("d_Search Icon")
|
|
: GetCachedIconContent("console.erroricon.sml");
|
|
}
|
|
|
|
return CustomDropdown(rect, currentValue, dropdownItems, buttonContent, DEFAULT_DROPDOWN_BUTTON_WIDTH, title);
|
|
}
|
|
|
|
// bit lazy ig
|
|
public static void LimitSliderSided(string label, ref Vector2 limits, float hardLimitMin, float hardLimitMax)
|
|
{
|
|
var originalLabelWidth = EditorGUIUtility.labelWidth;
|
|
EditorGUIUtility.labelWidth = 150;
|
|
|
|
EditorGUILayout.BeginHorizontal();
|
|
EditorGUILayout.LabelField(label, GUILayout.Width(originalLabelWidth - 16));
|
|
limits.x = EditorGUILayout.FloatField(limits.x, GUILayout.Width(50));
|
|
GUILayout.Space(-13);
|
|
EditorGUILayout.MinMaxSlider(ref limits.x, ref limits.y, hardLimitMin, hardLimitMax);
|
|
GUILayout.Space(-13);
|
|
limits.y = EditorGUILayout.FloatField(limits.y, GUILayout.Width(50));
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
EditorGUIUtility.labelWidth = originalLabelWidth;
|
|
}
|
|
|
|
public static void LimitSliderSided(ref Rect rect, float spacing, string label, ref Vector2 limits, float hardLimitMin, float hardLimitMax)
|
|
{
|
|
// Save the original label width and adjust it for your custom drawing
|
|
var originalLabelWidth = EditorGUIUtility.labelWidth;
|
|
EditorGUIUtility.labelWidth = 150;
|
|
|
|
// Calculate positions and sizes for custom drawing elements within the rect
|
|
float labelWidth = originalLabelWidth - 16;
|
|
float fieldWidth = 50;
|
|
float sliderWidth = rect.width - labelWidth - fieldWidth * 2 - spacing * 4;
|
|
float currentY = rect.y;
|
|
|
|
// Label
|
|
Rect labelRect = new Rect(rect.x, currentY, labelWidth, EditorGUIUtility.singleLineHeight);
|
|
GUI.Label(labelRect, label);
|
|
|
|
// Field for limits.x
|
|
Rect fieldXRect = new Rect(labelRect.xMax + spacing, currentY, fieldWidth, EditorGUIUtility.singleLineHeight);
|
|
limits.x = EditorGUI.FloatField(fieldXRect, limits.x);
|
|
|
|
// MinMaxSlider
|
|
Rect sliderRect = new Rect(fieldXRect.xMax + spacing, currentY, sliderWidth, EditorGUIUtility.singleLineHeight);
|
|
EditorGUI.MinMaxSlider(sliderRect, ref limits.x, ref limits.y, hardLimitMin, hardLimitMax);
|
|
|
|
// Field for limits.y
|
|
Rect fieldYRect = new Rect(sliderRect.xMax + spacing, currentY, fieldWidth, EditorGUIUtility.singleLineHeight);
|
|
limits.y = EditorGUI.FloatField(fieldYRect, limits.y);
|
|
|
|
// Reset the original label width
|
|
EditorGUIUtility.labelWidth = originalLabelWidth;
|
|
|
|
// Adjust rect.y for the next control, if you're drawing multiple controls in sequence
|
|
rect.y += spacing;
|
|
}
|
|
|
|
public static void LimitSliderSidedProp(string label, SerializedProperty limitsProp, float hardLimitMin, float hardLimitMax) {
|
|
Vector2 limits = limitsProp.vector2Value;
|
|
LimitSliderSided(label, ref limits, hardLimitMin, hardLimitMax);
|
|
limitsProp.vector2Value = limits;
|
|
}
|
|
|
|
public static void LimitSliderSidedFloats(string label, SerializedProperty floatMin, SerializedProperty floatMax, float hardLimitMin, float hardLimitMax) {
|
|
Vector2 limits = new(floatMin.floatValue, floatMax.floatValue);
|
|
LimitSliderSided(label, ref limits, hardLimitMin, hardLimitMax);
|
|
floatMin.floatValue = limits.x;
|
|
floatMax.floatValue = limits.y;
|
|
}
|
|
|
|
public static void LimitSliderSidedFloats(ref Rect rect, float spacing, string label, SerializedProperty floatMin, SerializedProperty floatMax, float hardLimitMin, float hardLimitMax) {
|
|
Vector2 limits = new(floatMin.floatValue, floatMax.floatValue);
|
|
LimitSliderSided(ref rect, spacing, label, ref limits, hardLimitMin, hardLimitMax);
|
|
floatMin.floatValue = limits.x;
|
|
floatMax.floatValue = limits.y;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region CustomPopup
|
|
|
|
public static string CustomPopup(Rect rect, string label, string currentItem, string[] items,
|
|
string title = null)
|
|
{
|
|
// TODO: temp fix
|
|
int currentIndex = Array.IndexOf(items, currentItem);
|
|
currentIndex = CustomPopup(rect, label, currentIndex, items, title);
|
|
return currentIndex >= 0 && currentIndex < items.Length ? items[currentIndex] : CVRCommon.NONE_OR_EMPTY;
|
|
}
|
|
|
|
public static string CustomPopup(Rect rect, string currentItem, string[] items,
|
|
string title = null)
|
|
{
|
|
// TODO: temp fix
|
|
int currentIndex = Array.IndexOf(items, currentItem);
|
|
currentIndex = CustomPopup(rect, currentIndex, items, title);
|
|
return currentIndex >= 0 && currentIndex < items.Length ? items[currentIndex] : CVRCommon.NONE_OR_EMPTY;
|
|
}
|
|
|
|
public static int CustomPopup(Rect rect, string label, int currentItem, string[] items, string title = null)
|
|
{
|
|
int controlId = GUIUtility.GetControlID(FocusType.Passive);
|
|
|
|
// Use label width to position the dropdown appropriately
|
|
float labelWidth = EditorGUIUtility.labelWidth;
|
|
|
|
Rect labelRect = new Rect(rect.x, rect.y, labelWidth, rect.height);
|
|
Rect buttonRect = new Rect(rect.x + labelWidth, rect.y, rect.width - labelWidth, rect.height);
|
|
|
|
EditorGUI.LabelField(labelRect, label);
|
|
|
|
string current = currentItem >= 0 && currentItem < items.Length ? items[currentItem] : CVRCommon.NONE_OR_EMPTY;
|
|
if (EditorGUI.DropdownButton(buttonRect, new GUIContent(current), FocusType.Passive))
|
|
{
|
|
GUIUtility.keyboardControl = controlId;
|
|
new CustomAdvancedDropdown(new AdvancedDropdownState(), items, title, selected =>
|
|
{
|
|
s_lastSelectedValue = selected;
|
|
}).Show(buttonRect); // We'll show the dropdown right where our buttonRect is
|
|
}
|
|
|
|
if (controlId == GUIUtility.keyboardControl && s_lastSelectedValue != -1)
|
|
{
|
|
currentItem = s_lastSelectedValue;
|
|
s_lastSelectedValue = -1;
|
|
}
|
|
|
|
return currentItem;
|
|
}
|
|
|
|
// CustomPopup but no label
|
|
public static int CustomPopup(Rect rect, int currentItem, string[] items, string title = null)
|
|
{
|
|
int controlId = GUIUtility.GetControlID(FocusType.Passive);
|
|
|
|
Rect buttonRect = new Rect(rect.x, rect.y, rect.width, rect.height);
|
|
|
|
string current = currentItem >= 0 && currentItem < items.Length ? items[currentItem] : CVRCommon.NONE_OR_EMPTY;
|
|
if (EditorGUI.DropdownButton(buttonRect, new GUIContent(current), FocusType.Passive))
|
|
{
|
|
GUIUtility.keyboardControl = controlId;
|
|
new CustomAdvancedDropdown(new AdvancedDropdownState(), items, title, selected =>
|
|
{
|
|
s_lastSelectedValue = selected;
|
|
}).Show(buttonRect); // We'll show the dropdown right where our buttonRect is
|
|
}
|
|
|
|
if (controlId == GUIUtility.keyboardControl && s_lastSelectedValue != -1)
|
|
{
|
|
currentItem = s_lastSelectedValue;
|
|
s_lastSelectedValue = -1;
|
|
}
|
|
|
|
return currentItem;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region CustomDropdown
|
|
|
|
public static string CustomDropdown(
|
|
Rect rect,
|
|
string label,
|
|
string currentItem,
|
|
string[] items,
|
|
string title = null)
|
|
{
|
|
GUIContent defaultButtonContent = items.Contains(currentItem)
|
|
? GetCachedIconContent("d_Search Icon")
|
|
: GetCachedIconContent("console.erroricon.sml");
|
|
|
|
return CustomDropdown(rect, label, currentItem, items, defaultButtonContent, DEFAULT_DROPDOWN_BUTTON_WIDTH, title);
|
|
}
|
|
|
|
public static string CustomDropdown(
|
|
Rect rect,
|
|
string label,
|
|
string currentItem,
|
|
string[] items,
|
|
GUIContent buttonContent,
|
|
string title = null)
|
|
{
|
|
return CustomDropdown(rect, label, currentItem, items, buttonContent, DEFAULT_DROPDOWN_BUTTON_WIDTH, title);
|
|
}
|
|
|
|
private static string CustomDropdown(
|
|
Rect rect,
|
|
string label,
|
|
string currentItem,
|
|
string[] items,
|
|
GUIContent buttonContent,
|
|
float dropdownButtonWidth,
|
|
string title)
|
|
{
|
|
float labelWidth = 2f + EditorGUIUtility.labelWidth;
|
|
float indentOffset = 15 * EditorGUI.indentLevel;
|
|
|
|
Rect textFieldRect = new Rect(rect.x + labelWidth - indentOffset, rect.y, rect.width - labelWidth + indentOffset - dropdownButtonWidth, rect.height);
|
|
Rect buttonRect = new Rect(rect.x + rect.width - dropdownButtonWidth, rect.y, dropdownButtonWidth, rect.height);
|
|
Rect dropdownRect = new Rect(rect.x + labelWidth, rect.y, rect.width - labelWidth, rect.height);
|
|
|
|
EditorGUI.LabelField(new Rect(rect.x, rect.y, labelWidth, rect.height), label);
|
|
currentItem = EditorGUI.TextField(textFieldRect, currentItem);
|
|
|
|
return CustomDropdown(currentItem, items, title, buttonRect, dropdownRect, buttonContent);
|
|
}
|
|
|
|
private static string CustomDropdown(
|
|
Rect rect,
|
|
string currentItem,
|
|
string[] items,
|
|
GUIContent buttonContent,
|
|
float dropdownButtonWidth,
|
|
string title)
|
|
{
|
|
float indentOffset = 15 * EditorGUI.indentLevel;
|
|
Rect textFieldRect = new Rect(rect.x - indentOffset, rect.y, rect.width + indentOffset - dropdownButtonWidth, rect.height);
|
|
Rect buttonRect = new Rect(rect.x + rect.width - dropdownButtonWidth, rect.y, dropdownButtonWidth, rect.height);
|
|
Rect dropdownRect = new Rect(rect.x, rect.y, rect.width, rect.height);
|
|
|
|
currentItem = EditorGUI.TextField(textFieldRect, currentItem);
|
|
return CustomDropdown(currentItem, items, title, buttonRect, dropdownRect, buttonContent);
|
|
}
|
|
|
|
private static string CustomDropdown(
|
|
string currentItem,
|
|
string[] items,
|
|
string title,
|
|
Rect buttonRect,
|
|
Rect dropdownRect,
|
|
GUIContent buttonContent)
|
|
{
|
|
int controlId = GUIUtility.GetControlID(FocusType.Passive);
|
|
|
|
if (EditorGUI.DropdownButton(buttonRect, buttonContent, FocusType.Passive))
|
|
{
|
|
GUIUtility.keyboardControl = controlId;
|
|
new CustomAdvancedDropdown(new AdvancedDropdownState(), items, title, selected =>
|
|
{
|
|
s_lastSelectedValue = selected;
|
|
}).Show(dropdownRect);
|
|
}
|
|
|
|
if (controlId == GUIUtility.keyboardControl && s_lastSelectedValue != -1)
|
|
{
|
|
currentItem = items[s_lastSelectedValue];
|
|
s_lastSelectedValue = -1;
|
|
}
|
|
|
|
return currentItem;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Private Methods
|
|
|
|
public static GUIContent GetCachedIconContent(string iconName)
|
|
{
|
|
if (s_iconContentCache.TryGetValue(iconName, out GUIContent iconContent))
|
|
return iconContent;
|
|
|
|
iconContent = EditorGUIUtility.IconContent(iconName);
|
|
s_iconContentCache[iconName] = iconContent;
|
|
return iconContent;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Custom GUI
|
|
|
|
private class CustomAdvancedDropdown : AdvancedDropdown
|
|
{
|
|
private readonly Action<int> _onItemSelected;
|
|
private readonly string[] _items;
|
|
private readonly string _title;
|
|
|
|
internal CustomAdvancedDropdown(AdvancedDropdownState state, string[] items, string title, Action<int> onItemSelected) : base(state)
|
|
{
|
|
_items = items ?? Array.Empty<string>();
|
|
_title = title ?? "Items";
|
|
_onItemSelected = onItemSelected;
|
|
}
|
|
|
|
internal new void Show(Rect rect)
|
|
{
|
|
minimumSize = new Vector2(rect.width, 200f);
|
|
|
|
// We have to set the maximumSize through reflection, cause unity is cool
|
|
typeof(AdvancedDropdown).GetProperty("maximumSize", BindingFlags.NonPublic | BindingFlags.Instance)
|
|
?.SetValue(this, new Vector2(rect.width, 400f));
|
|
|
|
base.Show(rect);
|
|
}
|
|
|
|
protected override AdvancedDropdownItem BuildRoot()
|
|
{
|
|
AdvancedDropdownItem root = new AdvancedDropdownItem(_title);
|
|
|
|
for (var itemIdx = 0; itemIdx < _items.Length; itemIdx++)
|
|
{
|
|
var item = _items[itemIdx];
|
|
AdvancedDropdownItem current = root;
|
|
string[] parts = item.Split('/');
|
|
|
|
for (int i = 0; i < parts.Length; i++)
|
|
{
|
|
string name = parts[i];
|
|
AdvancedDropdownItem child = current.children.FirstOrDefault(c => c.name == name);
|
|
|
|
if (child == null)
|
|
{
|
|
child = new AdvancedDropdownItem(name)
|
|
{
|
|
id = itemIdx // id is based on itteration order
|
|
};
|
|
if (name == item && name == _title)
|
|
child.icon = GetCachedIconContent("d_FilterSelectedOnly").image as Texture2D;
|
|
|
|
current.AddChild(child);
|
|
}
|
|
|
|
current = child;
|
|
}
|
|
}
|
|
|
|
return root;
|
|
}
|
|
|
|
protected override void ItemSelected(AdvancedDropdownItem item)
|
|
{
|
|
base.ItemSelected(item);
|
|
_onItemSelected?.Invoke(item.id);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|
|
#endif |