Multi edit support for non serialized lists

This commit is contained in:
VladV 2022-05-10 16:27:41 +03:00
parent a94645a3ad
commit 87972df66b
2 changed files with 143 additions and 29 deletions

View File

@ -1,4 +1,5 @@
using System.Collections;
using System;
using System.Collections;
using TriInspectorUnityInternalBridge;
using TriInspector.Utilities;
using UnityEditor;
@ -9,8 +10,6 @@ namespace TriInspector.Elements
{
internal class TriListElement : TriElement
{
private static readonly GUIContent ListIsNullContent = new GUIContent("List is null");
private readonly TriProperty _property;
private readonly ReorderableList _reorderableListGui;
private readonly bool _alwaysExpanded;
@ -31,12 +30,11 @@ namespace TriInspector.Elements
drawHeaderCallback = DrawHeaderCallback,
elementHeightCallback = ElementHeightCallback,
drawElementCallback = DrawElementCallback,
onAddCallback = AddElementCallback,
onRemoveCallback = RemoveElementCallback,
onReorderCallbackWithDetails = ReorderCallback,
};
var canResize = property.TryGetSerializedProperty(out _) || !property.FieldType.IsArray;
_reorderableListGui.displayAdd &= canResize;
_reorderableListGui.displayRemove &= canResize;
if (!_reorderableListGui.displayAdd && !_reorderableListGui.displayRemove)
{
_reorderableListGui.footerHeight = 0f;
@ -51,10 +49,16 @@ namespace TriInspector.Elements
{
_reorderableListGui.serializedProperty = serializedProperty;
}
else
else if (_property.Value != null)
{
_reorderableListGui.list = (IList) _property.Value;
}
else if (_reorderableListGui.list == null)
{
_reorderableListGui.list = (IList) (_property.FieldType.IsArray
? Array.CreateInstance(_property.ArrayElementType, 0)
: Activator.CreateInstance(_property.FieldType));
}
if (_alwaysExpanded && !_property.IsExpanded)
{
@ -82,11 +86,6 @@ namespace TriInspector.Elements
public override float GetHeight(float width)
{
if (_property.Value == null)
{
return EditorGUIUtility.singleLineHeight;
}
if (!_property.IsExpanded)
{
return _reorderableListGui.headerHeight + 4f;
@ -99,12 +98,6 @@ namespace TriInspector.Elements
public override void OnGUI(Rect position)
{
if (_property.Value == null)
{
EditorGUI.LabelField(position, _property.DisplayNameContent, ListIsNullContent);
return;
}
position = EditorGUI.IndentedRect(position);
if (!_property.IsExpanded)
@ -124,20 +117,115 @@ namespace TriInspector.Elements
}
}
private bool GenerateChildren()
private void AddElementCallback(ReorderableList reorderableList)
{
if (_property.Value == null)
if (_property.TryGetSerializedProperty(out _))
{
if (ChildrenCount == 0)
{
return false;
}
ClearChildren();
return true;
ReorderableList.defaultBehaviours.DoAddButton(reorderableList);
return;
}
var template = CloneValue(_property);
_property.SetValues(targetIndex =>
{
var value = (IList) _property.GetValue(targetIndex);
if (_property.FieldType.IsArray)
{
var array = Array.CreateInstance(_property.ArrayElementType, template.Length + 1);
Array.Copy(template, array, template.Length);
value = array;
}
else
{
if (value == null)
{
value = (IList) Activator.CreateInstance(_property.FieldType);
}
var newElement = CreateDefaultElementValue(_property);
value.Add(newElement);
}
return value;
});
}
private void RemoveElementCallback(ReorderableList reorderableList)
{
if (_property.TryGetSerializedProperty(out _))
{
ReorderableList.defaultBehaviours.DoRemoveButton(reorderableList);
return;
}
var template = CloneValue(_property);
var ind = reorderableList.index;
_property.SetValues(targetIndex =>
{
var value = (IList) _property.GetValue(targetIndex);
if (_property.FieldType.IsArray)
{
var array = Array.CreateInstance(_property.ArrayElementType, template.Length - 1);
Array.Copy(template, 0, array, 0, ind);
Array.Copy(template, ind + 1, array, ind, array.Length - ind);
value = array;
}
else
{
value?.RemoveAt(ind);
}
return value;
});
}
private void ReorderCallback(ReorderableList list, int oldIndex, int newIndex)
{
if (_property.TryGetSerializedProperty(out _))
{
return;
}
var mainValue = _property.Value;
_property.SetValues(targetIndex =>
{
var value = (IList) _property.GetValue(targetIndex);
if (value == mainValue)
{
return value;
}
var element = value[oldIndex];
for (var index = 0; index < value.Count - 1; ++index)
{
if (index >= oldIndex)
{
value[index] = value[index + 1];
}
}
for (var index = value.Count - 1; index > 0; --index)
{
if (index > newIndex)
{
value[index] = value[index - 1];
}
}
value[newIndex] = element;
return value;
});
}
private bool GenerateChildren()
{
var count = _reorderableListGui.count;
if (ChildrenCount == count)
@ -219,6 +307,22 @@ namespace TriInspector.Elements
return GetChild(index).GetHeight(_lastContentWidth);
}
private static object CreateDefaultElementValue(TriProperty property)
{
var canActivate = property.ArrayElementType.IsValueType ||
property.ArrayElementType.GetConstructor(Type.EmptyTypes) != null;
return canActivate ? Activator.CreateInstance(property.ArrayElementType) : null;
}
private static Array CloneValue(TriProperty property)
{
var list = (IList) property.Value;
var template = Array.CreateInstance(property.ArrayElementType, list?.Count ?? 0);
list?.CopyTo(template, 0);
return template;
}
private static class Styles
{
public static readonly GUIStyle ItemsCount;

View File

@ -236,6 +236,16 @@ namespace TriInspector
ModifyAndRecordForUndo(targetIndex => SetValueRecursive(this, value, targetIndex));
}
[PublicAPI]
public void SetValues(Func<int, object> getValue)
{
ModifyAndRecordForUndo(targetIndex =>
{
var value = getValue.Invoke(targetIndex);
SetValueRecursive(this, value, targetIndex);
});
}
public void ModifyAndRecordForUndo(Action<int> call)
{
// save any pending changes