UI 자동화를 위해 바인딩 기능 구현
- 유니티 에셋 인증 오류로 meta 재생성
This commit is contained in:
@@ -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
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4b0617d4946754ab980342582b1f560c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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.ToLowerInvariant().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.ToLowerInvariant().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
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0fb454a124e8649bb9326261b1739032
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6109a857ac56b4017b44b06a9de0f827
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 42a7c5e6c9bc849a3a9163e719422c50
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f61b0e35037644f37b6ac81513577c50
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,574 @@
|
||||
#if UNITY_EDITOR
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Callbacks;
|
||||
|
||||
namespace UnityEngine.InputSystem.Editor
|
||||
{
|
||||
internal class AdvancedDropdownWindow : EditorWindow
|
||||
{
|
||||
private static readonly float kBorderThickness = 1f;
|
||||
private static readonly float kRightMargin = 13f;
|
||||
|
||||
private AdvancedDropdownGUI m_Gui;
|
||||
private AdvancedDropdownDataSource m_DataSource;
|
||||
private AdvancedDropdownState m_State;
|
||||
|
||||
private AdvancedDropdownItem m_CurrentlyRenderedTree;
|
||||
|
||||
protected AdvancedDropdownItem renderedTreeItem => m_CurrentlyRenderedTree;
|
||||
|
||||
private AdvancedDropdownItem m_AnimationTree;
|
||||
private float m_NewAnimTarget;
|
||||
private long m_LastTime;
|
||||
private bool m_ScrollToSelected = true;
|
||||
private float m_InitialSelectionPosition;
|
||||
////FIXME: looks like a bug?
|
||||
#pragma warning disable CS0649
|
||||
private Rect m_ButtonRectScreenPos;
|
||||
private Stack<AdvancedDropdownItem> m_ViewsStack = new Stack<AdvancedDropdownItem>();
|
||||
private bool m_DirtyList = true;
|
||||
|
||||
private string m_Search = "";
|
||||
private bool hasSearch => !string.IsNullOrEmpty(m_Search);
|
||||
|
||||
protected internal string searchString
|
||||
{
|
||||
get => m_Search;
|
||||
set
|
||||
{
|
||||
var isNewSearch = string.IsNullOrEmpty(m_Search) && !string.IsNullOrEmpty(value);
|
||||
m_Search = value;
|
||||
m_DataSource.RebuildSearch(m_Search);
|
||||
m_CurrentlyRenderedTree = m_DataSource.mainTree;
|
||||
if (hasSearch)
|
||||
{
|
||||
m_CurrentlyRenderedTree = m_DataSource.searchTree;
|
||||
if (isNewSearch || state.GetSelectedIndex(m_CurrentlyRenderedTree) < 0)
|
||||
state.SetSelectedIndex(m_CurrentlyRenderedTree, 0);
|
||||
m_ViewsStack.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal bool m_ShowHeader = true;
|
||||
internal bool showHeader
|
||||
{
|
||||
get => m_ShowHeader;
|
||||
set => m_ShowHeader = value;
|
||||
}
|
||||
internal bool m_Searchable = true;
|
||||
internal bool searchable
|
||||
{
|
||||
get => m_Searchable;
|
||||
set => m_Searchable = value;
|
||||
}
|
||||
internal bool m_closeOnSelection = true;
|
||||
internal bool closeOnSelection
|
||||
{
|
||||
get => m_closeOnSelection;
|
||||
set => m_closeOnSelection = value;
|
||||
}
|
||||
|
||||
protected virtual bool isSearchFieldDisabled { get; set; }
|
||||
|
||||
protected bool m_SetInitialSelectionPosition = true;
|
||||
|
||||
public AdvancedDropdownWindow()
|
||||
{
|
||||
m_InitialSelectionPosition = 0f;
|
||||
}
|
||||
|
||||
protected virtual bool setInitialSelectionPosition => m_SetInitialSelectionPosition;
|
||||
|
||||
protected internal AdvancedDropdownState state
|
||||
{
|
||||
get => m_State;
|
||||
set => m_State = value;
|
||||
}
|
||||
|
||||
protected internal AdvancedDropdownGUI gui
|
||||
{
|
||||
get => m_Gui;
|
||||
set => m_Gui = value;
|
||||
}
|
||||
|
||||
protected internal AdvancedDropdownDataSource dataSource
|
||||
{
|
||||
get => m_DataSource;
|
||||
set => m_DataSource = value;
|
||||
}
|
||||
|
||||
public event Action<AdvancedDropdownWindow> windowClosed;
|
||||
public event Action windowDestroyed;
|
||||
public event Action<AdvancedDropdownItem> selectionChanged;
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
m_DirtyList = true;
|
||||
}
|
||||
|
||||
protected virtual void OnDestroy()
|
||||
{
|
||||
// This window sets 'editingTextField = true' continuously, through EditorGUI.FocusTextInControl(),
|
||||
// for the searchfield in its AdvancedDropdownGUI so here we ensure to clean up. This fixes the issue that
|
||||
// EditorGUI.IsEditingTextField() was returning true after e.g the Add Component Menu closes
|
||||
EditorGUIUtility.editingTextField = false;
|
||||
GUIUtility.keyboardControl = 0;
|
||||
windowDestroyed?.Invoke();
|
||||
}
|
||||
|
||||
public static T CreateAndInit<T>(Rect rect, AdvancedDropdownState state) where T : AdvancedDropdownWindow
|
||||
{
|
||||
var instance = CreateInstance<T>();
|
||||
instance.m_State = state;
|
||||
instance.Init(rect);
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void Init(Rect buttonRect)
|
||||
{
|
||||
var screenPoint = GUIUtility.GUIToScreenPoint(new Vector2(buttonRect.x, buttonRect.y));
|
||||
m_ButtonRectScreenPos.x = screenPoint.x;
|
||||
m_ButtonRectScreenPos.y = screenPoint.y;
|
||||
|
||||
if (m_State == null)
|
||||
m_State = new AdvancedDropdownState();
|
||||
if (m_DataSource == null)
|
||||
m_DataSource = new MultiLevelDataSource();
|
||||
if (m_Gui == null)
|
||||
m_Gui = new AdvancedDropdownGUI();
|
||||
m_Gui.state = m_State;
|
||||
m_Gui.Init();
|
||||
|
||||
// Has to be done before calling Show / ShowWithMode
|
||||
screenPoint = GUIUtility.GUIToScreenPoint(new Vector2(buttonRect.x, buttonRect.y));
|
||||
buttonRect.x = screenPoint.x;
|
||||
buttonRect.y = screenPoint.y;
|
||||
|
||||
OnDirtyList();
|
||||
m_CurrentlyRenderedTree = hasSearch ? m_DataSource.searchTree : m_DataSource.mainTree;
|
||||
ShowAsDropDown(buttonRect, CalculateWindowSize(m_ButtonRectScreenPos, out var requiredDropdownSize));
|
||||
|
||||
// If the dropdown is as height as the screen height, give it some margin
|
||||
if (position.height < requiredDropdownSize.y)
|
||||
{
|
||||
var pos = position;
|
||||
pos.y += 5;
|
||||
pos.height -= 10;
|
||||
position = pos;
|
||||
}
|
||||
|
||||
if (setInitialSelectionPosition)
|
||||
{
|
||||
m_InitialSelectionPosition = m_Gui.GetSelectionHeight(m_DataSource, buttonRect);
|
||||
}
|
||||
|
||||
wantsMouseMove = true;
|
||||
SetSelectionFromState();
|
||||
}
|
||||
|
||||
void SetSelectionFromState()
|
||||
{
|
||||
var selectedIndex = m_State.GetSelectedIndex(m_CurrentlyRenderedTree);
|
||||
while (selectedIndex >= 0)
|
||||
{
|
||||
var child = m_State.GetSelectedChild(m_CurrentlyRenderedTree);
|
||||
if (child == null)
|
||||
break;
|
||||
selectedIndex = m_State.GetSelectedIndex(child);
|
||||
if (selectedIndex < 0)
|
||||
break;
|
||||
m_ViewsStack.Push(m_CurrentlyRenderedTree);
|
||||
m_CurrentlyRenderedTree = child;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual Vector2 CalculateWindowSize(Rect buttonRect, out Vector2 requiredDropdownSize)
|
||||
{
|
||||
requiredDropdownSize = m_Gui.CalculateContentSize(m_DataSource);
|
||||
// Add 1 pixel for each border
|
||||
requiredDropdownSize.x += kBorderThickness * 2;
|
||||
requiredDropdownSize.y += kBorderThickness * 2;
|
||||
requiredDropdownSize.x += kRightMargin;
|
||||
|
||||
requiredDropdownSize.y += m_Gui.searchHeight;
|
||||
|
||||
if (showHeader)
|
||||
{
|
||||
requiredDropdownSize.y += m_Gui.headerHeight;
|
||||
}
|
||||
|
||||
requiredDropdownSize.y = Mathf.Clamp(requiredDropdownSize.y, minSize.y, maxSize.y);
|
||||
|
||||
var adjustedButtonRect = buttonRect;
|
||||
adjustedButtonRect.y = 0;
|
||||
adjustedButtonRect.height = requiredDropdownSize.y;
|
||||
|
||||
// Stretch to the width of the button
|
||||
if (requiredDropdownSize.x < buttonRect.width)
|
||||
{
|
||||
requiredDropdownSize.x = buttonRect.width;
|
||||
}
|
||||
// Apply minimum size
|
||||
if (requiredDropdownSize.x < minSize.x)
|
||||
{
|
||||
requiredDropdownSize.x = minSize.x;
|
||||
}
|
||||
if (requiredDropdownSize.y < minSize.y)
|
||||
{
|
||||
requiredDropdownSize.y = minSize.y;
|
||||
}
|
||||
|
||||
return requiredDropdownSize;
|
||||
}
|
||||
|
||||
internal void OnGUI()
|
||||
{
|
||||
m_Gui.BeginDraw(this);
|
||||
|
||||
GUI.Label(new Rect(0, 0, position.width, position.height), GUIContent.none, Styles.background);
|
||||
|
||||
if (m_DirtyList)
|
||||
{
|
||||
OnDirtyList();
|
||||
}
|
||||
|
||||
HandleKeyboard();
|
||||
if (searchable)
|
||||
OnGUISearch();
|
||||
|
||||
if (m_NewAnimTarget != 0 && Event.current.type == EventType.Layout)
|
||||
{
|
||||
var now = DateTime.Now.Ticks;
|
||||
var deltaTime = (now - m_LastTime) / (float)TimeSpan.TicksPerSecond;
|
||||
m_LastTime = now;
|
||||
|
||||
m_NewAnimTarget = Mathf.MoveTowards(m_NewAnimTarget, 0, deltaTime * 4);
|
||||
|
||||
if (m_NewAnimTarget == 0)
|
||||
{
|
||||
m_AnimationTree = null;
|
||||
}
|
||||
Repaint();
|
||||
}
|
||||
|
||||
var anim = m_NewAnimTarget;
|
||||
// Smooth the animation
|
||||
anim = Mathf.Floor(anim) + Mathf.SmoothStep(0, 1, Mathf.Repeat(anim, 1));
|
||||
|
||||
if (anim == 0)
|
||||
{
|
||||
DrawDropdown(0, m_CurrentlyRenderedTree);
|
||||
}
|
||||
else if (anim < 0)
|
||||
{
|
||||
// Go to parent
|
||||
// m_NewAnimTarget goes -1 -> 0
|
||||
DrawDropdown(anim, m_CurrentlyRenderedTree);
|
||||
DrawDropdown(anim + 1, m_AnimationTree);
|
||||
}
|
||||
else // > 0
|
||||
{
|
||||
// Go to child
|
||||
// m_NewAnimTarget 1 -> 0
|
||||
DrawDropdown(anim - 1, m_AnimationTree);
|
||||
DrawDropdown(anim, m_CurrentlyRenderedTree);
|
||||
}
|
||||
|
||||
m_Gui.EndDraw(this);
|
||||
}
|
||||
|
||||
public void ReloadData()
|
||||
{
|
||||
OnDirtyList();
|
||||
}
|
||||
|
||||
private void OnDirtyList()
|
||||
{
|
||||
m_DirtyList = false;
|
||||
m_DataSource.ReloadData();
|
||||
if (hasSearch)
|
||||
{
|
||||
m_DataSource.RebuildSearch(searchString);
|
||||
if (state.GetSelectedIndex(m_CurrentlyRenderedTree) < 0)
|
||||
{
|
||||
state.SetSelectedIndex(m_CurrentlyRenderedTree, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGUISearch()
|
||||
{
|
||||
m_Gui.DrawSearchField(isSearchFieldDisabled, m_Search, (newSearch) =>
|
||||
{
|
||||
searchString = newSearch;
|
||||
});
|
||||
}
|
||||
|
||||
private void HandleKeyboard()
|
||||
{
|
||||
var evt = Event.current;
|
||||
if (evt.type == EventType.KeyDown)
|
||||
{
|
||||
// Special handling when in new script panel
|
||||
if (SpecialKeyboardHandling(evt))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Always do these
|
||||
if (evt.keyCode == KeyCode.DownArrow)
|
||||
{
|
||||
m_State.MoveDownSelection(m_CurrentlyRenderedTree);
|
||||
m_ScrollToSelected = true;
|
||||
evt.Use();
|
||||
}
|
||||
if (evt.keyCode == KeyCode.UpArrow)
|
||||
{
|
||||
m_State.MoveUpSelection(m_CurrentlyRenderedTree);
|
||||
m_ScrollToSelected = true;
|
||||
evt.Use();
|
||||
}
|
||||
if (evt.keyCode == KeyCode.Return || evt.keyCode == KeyCode.KeypadEnter)
|
||||
{
|
||||
var selected = m_State.GetSelectedChild(m_CurrentlyRenderedTree);
|
||||
if (selected != null)
|
||||
{
|
||||
if (selected.children.Any())
|
||||
{
|
||||
GoToChild();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (selectionChanged != null)
|
||||
{
|
||||
selectionChanged(m_State.GetSelectedChild(m_CurrentlyRenderedTree));
|
||||
}
|
||||
if (closeOnSelection)
|
||||
{
|
||||
CloseWindow();
|
||||
}
|
||||
}
|
||||
}
|
||||
evt.Use();
|
||||
}
|
||||
|
||||
// Do these if we're not in search mode
|
||||
if (!hasSearch)
|
||||
{
|
||||
if (evt.keyCode == KeyCode.LeftArrow || evt.keyCode == KeyCode.Backspace)
|
||||
{
|
||||
GoToParent();
|
||||
evt.Use();
|
||||
}
|
||||
if (evt.keyCode == KeyCode.RightArrow)
|
||||
{
|
||||
var idx = m_State.GetSelectedIndex(m_CurrentlyRenderedTree);
|
||||
if (idx > -1 && m_CurrentlyRenderedTree.children.ElementAt(idx).children.Any())
|
||||
{
|
||||
GoToChild();
|
||||
}
|
||||
evt.Use();
|
||||
}
|
||||
if (evt.keyCode == KeyCode.Escape)
|
||||
{
|
||||
Close();
|
||||
evt.Use();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CloseWindow()
|
||||
{
|
||||
windowClosed?.Invoke(this);
|
||||
Close();
|
||||
}
|
||||
|
||||
internal AdvancedDropdownItem GetSelectedItem()
|
||||
{
|
||||
return m_State.GetSelectedChild(m_CurrentlyRenderedTree);
|
||||
}
|
||||
|
||||
protected virtual bool SpecialKeyboardHandling(Event evt)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private void DrawDropdown(float anim, AdvancedDropdownItem group)
|
||||
{
|
||||
// Start of animated area (the part that moves left and right)
|
||||
var areaPosition = new Rect(0, 0, position.width, position.height);
|
||||
// Adjust to the frame
|
||||
areaPosition.x += kBorderThickness;
|
||||
areaPosition.y += kBorderThickness;
|
||||
areaPosition.height -= kBorderThickness * 2;
|
||||
areaPosition.width -= kBorderThickness * 2;
|
||||
|
||||
GUILayout.BeginArea(m_Gui.GetAnimRect(areaPosition, anim));
|
||||
// Header
|
||||
if (showHeader)
|
||||
m_Gui.DrawHeader(group, GoToParent, m_ViewsStack.Count > 0);
|
||||
|
||||
DrawList(group);
|
||||
GUILayout.EndArea();
|
||||
}
|
||||
|
||||
private void DrawList(AdvancedDropdownItem item)
|
||||
{
|
||||
// Start of scroll view list
|
||||
m_State.SetScrollState(item, GUILayout.BeginScrollView(m_State.GetScrollState(item), GUIStyle.none, GUI.skin.verticalScrollbar));
|
||||
EditorGUIUtility.SetIconSize(m_Gui.iconSize);
|
||||
Rect selectedRect = new Rect();
|
||||
for (var i = 0; i < item.children.Count(); i++)
|
||||
{
|
||||
var child = item.children.ElementAt(i);
|
||||
var selected = m_State.GetSelectedIndex(item) == i;
|
||||
|
||||
if (child.IsSeparator())
|
||||
{
|
||||
GUIHelpers.DrawLineSeparator(child.name);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Gui.DrawItem(child, child.name, child.icon, child.enabled, child.children.Any(), selected, hasSearch);
|
||||
}
|
||||
|
||||
var r = GUILayoutUtility.GetLastRect();
|
||||
if (selected)
|
||||
selectedRect = r;
|
||||
|
||||
// Skip input handling for the tree used for animation
|
||||
if (item != m_CurrentlyRenderedTree)
|
||||
continue;
|
||||
|
||||
// Select the element the mouse cursor is over.
|
||||
// Only do it on mouse move - keyboard controls are allowed to overwrite this until the next time the mouse moves.
|
||||
if ((Event.current.type == EventType.MouseMove || Event.current.type == EventType.MouseDrag) && child.enabled)
|
||||
{
|
||||
if (!selected && r.Contains(Event.current.mousePosition))
|
||||
{
|
||||
m_State.SetSelectedIndex(item, i);
|
||||
Event.current.Use();
|
||||
}
|
||||
}
|
||||
if (Event.current.type == EventType.MouseUp && r.Contains(Event.current.mousePosition) && child.enabled)
|
||||
{
|
||||
m_State.SetSelectedIndex(item, i);
|
||||
var selectedChild = m_State.GetSelectedChild(item);
|
||||
if (selectedChild.children.Any())
|
||||
{
|
||||
GoToChild();
|
||||
}
|
||||
else if (!selectedChild.IsSeparator())
|
||||
{
|
||||
selectionChanged?.Invoke(selectedChild);
|
||||
if (closeOnSelection)
|
||||
{
|
||||
CloseWindow();
|
||||
GUIUtility.ExitGUI();
|
||||
}
|
||||
}
|
||||
Event.current.Use();
|
||||
}
|
||||
}
|
||||
EditorGUIUtility.SetIconSize(Vector2.zero);
|
||||
GUILayout.EndScrollView();
|
||||
|
||||
// Scroll to selected on windows creation
|
||||
if (m_ScrollToSelected && m_InitialSelectionPosition != 0)
|
||||
{
|
||||
var diffOfPopupAboveTheButton = m_ButtonRectScreenPos.y - position.y;
|
||||
diffOfPopupAboveTheButton -= m_Gui.searchHeight + m_Gui.headerHeight;
|
||||
m_State.SetScrollState(item, new Vector2(0, m_InitialSelectionPosition - diffOfPopupAboveTheButton));
|
||||
m_ScrollToSelected = false;
|
||||
m_InitialSelectionPosition = 0;
|
||||
}
|
||||
// Scroll to show selected
|
||||
else if (m_ScrollToSelected && Event.current.type == EventType.Repaint)
|
||||
{
|
||||
m_ScrollToSelected = false;
|
||||
Rect scrollRect = GUILayoutUtility.GetLastRect();
|
||||
if (selectedRect.yMax - scrollRect.height > m_State.GetScrollState(item).y)
|
||||
{
|
||||
m_State.SetScrollState(item, new Vector2(0, selectedRect.yMax - scrollRect.height));
|
||||
Repaint();
|
||||
}
|
||||
if (selectedRect.y < m_State.GetScrollState(item).y)
|
||||
{
|
||||
m_State.SetScrollState(item, new Vector2(0, selectedRect.y));
|
||||
Repaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void GoToParent()
|
||||
{
|
||||
if (m_ViewsStack.Count == 0)
|
||||
return;
|
||||
m_LastTime = DateTime.Now.Ticks;
|
||||
if (m_NewAnimTarget > 0)
|
||||
m_NewAnimTarget = -1 + m_NewAnimTarget;
|
||||
else
|
||||
m_NewAnimTarget = -1;
|
||||
m_AnimationTree = m_CurrentlyRenderedTree;
|
||||
var parentItem = m_ViewsStack.Pop();
|
||||
|
||||
m_State.ClearSelectionOnItem(m_CurrentlyRenderedTree);
|
||||
|
||||
if (parentItem != null)
|
||||
{
|
||||
var suggestedIndex = parentItem.GetIndexOfChild(m_CurrentlyRenderedTree);
|
||||
m_State.SetSelectionOnItem(parentItem, suggestedIndex);
|
||||
}
|
||||
|
||||
m_CurrentlyRenderedTree = parentItem;
|
||||
}
|
||||
|
||||
private void GoToChild()
|
||||
{
|
||||
m_ViewsStack.Push(m_CurrentlyRenderedTree);
|
||||
m_LastTime = DateTime.Now.Ticks;
|
||||
if (m_NewAnimTarget < 0)
|
||||
m_NewAnimTarget = 1 + m_NewAnimTarget;
|
||||
else
|
||||
m_NewAnimTarget = 1;
|
||||
m_AnimationTree = m_CurrentlyRenderedTree;
|
||||
m_CurrentlyRenderedTree = m_State.GetSelectedChild(m_CurrentlyRenderedTree);
|
||||
}
|
||||
|
||||
[DidReloadScripts]
|
||||
private static void OnScriptReload()
|
||||
{
|
||||
CloseAllOpenWindows<AdvancedDropdownWindow>();
|
||||
}
|
||||
|
||||
protected static void CloseAllOpenWindows<T>()
|
||||
{
|
||||
var windows = Resources.FindObjectsOfTypeAll(typeof(T));
|
||||
foreach (var window in windows)
|
||||
{
|
||||
try
|
||||
{
|
||||
((EditorWindow)window).Close();
|
||||
}
|
||||
catch
|
||||
{
|
||||
DestroyImmediate(window);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class Styles
|
||||
{
|
||||
public static readonly GUIStyle background = "grey_border";
|
||||
public static readonly GUIStyle previewHeader = new GUIStyle(EditorStyles.label).WithPadding(new RectOffset(5, 5, 1, 2));
|
||||
public static readonly GUIStyle previewText = new GUIStyle(EditorStyles.wordWrappedLabel).WithPadding(new RectOffset(3, 5, 4, 4));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // UNITY_EDITOR
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c46ed0dd23ba548fd87436e8c89334e9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2fe2cf22711ac4db3b094a955603f8bd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: caeab4c8826564dfda5e44a7e01a8de1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user