Files
trail-into-darkness/Packages/SerializedDictionary-main/Editor/Scripts/SerializedDictionaryInstanceDrawer.cs
2026-03-19 22:20:34 +01:00

689 lines
30 KiB
C#

using AYellowpaper.SerializedCollections.Editor.Data;
using AYellowpaper.SerializedCollections.Editor.States;
using AYellowpaper.SerializedCollections.KeysGenerators;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEditor.IMGUI.Controls;
using UnityEditorInternal;
using UnityEngine;
namespace AYellowpaper.SerializedCollections.Editor
{
public class SerializedDictionaryInstanceDrawer
{
private FieldInfo _fieldInfo;
private ReorderableList _unexpandedList;
private SingleEditingData _singleEditingData;
private FieldInfo _keyFieldInfo;
private GUIContent _label;
private Rect _totalRect;
private GUIStyle _keyValueStyle;
private SerializedDictionaryAttribute _dictionaryAttribute;
private PropertyData _propertyData;
private bool _propertyListSettingsInitialized = false;
private List<int> _pagedIndices;
private PagingElement _pagingElement;
private int _lastListSize = -1;
private IReadOnlyList<KeyListGeneratorData> _keyGeneratorsWithoutWindow;
private IReadOnlyList<KeyListGeneratorData> _keyGeneratorsWithWindow;
private SearchField _searchField;
private GUIContent _shortDetailsContent;
private GUIContent _detailsContent;
private bool _showSearchBar = false;
private ListState _activeState;
internal ReorderableList ReorderableList { get; private set; }
internal SerializedProperty ListProperty { get; private set; }
internal string SearchText { get; private set; } = string.Empty;
internal SearchListState SearchState { get; private set; }
internal DefaultListState DefaultState { get; private set; }
private class SingleEditingData
{
public bool IsValid => LookupTable != null;
public IKeyable LookupTable;
public void Invalidate()
{
LookupTable = null;
}
}
public SerializedDictionaryInstanceDrawer(SerializedProperty property, FieldInfo fieldInfo)
{
_fieldInfo = fieldInfo;
ListProperty = property.FindPropertyRelative(SerializedDictionaryDrawer.SerializedListName);
_keyValueStyle = new GUIStyle(EditorStyles.toolbarButton);
_keyValueStyle.padding = new RectOffset(0, 0, 0, 0);
_keyValueStyle.border = new RectOffset(0, 0, 0, 0);
_keyValueStyle.alignment = TextAnchor.MiddleCenter;
DefaultState = new DefaultListState(this);
SearchState = new SearchListState(this);
_activeState = DefaultState;
_dictionaryAttribute = _fieldInfo.GetCustomAttribute<SerializedDictionaryAttribute>();
_propertyData = SCEditorUtility.GetPropertyData(ListProperty);
_propertyData.GetElementData(SCEditorUtility.KeyFlag).Settings.DisplayName = _dictionaryAttribute?.KeyName ?? "Key";
_propertyData.GetElementData(SCEditorUtility.ValueFlag).Settings.DisplayName = _dictionaryAttribute?.ValueName ?? "Value";
SavePropertyData();
_pagingElement = new PagingElement();
_pagedIndices = new List<int>();
UpdatePaging();
ReorderableList = MakeList();
_unexpandedList = MakeUnexpandedList();
_searchField = new SearchField();
var listField = _fieldInfo.FieldType.GetField(SerializedDictionaryDrawer.SerializedListName, BindingFlags.Instance | BindingFlags.NonPublic);
var entryType = listField.FieldType.GetGenericArguments()[0];
_keyFieldInfo = entryType.GetField(SerializedDictionaryDrawer.KeyName);
_singleEditingData = new SingleEditingData();
var keyGenerators = KeyListGeneratorCache.GetPopulatorsForType(_keyFieldInfo.FieldType);
_keyGeneratorsWithWindow = keyGenerators.Where(x => x.NeedsWindow).ToList();
_keyGeneratorsWithoutWindow = keyGenerators.Where(x => !x.NeedsWindow).ToList();
UpdateAfterInput();
}
public void OnGUI(Rect position, GUIContent label)
{
_totalRect = position;
_label = new GUIContent(label);
// Only call Update() in EditorWindow contexts where it's required.
// In PropertyDrawers for MonoBehaviours/ScriptableObjects, Unity manages serialization
// and calling Update() interferes with pending edits, causing values to revert.
if (ListProperty.serializedObject.targetObject is EditorWindow)
ListProperty.serializedObject.Update();
EditorGUI.BeginChangeCheck();
DoList(position);
if (EditorGUI.EndChangeCheck())
{
ListProperty.serializedObject.ApplyModifiedProperties();
}
}
public float GetPropertyHeight(GUIContent label)
{
if (!ListProperty.isExpanded)
return SerializedDictionaryDrawer.TopHeaderClipHeight;
return ReorderableList.GetHeight();
}
private void DoList(Rect position)
{
if (ListProperty.isExpanded)
ReorderableList.DoList(position);
else
{
using (new GUI.ClipScope(new Rect(0, position.y, position.width + position.x, SerializedDictionaryDrawer.TopHeaderClipHeight)))
{
_unexpandedList.DoList(position.WithY(0));
}
}
}
private void ProcessState()
{
var newState = _activeState.OnUpdate();
if (newState != null && newState != _activeState)
{
_activeState.OnExit();
_activeState = newState;
newState.OnEnter();
}
}
private SerializedProperty GetElementProperty(SerializedProperty property, bool fieldFlag)
{
return property.FindPropertyRelative(fieldFlag == SerializedDictionaryDrawer.KeyFlag ? SerializedDictionaryDrawer.KeyName : SerializedDictionaryDrawer.ValueName);
}
internal static float CalculateHeightOfElement(SerializedProperty property, bool drawKeyAsList, bool drawValueAsList)
{
SerializedProperty keyProperty = property.FindPropertyRelative(SerializedDictionaryDrawer.KeyName);
SerializedProperty valueProperty = property.FindPropertyRelative(SerializedDictionaryDrawer.ValueName);
return Mathf.Max(SCEditorUtility.CalculateHeight(keyProperty, drawKeyAsList), SCEditorUtility.CalculateHeight(valueProperty, drawValueAsList));
}
private void UpdateAfterInput()
{
InitializeSettingsIfNeeded();
ProcessState();
CheckPaging();
var elementsPerPage = EditorUserSettings.Get().ElementsPerPage;
int pageCount = Mathf.Max(1, Mathf.CeilToInt((float)DefaultState.ListSize / elementsPerPage));
ToggleSearchBar(_propertyData.AlwaysShowSearch ? true : SCEditorUtility.ShouldShowSearch(pageCount));
}
private void InitializeSettingsIfNeeded()
{
void InitializeSettings(bool fieldFlag)
{
var genericArgs = _fieldInfo.FieldType.GetGenericArguments();
var firstProperty = ListProperty.GetArrayElementAtIndex(0);
var keySettings = CreateDisplaySettings(GetElementProperty(firstProperty, fieldFlag), genericArgs[fieldFlag == SCEditorUtility.KeyFlag ? 0 : 1]);
var settings = _propertyData.GetElementData(fieldFlag).Settings;
settings.DisplayType = keySettings.displayType;
settings.HasListDrawerToggle = keySettings.canToggleListDrawer;
}
if (!_propertyListSettingsInitialized && ListProperty.minArraySize > 0)
{
_propertyListSettingsInitialized = true;
InitializeSettings(SCEditorUtility.KeyFlag);
InitializeSettings(SCEditorUtility.ValueFlag);
SavePropertyData();
}
}
private void CheckPaging()
{
// TODO: Is there a better solution to check for Revert/delete/add?
if (_lastListSize != _activeState.ListSize)
{
_lastListSize = _activeState.ListSize;
UpdateSingleEditing();
UpdatePaging();
}
}
private void SavePropertyData()
{
SCEditorUtility.SavePropertyData(ListProperty, _propertyData);
}
private void UpdateSingleEditing()
{
if (ListProperty.serializedObject.isEditingMultipleObjects && _singleEditingData.IsValid)
_singleEditingData.Invalidate();
else if (!ListProperty.serializedObject.isEditingMultipleObjects && !_singleEditingData.IsValid)
{
var dictionary = SCEditorUtility.GetPropertyValue(ListProperty, ListProperty.serializedObject.targetObject);
_singleEditingData.LookupTable = GetLookupTable(dictionary);
}
}
private IKeyable GetLookupTable(object dictionary)
{
var propInfo = dictionary.GetType().GetProperty(SerializedDictionaryDrawer.LookupTableName, BindingFlags.Instance | BindingFlags.NonPublic);
return (IKeyable)propInfo.GetValue(dictionary);
}
private void UpdatePaging()
{
var elementsPerPage = EditorUserSettings.Get().ElementsPerPage;
_pagingElement.PageCount = Mathf.Max(1, Mathf.CeilToInt((float)_activeState.ListSize / elementsPerPage));
_pagedIndices.Clear();
_pagedIndices.Capacity = Mathf.Max(elementsPerPage, _pagedIndices.Capacity);
int startIndex = (_pagingElement.Page - 1) * elementsPerPage;
int endIndex = Mathf.Min(startIndex + elementsPerPage, _activeState.ListSize);
for (int i = startIndex; i < endIndex; i++)
_pagedIndices.Add(i);
string shortDetailsString = (_activeState.ListSize + " " + (_pagedIndices.Count == 1 ? "Element" : "Elements"));
string detailsString = _pagingElement.PageCount > 1
? $"{_pagedIndices[0] + 1}..{_pagedIndices.Last() + 1} / {_activeState.ListSize} Elements"
: shortDetailsString;
_detailsContent = new GUIContent(detailsString);
_shortDetailsContent = new GUIContent(shortDetailsString);
}
private ReorderableList MakeList()
{
var list = new ReorderableList(_pagedIndices, typeof(int), true, true, true, true);
list.onAddCallback += OnAdd;
list.onRemoveCallback += OnRemove;
list.onReorderCallbackWithDetails += OnReorder;
list.drawElementCallback += OnDrawElement;
list.elementHeightCallback += OnGetElementHeight;
list.drawHeaderCallback += OnDrawHeader;
list.drawNoneElementCallback += OnDrawNoneElement;
return list;
}
private ReorderableList MakeUnexpandedList()
{
var list = new ReorderableList(SerializedDictionaryDrawer.NoEntriesList, typeof(int));
list.drawHeaderCallback = DrawUnexpandedHeader;
return list;
}
private void ToggleSearchBar(bool flag)
{
_showSearchBar = flag;
ReorderableList.headerHeight = SerializedDictionaryDrawer.TopHeaderClipHeight + SerializedDictionaryDrawer.KeyValueHeaderHeight + (_showSearchBar ? SerializedDictionaryDrawer.SearchHeaderHeight : 0);
if (!_showSearchBar)
{
if (_searchField.HasFocus())
GUI.FocusControl(null);
SearchText = string.Empty;
}
}
private void OnDrawNoneElement(Rect rect)
{
EditorGUI.LabelField(rect, EditorGUIUtility.TrTextContent(_activeState.NoElementsText));
}
private (DisplayType displayType, bool canToggleListDrawer) CreateDisplaySettings(SerializedProperty property, Type type)
{
bool hasCustomEditor = SCEditorUtility.HasDrawerForType(type, property.propertyType == SerializedPropertyType.ManagedReference);
bool isGenericWithChildren = property.propertyType == SerializedPropertyType.Generic && property.hasVisibleChildren;
bool isArray = property.isArray && property.propertyType != SerializedPropertyType.String;
bool canToggleListDrawer = isArray || (isGenericWithChildren && hasCustomEditor);
DisplayType displayType = DisplayType.PropertyNoLabel;
if (canToggleListDrawer)
displayType = DisplayType.Property;
else if (!isArray && isGenericWithChildren && !hasCustomEditor)
displayType = DisplayType.List;
return (displayType, canToggleListDrawer);
}
private void DrawUnexpandedHeader(Rect rect)
{
EditorGUI.BeginProperty(rect, _label, ListProperty);
ListProperty.isExpanded = EditorGUI.Foldout(rect.WithX(rect.x - 5), ListProperty.isExpanded, _label, true);
var detailsStyle = EditorStyles.miniLabel;
var detailsRect = rect.AppendRight(0).AppendLeft(detailsStyle.CalcSize(_shortDetailsContent).x);
GUI.Label(detailsRect, _shortDetailsContent, detailsStyle);
EditorGUI.EndProperty();
}
private void DoPaging(Rect rect)
{
EditorGUI.BeginChangeCheck();
_pagingElement.OnGUI(rect);
if (EditorGUI.EndChangeCheck())
{
ReorderableList.ClearSelection();
UpdatePaging();
}
}
private void OnDrawHeader(Rect rect)
{
Rect topRect = rect.WithHeight(SerializedDictionaryDrawer.TopHeaderHeight);
Rect adjustedTopRect = topRect.WithXAndWidth(_totalRect.x + 1, _totalRect.width - 1);
DoMainHeader(adjustedTopRect.CutLeft(topRect.x - adjustedTopRect.x));
if (_showSearchBar)
{
adjustedTopRect = adjustedTopRect.AppendDown(SerializedDictionaryDrawer.SearchHeaderHeight);
DoSearch(adjustedTopRect);
}
DoKeyValueRect(adjustedTopRect.AppendDown(SerializedDictionaryDrawer.KeyValueHeaderHeight));
UpdateAfterInput();
}
private void DoMainHeader(Rect rect)
{
Rect lastTopRect = rect.AppendRight(0).WithHeight(EditorGUIUtility.singleLineHeight);
lastTopRect = lastTopRect.AppendLeft(20);
DoOptionsButton(lastTopRect);
lastTopRect = lastTopRect.AppendLeft(5);
if (_pagingElement.PageCount > 1)
{
lastTopRect = lastTopRect.AppendLeft(_pagingElement.GetDesiredWidth());
DoPaging(lastTopRect);
}
var detailsStyle = EditorStyles.miniLabel;
lastTopRect = lastTopRect.AppendLeft(detailsStyle.CalcSize(_detailsContent).x, 5);
GUI.Label(lastTopRect, _detailsContent, detailsStyle);
if (!_singleEditingData.IsValid)
{
lastTopRect = lastTopRect.AppendLeft(lastTopRect.height + 5);
var guicontent = EditorGUIUtility.TrIconContent(EditorGUIUtility.Load("d_console.infoicon") as Texture, "Conflict checking, duplicate key removal and populators not supported in multi object editing mode.");
GUI.Label(lastTopRect, guicontent);
}
EditorGUI.BeginProperty(rect, _label, ListProperty);
ListProperty.isExpanded = EditorGUI.Foldout(rect.WithXAndWidth(rect.x - 5, lastTopRect.x - rect.x), ListProperty.isExpanded, _label, true);
EditorGUI.EndProperty();
}
private void DoOptionsButton(Rect rect)
{
var screenRect = GUIUtility.GUIToScreenRect(rect);
if (GUI.Button(rect, EditorGUIUtility.IconContent("pane options@2x"), EditorStyles.iconButton))
{
var gm = new GenericMenu();
SCEditorUtility.AddGenericMenuItem(gm, false, ListProperty.minArraySize > 0, new GUIContent("Clear"), () => QueueAction(ClearList));
SCEditorUtility.AddGenericMenuItem(gm, false, true, new GUIContent("Remove Conflicts"), () => QueueAction(RemoveConflicts));
SCEditorUtility.AddGenericMenuItem(gm, false, _keyGeneratorsWithWindow.Count > 0, new GUIContent("Bulk Edit..."), () => OpenKeysGeneratorSelectorWindow(screenRect));
if (_keyGeneratorsWithoutWindow.Count > 0)
{
gm.AddSeparator(string.Empty);
foreach (var generatorData in _keyGeneratorsWithoutWindow)
{
SCEditorUtility.AddGenericMenuItem(gm, false, true, new GUIContent(generatorData.Name), OnPopulatorDataSelected, generatorData);
}
}
gm.AddSeparator(string.Empty);
SCEditorUtility.AddGenericMenuItem(gm, _propertyData.AlwaysShowSearch, true, new GUIContent("Always Show Search"), ToggleAlwaysShowSearchPropertyData);
gm.AddItem(new GUIContent("Preferences..."), false, () => SettingsService.OpenUserPreferences(EditorUserSettingsProvider.PreferencesPath));
gm.DropDown(rect);
}
}
private void OnPopulatorDataSelected(object userData)
{
var data = (KeyListGeneratorData)userData;
var so = (KeyListGenerator)ScriptableObject.CreateInstance(data.GeneratorType);
so.hideFlags = HideFlags.DontSave;
ApplyPopulatorQueued(so, ModificationType.Add);
}
private void OpenKeysGeneratorSelectorWindow(Rect rect)
{
var window = ScriptableObject.CreateInstance<KeyListGeneratorSelectorWindow>();
window.Initialize(_keyGeneratorsWithWindow, _keyFieldInfo.FieldType);
window.ShowAsDropDown(rect, new Vector2(400, 200));
window.OnApply += ApplyPopulatorQueued;
}
private void ToggleAlwaysShowSearchPropertyData()
{
_propertyData.AlwaysShowSearch = !_propertyData.AlwaysShowSearch;
SavePropertyData();
}
private void DoKeyValueRect(Rect rect)
{
float width = EditorGUIUtility.labelWidth + 22;
Rect leftRect = rect.WithWidth(width);
Rect rightRect = leftRect.AppendRight(rect.width - width);
if (Event.current.type == EventType.Repaint && _propertyData != null)
{
_keyValueStyle.Draw(leftRect, EditorGUIUtility.TrTextContent(_propertyData.GetElementData(SerializedDictionaryDrawer.KeyFlag).Settings.DisplayName), false, false, false, false);
_keyValueStyle.Draw(rightRect, EditorGUIUtility.TrTextContent(_propertyData.GetElementData(SerializedDictionaryDrawer.ValueFlag).Settings.DisplayName), false, false, false, false);
}
if (ListProperty.minArraySize > 0)
{
DoDisplayTypeToggle(leftRect, SerializedDictionaryDrawer.KeyFlag);
DoDisplayTypeToggle(rightRect, SerializedDictionaryDrawer.ValueFlag);
}
EditorGUI.DrawRect(rect.AppendDown(1, -1), SerializedDictionaryDrawer.BorderColor);
}
private void DoSearch(Rect rect)
{
EditorGUI.DrawRect(rect.AppendLeft(1), SerializedDictionaryDrawer.BorderColor);
EditorGUI.DrawRect(rect.AppendRight(1, -1), SerializedDictionaryDrawer.BorderColor);
EditorGUI.DrawRect(rect.AppendDown(1, -1), SerializedDictionaryDrawer.BorderColor);
SearchText = _searchField.OnToolbarGUI(rect.CutTop(2).CutHorizontal(6), SearchText);
}
private void ApplyPopulatorQueued(KeyListGenerator populator, ModificationType modificationType)
{
var array = populator.GetKeys(_keyFieldInfo.FieldType).OfType<object>().ToArray();
QueueAction(() => ApplyPopulator(array, modificationType));
}
private void QueueAction(EditorApplication.CallbackFunction action)
{
EditorApplication.delayCall += action;
}
private void ApplyPopulator(IEnumerable<object> elements, ModificationType modificationType)
{
foreach (var targetObject in ListProperty.serializedObject.targetObjects)
{
Undo.RecordObject(targetObject, "Populate");
var dictionary = SCEditorUtility.GetPropertyValue(ListProperty, targetObject);
var lookupTable = GetLookupTable(dictionary);
if (modificationType == ModificationType.Add)
AddElements(lookupTable, elements);
else if (modificationType == ModificationType.Remove)
RemoveElements(lookupTable, elements);
else if (modificationType == ModificationType.Confine)
ConfineElements(lookupTable, elements);
lookupTable.RecalculateOccurences();
PrefabUtility.RecordPrefabInstancePropertyModifications(targetObject);
}
ListProperty.serializedObject.Update();
ActiveEditorTracker.sharedTracker.ForceRebuild();
}
private static void AddElements(IKeyable lookupTable, IEnumerable<object> elements)
{
foreach (var key in elements)
{
var occurences = lookupTable.GetOccurences(key);
if (occurences.Count > 0)
continue;
lookupTable.AddKey(key);
}
}
private static void ConfineElements(IKeyable lookupTable, IEnumerable<object> elements)
{
var keysToRemove = lookupTable.Keys.OfType<object>().ToHashSet();
foreach (var key in elements)
keysToRemove.Remove(key);
RemoveElements(lookupTable, keysToRemove);
}
private static void RemoveElements(IKeyable lookupTable, IEnumerable<object> elements)
{
var indicesToRemove = elements.SelectMany(x => lookupTable.GetOccurences(x)).OrderByDescending(index => index);
foreach (var index in indicesToRemove)
{
lookupTable.RemoveAt(index);
}
}
private void ClearList()
{
ListProperty.ClearArray();
ListProperty.serializedObject.ApplyModifiedProperties();
}
private void RemoveConflicts()
{
foreach (var targetObject in ListProperty.serializedObject.targetObjects)
{
Undo.RecordObject(targetObject, "Remove Conflicts");
var dictionary = SCEditorUtility.GetPropertyValue(ListProperty, targetObject);
var lookupTable = GetLookupTable(dictionary);
List<int> duplicateIndices = new List<int>();
foreach (var key in lookupTable.Keys)
{
var occurences = lookupTable.GetOccurences(key);
for (int i = 1; i < occurences.Count; i++)
duplicateIndices.Add(occurences[i]);
}
foreach (var indexToRemove in duplicateIndices.OrderByDescending(x => x))
{
lookupTable.RemoveAt(indexToRemove);
}
lookupTable.RecalculateOccurences();
PrefabUtility.RecordPrefabInstancePropertyModifications(targetObject);
}
ListProperty.serializedObject.Update();
ActiveEditorTracker.sharedTracker.ForceRebuild();
}
private void DoDisplayTypeToggle(Rect contentRect, bool fieldFlag)
{
var displayData = _propertyData.GetElementData(fieldFlag);
if (displayData.Settings.HasListDrawerToggle)
{
Rect rightRectToggle = new Rect(contentRect);
rightRectToggle.x += rightRectToggle.width - 18;
rightRectToggle.width = 18;
EditorGUI.BeginChangeCheck();
bool newValue = GUI.Toggle(rightRectToggle, displayData.IsListToggleActive, SerializedDictionaryDrawer.DisplayTypeToggleContent, EditorStyles.toolbarButton);
if (EditorGUI.EndChangeCheck())
{
displayData.IsListToggleActive = newValue;
SavePropertyData();
}
}
}
private float OnGetElementHeight(int index)
{
int actualIndex = _pagedIndices[index];
var element = _activeState.GetPropertyAtIndex(actualIndex);
return CalculateHeightOfElement(element, _propertyData.GetElementData(SerializedDictionaryDrawer.KeyFlag).EffectiveDisplayType == DisplayType.List ? true : false, _propertyData.GetElementData(SerializedDictionaryDrawer.ValueFlag).EffectiveDisplayType == DisplayType.List ? true : false);
}
private void OnDrawElement(Rect rect, int index, bool isActive, bool isFocused)
{
const int lineLeftSpace = 2;
const int lineWidth = 1;
const int lineRightSpace = 12;
const int totalSpace = lineLeftSpace + lineWidth + lineRightSpace;
int actualIndex = _pagedIndices[index];
SerializedProperty kvp = _activeState.GetPropertyAtIndex(actualIndex);
Rect keyRect = rect.WithSize(EditorGUIUtility.labelWidth - lineLeftSpace, EditorGUIUtility.singleLineHeight);
Rect lineRect = keyRect.WithXAndWidth(keyRect.x + keyRect.width + lineLeftSpace, lineWidth).WithHeight(rect.height);
Rect valueRect = keyRect.AppendRight(rect.width - keyRect.width - totalSpace, totalSpace);
var keyProperty = kvp.FindPropertyRelative(SerializedDictionaryDrawer.KeyName);
var valueProperty = kvp.FindPropertyRelative(SerializedDictionaryDrawer.ValueName);
Color prevColor = GUI.color;
if (_singleEditingData.IsValid)
{
var keyObject = _keyFieldInfo.GetValue(_singleEditingData.LookupTable.GetKeyAt(actualIndex));
var occurences = _singleEditingData.LookupTable.GetOccurences(keyObject);
if (occurences.Count > 1)
{
GUI.color = occurences[0] == actualIndex ? Color.yellow : Color.red;
}
if (!SerializedCollectionsUtility.IsValidKey(keyObject))
{
GUI.color = Color.red;
}
}
var keyDisplayData = _propertyData.GetElementData(SerializedDictionaryDrawer.KeyFlag);
DrawGroupedElement(keyRect, 20, keyProperty, keyDisplayData.EffectiveDisplayType);
EditorGUI.DrawRect(lineRect, new Color(36 / 255f, 36 / 255f, 36 / 255f));
GUI.color = prevColor;
var valueDisplayData = _propertyData.GetElementData(SerializedDictionaryDrawer.ValueFlag);
DrawGroupedElement(valueRect, lineRightSpace, valueProperty, valueDisplayData.EffectiveDisplayType);
}
private void DrawGroupedElement(Rect rect, int spaceForProperty, SerializedProperty property, DisplayType displayType)
{
using (new LabelWidth(rect.width * 0.4f))
{
float height = SCEditorUtility.CalculateHeight(property.Copy(), displayType);
Rect groupRect = rect.CutLeft(-spaceForProperty).WithHeight(height);
GUI.BeginGroup(groupRect);
Rect elementRect = new Rect(spaceForProperty, 0, rect.width, height);
_activeState.DrawElement(elementRect, property, displayType);
DrawInvisibleProperty(rect.WithWidth(spaceForProperty), property);
GUI.EndGroup();
}
}
internal static void DrawInvisibleProperty(Rect rect, SerializedProperty property)
{
const int propertyOffset = 5;
GUI.BeginClip(rect.CutLeft(-propertyOffset));
EditorGUI.BeginProperty(rect, GUIContent.none, property);
EditorGUI.EndProperty();
GUI.EndClip();
}
internal static void DrawElement(Rect rect, SerializedProperty property, DisplayType displayType, Action<SerializedProperty> BeforeDrawingCallback = null, Action<SerializedProperty> AfterDrawingCallback = null)
{
switch (displayType)
{
case DisplayType.Property:
BeforeDrawingCallback?.Invoke(property);
EditorGUI.PropertyField(rect, property, true);
AfterDrawingCallback?.Invoke(property);
break;
case DisplayType.PropertyNoLabel:
BeforeDrawingCallback?.Invoke(property);
EditorGUI.PropertyField(rect, property, GUIContent.none, true);
AfterDrawingCallback?.Invoke(property);
break;
case DisplayType.List:
Rect childRect = rect.WithHeight(0);
foreach (SerializedProperty prop in SCEditorUtility.GetChildren(property.Copy()))
{
childRect = childRect.AppendDown(EditorGUI.GetPropertyHeight(prop, true));
BeforeDrawingCallback?.Invoke(prop);
EditorGUI.PropertyField(childRect, prop, true);
AfterDrawingCallback?.Invoke(prop);
}
break;
default:
break;
}
}
private void OnAdd(ReorderableList list)
{
int targetIndex = list.selectedIndices.Count > 0 && list.selectedIndices[0] >= 0 ? list.selectedIndices[0] : 0;
int actualTargetIndex = targetIndex < _pagedIndices.Count ? _pagedIndices[targetIndex] : 0;
_activeState.InserElementAt(actualTargetIndex);
}
private void OnReorder(ReorderableList list, int oldIndex, int newIndex)
{
UpdatePaging();
ListProperty.MoveArrayElement(_pagedIndices[oldIndex], _pagedIndices[newIndex]);
}
private void OnRemove(ReorderableList list)
{
_activeState.RemoveElementAt(_pagedIndices[list.index]);
UpdatePaging();
//int actualIndex = _pagedIndices[list.index];
//ListProperty.DeleteArrayElementAtIndex(actualIndex);
//UpdatePaging();
//if (actualIndex >= ListProperty.minArraySize)
// list.index = _pagedIndices.Count - 1;
}
}
}