You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
223 lines
7.9 KiB
223 lines
7.9 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using UnityEditor;
|
|
using UnityEditorInternal;
|
|
using UnityEngine;
|
|
using UnityEngine.UIElements;
|
|
|
|
namespace UnityEditor.ShaderGraph.Drawing
|
|
{
|
|
internal class ReorderableListView<T> : VisualElement
|
|
{
|
|
// generic control to display and allow the user to directly reorder/add/remove from a list of T
|
|
|
|
// the List we are editing
|
|
List<T> m_DataList;
|
|
|
|
// this is how we get the string to display for each item
|
|
Func<T, string> m_GetDisplayTextFunc;
|
|
|
|
readonly bool m_AllowReorder;
|
|
ReorderableList m_ReorderableList;
|
|
List<string> m_TextList = new List<string>();
|
|
|
|
IMGUIContainer m_Container;
|
|
GUIStyle m_LabelStyle;
|
|
int m_SelectedIndex = -1;
|
|
string m_HeaderLabel;
|
|
|
|
// list of options for the drop down menu when the user clicks the add button
|
|
public delegate List<string> GetAddMenuOptionsDelegate();
|
|
public GetAddMenuOptionsDelegate GetAddMenuOptions;
|
|
|
|
// callback when the user clicks on an item from the AddMenu
|
|
public delegate void OnAddMenuItemDelegate(List<T> targetList, int addMenuOptionIndex, string addMenuOption);
|
|
public OnAddMenuItemDelegate OnAddMenuItemCallback;
|
|
|
|
// callback to override how an item is removed from the list
|
|
internal delegate void RemoveItemDelegate(List<T> list, int itemIndex);
|
|
public RemoveItemDelegate RemoveItemCallback;
|
|
|
|
// callback when the list is reordered
|
|
internal delegate void ListReorderedDelegate(List<T> reorderedList);
|
|
public ListReorderedDelegate OnListReorderedCallback;
|
|
|
|
internal ReorderableListView(
|
|
List<T> dataList,
|
|
string header = "Reorder data:",
|
|
bool allowReorder = true,
|
|
Func<T, string> getDisplayText = null)
|
|
{
|
|
m_DataList = dataList;
|
|
m_HeaderLabel = header;
|
|
m_AllowReorder = allowReorder;
|
|
|
|
// setup GetDisplayTextFunc
|
|
if (getDisplayText == null)
|
|
m_GetDisplayTextFunc = data => data.ToString();
|
|
else
|
|
m_GetDisplayTextFunc = getDisplayText;
|
|
|
|
RebuildTextList();
|
|
|
|
// should we set up a new style sheet? allow user overrides?
|
|
styleSheets.Add(Resources.Load<StyleSheet>("Styles/ReorderableSlotListView"));
|
|
|
|
m_Container = new IMGUIContainer(() => OnGUIHandler()) { name = "ListContainer" };
|
|
Add(m_Container);
|
|
}
|
|
|
|
void RebuildTextList()
|
|
{
|
|
m_TextList.Clear();
|
|
try
|
|
{
|
|
foreach (var data in m_DataList)
|
|
{
|
|
m_TextList.Add(m_GetDisplayTextFunc(data));
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.Log("Exception: " + e.ToString() + " while handling ReorderableListView of type: " + typeof(T).ToString());
|
|
}
|
|
}
|
|
|
|
internal void RecreateList(List<T> dataList)
|
|
{
|
|
m_DataList = dataList;
|
|
|
|
// Create reorderable list from data list
|
|
m_ReorderableList = new ReorderableList(
|
|
dataList,
|
|
typeof(T), // the type of the elements in dataList
|
|
m_AllowReorder, // draggable (to reorder)
|
|
true, // displayHeader
|
|
true, // displayAddButton
|
|
true); // displayRemoveButton
|
|
}
|
|
|
|
private void OnGUIHandler()
|
|
{
|
|
try
|
|
{
|
|
if (m_ReorderableList == null)
|
|
{
|
|
RecreateList(m_DataList);
|
|
AddCallbacks();
|
|
}
|
|
|
|
using (var changeCheckScope = new EditorGUI.ChangeCheckScope())
|
|
{
|
|
m_ReorderableList.index = m_SelectedIndex;
|
|
m_ReorderableList.DoLayoutList();
|
|
|
|
if (changeCheckScope.changed)
|
|
{
|
|
// Do things when changed
|
|
}
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.Log("Exception: " + e.ToString() + " while handling ReorderableListView of type: " + typeof(T).ToString());
|
|
}
|
|
}
|
|
|
|
private void AddCallbacks()
|
|
{
|
|
m_ReorderableList.drawHeaderCallback = (Rect rect) =>
|
|
{
|
|
var labelRect = new Rect(rect.x, rect.y, rect.width - 10, rect.height);
|
|
EditorGUI.LabelField(labelRect, m_HeaderLabel);
|
|
};
|
|
|
|
// Draw Element
|
|
m_ReorderableList.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) =>
|
|
{
|
|
EditorGUI.LabelField(
|
|
new Rect(rect.x, rect.y, rect.width / 2, EditorGUIUtility.singleLineHeight),
|
|
m_TextList[index], EditorStyles.label);
|
|
};
|
|
|
|
// Element height
|
|
m_ReorderableList.elementHeightCallback = (int indexer) => { return m_ReorderableList.elementHeight; };
|
|
|
|
// Add callback delegates
|
|
m_ReorderableList.onSelectCallback += SelectEntry; // should we propagate this up if user wants to do something with selection?
|
|
m_ReorderableList.onReorderCallback += ReorderEntries;
|
|
m_ReorderableList.onAddDropdownCallback += OnAddDropdownMenu;
|
|
m_ReorderableList.onRemoveCallback += OnRemove;
|
|
}
|
|
|
|
private void SelectEntry(ReorderableList list)
|
|
{
|
|
m_SelectedIndex = list.index;
|
|
}
|
|
|
|
private void ReorderEntries(ReorderableList list)
|
|
{
|
|
RebuildTextList();
|
|
OnListReorderedCallback(m_DataList);
|
|
}
|
|
|
|
private void OnAddDropdownMenu(Rect buttonRect, ReorderableList list)
|
|
{
|
|
//created the drop down menu on add item from the listview
|
|
List<string> addMenuOptions;
|
|
if (GetAddMenuOptions != null)
|
|
addMenuOptions = GetAddMenuOptions();
|
|
else
|
|
addMenuOptions = new List<string>();
|
|
|
|
var menu = new GenericMenu();
|
|
for (int optionIndex = 0; optionIndex < addMenuOptions.Count; optionIndex++)
|
|
{
|
|
string optionText = addMenuOptions[optionIndex];
|
|
|
|
// disable any option that is already in the text list
|
|
bool optionEnabled = !m_TextList.Contains(optionText);
|
|
|
|
// need to make a copy so the lambda will capture the value, not the variable
|
|
int localOptionIndex = optionIndex;
|
|
|
|
if (optionEnabled)
|
|
menu.AddItem(new GUIContent(optionText), false, () => OnAddMenuClicked(localOptionIndex, optionText));
|
|
else
|
|
menu.AddDisabledItem(new GUIContent(optionText));
|
|
}
|
|
menu.ShowAsContext();
|
|
}
|
|
|
|
void OnAddMenuClicked(int optionIndex, string optionText)
|
|
{
|
|
OnAddMenuItemCallback(m_DataList, optionIndex, optionText);
|
|
|
|
// if anything was changed about the list in the callback, we need to rebuild our text list
|
|
RebuildTextList();
|
|
}
|
|
|
|
void OnRemove(ReorderableList list)
|
|
{
|
|
int indexToRemove = list.index;
|
|
if (indexToRemove < 0)
|
|
indexToRemove = m_DataList.Count - 1;
|
|
|
|
if (indexToRemove >= 0)
|
|
{
|
|
if (RemoveItemCallback != null)
|
|
{
|
|
RemoveItemCallback(m_DataList, indexToRemove);
|
|
}
|
|
else
|
|
{
|
|
// no callback provided, do it ourselves
|
|
m_DataList.RemoveAt(indexToRemove);
|
|
}
|
|
|
|
// if anything was changed about the list in the callback, we need to rebuild our text list
|
|
RebuildTextList();
|
|
}
|
|
}
|
|
}
|
|
}
|