This commit is contained in:
2025-01-17 13:10:42 +01:00
commit 4536213c91
15115 changed files with 1442174 additions and 0 deletions

View File

@@ -0,0 +1,78 @@
#if UNITY_EDITOR
using System.Collections.Generic;
namespace UnityEngine.InputSystem.Editor
{
internal abstract class AdvancedDropdown
{
protected Vector2 minimumSize { get; set; }
protected Vector2 maximumSize { get; set; }
internal AdvancedDropdownWindow m_WindowInstance;
internal AdvancedDropdownState m_State;
internal AdvancedDropdownDataSource m_DataSource;
internal AdvancedDropdownGUI m_Gui;
public AdvancedDropdown(AdvancedDropdownState state)
{
m_State = state;
}
public void Show(Rect rect)
{
if (m_WindowInstance != null)
{
m_WindowInstance.Close();
m_WindowInstance = null;
}
if (m_DataSource == null)
{
m_DataSource = new CallbackDataSource(BuildRoot, BuildCustomSearch);
}
if (m_Gui == null)
{
m_Gui = new AdvancedDropdownGUI();
}
m_WindowInstance = ScriptableObject.CreateInstance<AdvancedDropdownWindow>();
if (minimumSize != Vector2.zero)
m_WindowInstance.minSize = minimumSize;
if (maximumSize != Vector2.zero)
m_WindowInstance.maxSize = maximumSize;
m_WindowInstance.state = m_State;
m_WindowInstance.dataSource = m_DataSource;
m_WindowInstance.gui = m_Gui;
m_WindowInstance.windowClosed +=
w => { ItemSelected(w.GetSelectedItem()); };
m_WindowInstance.windowDestroyed += OnDestroy;
m_WindowInstance.Init(rect);
}
public void Reload()
{
m_WindowInstance?.ReloadData();
}
public void Repaint()
{
m_WindowInstance?.Repaint();
}
protected abstract AdvancedDropdownItem BuildRoot();
protected virtual AdvancedDropdownItem BuildCustomSearch(string searchString,
IEnumerable<AdvancedDropdownItem> elements)
{
return null;
}
protected virtual void ItemSelected(AdvancedDropdownItem item)
{
}
protected virtual void OnDestroy()
{
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4b0617d4946754ab980342582b1f560c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,133 @@
#if UNITY_EDITOR
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
namespace UnityEngine.InputSystem.Editor
{
internal abstract class AdvancedDropdownDataSource
{
private static readonly string kSearchHeader = L10n.Tr("Search");
public AdvancedDropdownItem mainTree { get; private set; }
public AdvancedDropdownItem searchTree { get; private set; }
public List<int> selectedIDs { get; } = new List<int>();
protected AdvancedDropdownItem root => mainTree;
protected List<AdvancedDropdownItem> m_SearchableElements;
public void ReloadData()
{
mainTree = FetchData();
}
protected abstract AdvancedDropdownItem FetchData();
public void RebuildSearch(string search)
{
searchTree = Search(search);
}
protected bool AddMatchItem(AdvancedDropdownItem e, string name, string[] searchWords, List<AdvancedDropdownItem> matchesStart, List<AdvancedDropdownItem> matchesWithin)
{
var didMatchAll = true;
var didMatchStart = false;
// See if we match ALL the search words.
for (var w = 0; w < searchWords.Length; w++)
{
var search = searchWords[w];
if (name.Contains(search))
{
// If the start of the item matches the first search word, make a note of that.
if (w == 0 && name.StartsWith(search))
didMatchStart = true;
}
else
{
// As soon as any word is not matched, we disregard this item.
didMatchAll = false;
break;
}
}
// We always need to match all search words.
// If we ALSO matched the start, this item gets priority.
if (didMatchAll)
{
if (didMatchStart)
matchesStart.Add(e);
else
matchesWithin.Add(e);
}
return didMatchAll;
}
protected virtual AdvancedDropdownItem PerformCustomSearch(string searchString)
{
return null;
}
protected virtual AdvancedDropdownItem Search(string searchString)
{
if (m_SearchableElements == null)
{
BuildSearchableElements();
}
if (string.IsNullOrEmpty(searchString))
return null;
var searchTree = PerformCustomSearch(searchString);
if (searchTree == null)
{
// Support multiple search words separated by spaces.
var searchWords = searchString.ToLower().Split(' ');
// We keep two lists. Matches that matches the start of an item always get first priority.
var matchesStart = new List<AdvancedDropdownItem>();
var matchesWithin = new List<AdvancedDropdownItem>();
foreach (var e in m_SearchableElements)
{
var name = e.searchableName.ToLower().Replace(" ", "");
AddMatchItem(e, name, searchWords, matchesStart, matchesWithin);
}
searchTree = new AdvancedDropdownItem(kSearchHeader);
matchesStart.Sort();
foreach (var element in matchesStart)
{
searchTree.AddChild(element);
}
matchesWithin.Sort();
foreach (var element in matchesWithin)
{
searchTree.AddChild(element);
}
}
return searchTree;
}
private void BuildSearchableElements()
{
m_SearchableElements = new List<AdvancedDropdownItem>();
BuildSearchableElements(root);
}
private void BuildSearchableElements(AdvancedDropdownItem item)
{
if (!item.children.Any())
{
if (!item.IsSeparator())
m_SearchableElements.Add(item);
return;
}
foreach (var child in item.children)
{
BuildSearchableElements(child);
}
}
}
}
#endif // UNITY_EDITOR

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0fb454a124e8649bb9326261b1739032
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,259 @@
#if UNITY_EDITOR
using System;
using System.Linq;
using UnityEditor;
using UnityEditor.IMGUI.Controls;
namespace UnityEngine.InputSystem.Editor
{
internal class AdvancedDropdownGUI
{
private static class Styles
{
public static readonly GUIStyle toolbarSearchField = EditorStyles.toolbarSearchField;
public static readonly GUIStyle itemStyle = new GUIStyle("PR Label")
.WithAlignment(TextAnchor.MiddleLeft)
.WithPadding(new RectOffset())
.WithMargin(new RectOffset())
.WithFixedHeight(17);
public static readonly GUIStyle richTextItemStyle = new GUIStyle("PR Label")
.WithAlignment(TextAnchor.MiddleLeft)
.WithPadding(new RectOffset())
.WithMargin(new RectOffset())
.WithFixedHeight(17)
.WithRichText();
public static readonly GUIStyle header = new GUIStyle("In BigTitle")
.WithFont(EditorStyles.boldLabel.font)
.WithMargin(new RectOffset())
.WithBorder(new RectOffset(0, 0, 3, 3))
.WithPadding(new RectOffset(6, 6, 6, 6))
.WithContentOffset(Vector2.zero);
public static readonly GUIStyle headerArrow = new GUIStyle()
.WithAlignment(TextAnchor.MiddleCenter)
.WithFontSize(20)
.WithNormalTextColor(Color.gray);
public static readonly GUIStyle checkMark = new GUIStyle("PR Label")
.WithAlignment(TextAnchor.MiddleCenter)
.WithPadding(new RectOffset())
.WithMargin(new RectOffset())
.WithFixedHeight(17);
public static readonly GUIContent arrowRightContent = new GUIContent("▸");
public static readonly GUIContent arrowLeftContent = new GUIContent("◂");
}
//This should ideally match line height
private static readonly Vector2 s_IconSize = new Vector2(13, 13);
internal Rect m_SearchRect;
internal Rect m_HeaderRect;
private bool m_FocusSet;
internal virtual float searchHeight => m_SearchRect.height;
internal virtual float headerHeight => m_HeaderRect.height;
internal virtual GUIStyle lineStyle => Styles.itemStyle;
internal virtual GUIStyle richTextLineStyle => Styles.richTextItemStyle;
internal GUIStyle headerStyle => Styles.header;
internal virtual Vector2 iconSize => s_IconSize;
internal AdvancedDropdownState state { get; set; }
private readonly SearchField m_SearchField = new SearchField();
public void Init()
{
m_FocusSet = false;
}
private const float k_IndentPerLevel = 20f;
internal virtual void BeginDraw(EditorWindow window)
{
}
internal virtual void EndDraw(EditorWindow window)
{
}
internal virtual void DrawItem(AdvancedDropdownItem item, string name, Texture2D icon, bool enabled,
bool drawArrow, bool selected, bool hasSearch, bool richText = false)
{
var content = new GUIContent(name, icon);
var imgTemp = content.image;
//we need to pretend we have an icon to calculate proper width in case
if (content.image == null)
content.image = Texture2D.whiteTexture;
var style = richText ? richTextLineStyle : lineStyle;
var rect = GUILayoutUtility.GetRect(content, style, GUILayout.ExpandWidth(true));
content.image = imgTemp;
if (Event.current.type != EventType.Repaint)
return;
style.Draw(rect, GUIContent.none, false, false, selected, selected);
if (!hasSearch)
{
rect.x += item.indent * k_IndentPerLevel;
rect.width -= item.indent * k_IndentPerLevel;
}
var imageTemp = content.image;
if (content.image == null)
{
style.Draw(rect, GUIContent.none, false, false, selected, selected);
rect.x += iconSize.x + 1;
rect.width -= iconSize.x + 1;
}
rect.x += EditorGUIUtility.standardVerticalSpacing;
rect.width -= EditorGUIUtility.standardVerticalSpacing;
EditorGUI.BeginDisabledGroup(!enabled);
style.Draw(rect, content, false, false, selected, selected);
content.image = imageTemp;
if (drawArrow)
{
var size = style.lineHeight;
var arrowRect = new Rect(rect.x + rect.width - size, rect.y, size, size);
style.Draw(arrowRect, Styles.arrowRightContent, false, false, false, false);
}
EditorGUI.EndDisabledGroup();
}
internal virtual void DrawHeader(AdvancedDropdownItem group, Action backButtonPressed, bool hasParent)
{
var content = new GUIContent(group.name, group.icon);
m_HeaderRect = GUILayoutUtility.GetRect(content, Styles.header, GUILayout.ExpandWidth(true));
if (Event.current.type == EventType.Repaint)
Styles.header.Draw(m_HeaderRect, content, false, false, false, false);
// Back button
if (hasParent)
{
var arrowWidth = 13;
var arrowRect = new Rect(m_HeaderRect.x, m_HeaderRect.y, arrowWidth, m_HeaderRect.height);
if (Event.current.type == EventType.Repaint)
Styles.headerArrow.Draw(arrowRect, Styles.arrowLeftContent, false, false, false, false);
if (Event.current.type == EventType.MouseDown && m_HeaderRect.Contains(Event.current.mousePosition))
{
backButtonPressed();
Event.current.Use();
}
}
}
internal virtual void DrawFooter(AdvancedDropdownItem selectedItem)
{
}
internal void DrawSearchField(bool isSearchFieldDisabled, string searchString, Action<string> searchChanged)
{
if (!isSearchFieldDisabled && !m_FocusSet)
{
m_FocusSet = true;
m_SearchField.SetFocus();
}
using (new EditorGUI.DisabledScope(isSearchFieldDisabled))
{
var newSearch = DrawSearchFieldControl(searchString);
if (newSearch != searchString)
{
searchChanged(newSearch);
}
}
}
internal virtual string DrawSearchFieldControl(string searchString)
{
var paddingX = 8f;
var paddingY = 2f;
var rect = GUILayoutUtility.GetRect(0, 0, Styles.toolbarSearchField);
//rect.x += paddingX;
rect.y += paddingY + 1; // Add one for the border
rect.height += Styles.toolbarSearchField.fixedHeight + paddingY * 3;
rect.width -= paddingX;// * 2;
m_SearchRect = rect;
searchString = m_SearchField.OnToolbarGUI(m_SearchRect, searchString);
return searchString;
}
internal Rect GetAnimRect(Rect position, float anim)
{
// Calculate rect for animated area
var rect = new Rect(position);
rect.x = position.x + position.width * anim;
rect.y += searchHeight;
rect.height -= searchHeight;
return rect;
}
internal Vector2 CalculateContentSize(AdvancedDropdownDataSource dataSource)
{
var maxWidth = 0f;
var maxHeight = 0f;
var includeArrow = false;
var arrowWidth = 0f;
foreach (var child in dataSource.mainTree.children)
{
var content = new GUIContent(child.name, child.icon);
var a = lineStyle.CalcSize(content);
a.x += iconSize.x + 1;
if (maxWidth < a.x)
{
maxWidth = a.x + 1;
includeArrow |= child.children.Any();
}
if (child.IsSeparator())
{
maxHeight += GUIHelpers.Styles.lineSeparator.CalcHeight(content, maxWidth) + GUIHelpers.Styles.lineSeparator.margin.vertical;
}
else
{
maxHeight += lineStyle.CalcHeight(content, maxWidth);
}
if (arrowWidth == 0)
{
lineStyle.CalcMinMaxWidth(Styles.arrowRightContent, out arrowWidth, out arrowWidth);
}
}
if (includeArrow)
{
maxWidth += arrowWidth;
}
return new Vector2(maxWidth, maxHeight);
}
internal float GetSelectionHeight(AdvancedDropdownDataSource dataSource, Rect buttonRect)
{
if (state.GetSelectedIndex(dataSource.mainTree) == -1)
return 0;
var height = 0f;
for (var i = 0; i < dataSource.mainTree.children.Count(); i++)
{
var child = dataSource.mainTree.children.ElementAt(i);
var content = new GUIContent(child.name, child.icon);
if (state.GetSelectedIndex(dataSource.mainTree) == i)
{
var diff = (lineStyle.CalcHeight(content, 0) - buttonRect.height) / 2f;
return height + diff;
}
if (child.IsSeparator())
{
height += GUIHelpers.Styles.lineSeparator.CalcHeight(content, 0) + GUIHelpers.Styles.lineSeparator.margin.vertical;
}
else
{
height += lineStyle.CalcHeight(content, 0);
}
}
return height;
}
}
}
#endif // UNITY_EDITOR

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6109a857ac56b4017b44b06a9de0f827
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,75 @@
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
namespace UnityEngine.InputSystem.Editor
{
internal class AdvancedDropdownItem : IComparable
{
internal readonly List<AdvancedDropdownItem> m_Children = new List<AdvancedDropdownItem>();
public string name { get; set; }
public Texture2D icon { get; set; }
public int id { get; set; }
public bool enabled { get; set; } = true;
public int indent { get; set; }
internal int elementIndex { get; set; } = -1;
public IEnumerable<AdvancedDropdownItem> children => m_Children;
protected string m_SearchableName;
public virtual string searchableName => string.IsNullOrEmpty(m_SearchableName) ? name : m_SearchableName;
public void AddChild(AdvancedDropdownItem child)
{
m_Children.Add(child);
}
public int GetIndexOfChild(AdvancedDropdownItem child)
{
return m_Children.IndexOf(child);
}
static readonly AdvancedDropdownItem k_SeparatorItem = new SeparatorDropdownItem();
public AdvancedDropdownItem(string name)
{
this.name = name;
id = name.GetHashCode();
}
public virtual int CompareTo(object o)
{
return name.CompareTo((o as AdvancedDropdownItem).name);
}
public void AddSeparator(string label = null)
{
if (string.IsNullOrEmpty(label))
AddChild(k_SeparatorItem);
else
AddChild(new SeparatorDropdownItem(label));
}
internal bool IsSeparator()
{
return this is SeparatorDropdownItem;
}
public override string ToString()
{
return name;
}
private class SeparatorDropdownItem : AdvancedDropdownItem
{
public SeparatorDropdownItem(string label = "")
: base(label)
{
}
}
}
}
#endif // UNITY_EDITOR

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 42a7c5e6c9bc849a3a9163e719422c50
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,132 @@
#if UNITY_EDITOR
using System;
using System.Linq;
namespace UnityEngine.InputSystem.Editor
{
[Serializable]
internal class AdvancedDropdownState
{
[Serializable]
private class AdvancedDropdownItemState
{
public AdvancedDropdownItemState(AdvancedDropdownItem item)
{
itemId = item.id;
}
public int itemId;
public int selectedIndex = -1;
public Vector2 scroll;
}
[SerializeField]
private AdvancedDropdownItemState[] states = new AdvancedDropdownItemState[0];
private AdvancedDropdownItemState m_LastSelectedState;
private AdvancedDropdownItemState GetStateForItem(AdvancedDropdownItem item)
{
if (m_LastSelectedState != null && m_LastSelectedState.itemId == item.id)
return m_LastSelectedState;
for (int i = 0; i < states.Length; i++)
{
if (states[i].itemId == item.id)
{
m_LastSelectedState = states[i];
return m_LastSelectedState;
}
}
Array.Resize(ref states, states.Length + 1);
states[states.Length - 1] = new AdvancedDropdownItemState(item);
m_LastSelectedState = states[states.Length - 1];
return states[states.Length - 1];
}
internal void MoveDownSelection(AdvancedDropdownItem item)
{
var state = GetStateForItem(item);
var selectedIndex = state.selectedIndex;
do
{
++selectedIndex;
}
while (selectedIndex < item.children.Count() && item.children.ElementAt(selectedIndex).IsSeparator());
if (selectedIndex >= item.children.Count())
selectedIndex = 0;
if (selectedIndex < item.children.Count())
SetSelectionOnItem(item, selectedIndex);
}
internal void MoveUpSelection(AdvancedDropdownItem item)
{
var state = GetStateForItem(item);
var selectedIndex = state.selectedIndex;
do
{
--selectedIndex;
}
while (selectedIndex >= 0 && item.children.ElementAt(selectedIndex).IsSeparator());
if (selectedIndex < 0)
selectedIndex = item.children.Count() - 1;
if (selectedIndex >= 0)
SetSelectionOnItem(item, selectedIndex);
}
internal void SetSelectionOnItem(AdvancedDropdownItem item, int selectedIndex)
{
var state = GetStateForItem(item);
if (selectedIndex < 0)
{
state.selectedIndex = 0;
}
else if (selectedIndex >= item.children.Count())
{
state.selectedIndex = item.children.Count() - 1;
}
else
{
state.selectedIndex = selectedIndex;
}
}
internal void ClearSelectionOnItem(AdvancedDropdownItem item)
{
GetStateForItem(item).selectedIndex = -1;
}
internal int GetSelectedIndex(AdvancedDropdownItem item)
{
return GetStateForItem(item).selectedIndex;
}
internal void SetSelectedIndex(AdvancedDropdownItem item, int index)
{
GetStateForItem(item).selectedIndex = index;
}
internal AdvancedDropdownItem GetSelectedChild(AdvancedDropdownItem item)
{
var index = GetSelectedIndex(item);
if (!item.children.Any() || index < 0 || index >= item.children.Count())
return null;
return item.children.ElementAt(index);
}
internal Vector2 GetScrollState(AdvancedDropdownItem item)
{
return GetStateForItem(item).scroll;
}
internal void SetScrollState(AdvancedDropdownItem item, Vector2 scrollState)
{
GetStateForItem(item).scroll = scrollState;
}
}
}
#endif // UNITY_EDITOR

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f61b0e35037644f37b6ac81513577c50
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c46ed0dd23ba548fd87436e8c89334e9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,32 @@
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
namespace UnityEngine.InputSystem.Editor
{
internal class CallbackDataSource : AdvancedDropdownDataSource
{
private readonly Func<AdvancedDropdownItem> m_BuildCallback;
private readonly Func<string, IEnumerable<AdvancedDropdownItem>, AdvancedDropdownItem>
m_SearchCallback;
internal CallbackDataSource(Func<AdvancedDropdownItem> buildCallback,
Func<string, IEnumerable<AdvancedDropdownItem>, AdvancedDropdownItem> searchCallback = null)
{
m_BuildCallback = buildCallback;
m_SearchCallback = searchCallback;
}
protected override AdvancedDropdownItem FetchData()
{
return m_BuildCallback();
}
protected override AdvancedDropdownItem PerformCustomSearch(string searchString)
{
return m_SearchCallback?.Invoke(searchString, m_SearchableElements);
}
}
}
#endif // UNITY_EDITOR

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2fe2cf22711ac4db3b094a955603f8bd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,86 @@
#if UNITY_EDITOR
using System.Collections.Generic;
using System.Linq;
namespace UnityEngine.InputSystem.Editor
{
internal class MultiLevelDataSource : AdvancedDropdownDataSource
{
private string[] m_DisplayedOptions;
internal string[] displayedOptions
{
set { m_DisplayedOptions = value; }
}
private string m_Label = "";
internal string label
{
set { m_Label = value; }
}
internal MultiLevelDataSource()
{
}
public MultiLevelDataSource(string[] displayOptions)
{
m_DisplayedOptions = displayOptions;
}
protected override AdvancedDropdownItem FetchData()
{
var rootGroup = new AdvancedDropdownItem(m_Label);
m_SearchableElements = new List<AdvancedDropdownItem>();
for (int i = 0; i < m_DisplayedOptions.Length; i++)
{
var menuPath = m_DisplayedOptions[i];
var paths = menuPath.Split('/');
AdvancedDropdownItem parent = rootGroup;
for (var j = 0; j < paths.Length; j++)
{
var path = paths[j];
if (j == paths.Length - 1)
{
var element = new MultiLevelItem(path, menuPath);
element.elementIndex = i;
parent.AddChild(element);
m_SearchableElements.Add(element);
continue;
}
var groupPathId = paths[0];
for (int k = 1; k <= j; k++)
groupPathId += "/" + paths[k];
var group = parent.children.SingleOrDefault(c => ((MultiLevelItem)c).stringId == groupPathId);
if (group == null)
{
group = new MultiLevelItem(path, groupPathId);
parent.AddChild(group);
}
parent = group;
}
}
return rootGroup;
}
class MultiLevelItem : AdvancedDropdownItem
{
internal string stringId;
public MultiLevelItem(string path, string menuPath) : base(path)
{
stringId = menuPath;
id = menuPath.GetHashCode();
}
public override string ToString()
{
return stringId;
}
}
}
}
#endif // UNITY_EDITOR

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: caeab4c8826564dfda5e44a7e01a8de1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: