Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Editor/Scripts/Data/PropertyData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ namespace AYellowpaper.SerializedCollections.Editor.Data
[System.Serializable]
internal class PropertyData
{
[SerializeField]
private float _keyLabelWidth;
[SerializeField]
private ElementData _keyData;
[SerializeField]
Expand All @@ -20,6 +22,12 @@ public bool AlwaysShowSearch
set => _alwaysShowSearch = value;
}

public float KeyLabelWidth
{
get => _keyLabelWidth;
set => _keyLabelWidth = value;
}

public ElementData GetElementData(bool fieldType)
{
return fieldType == SCEditorUtility.KeyFlag ? _keyData : _valueData;
Expand Down
5 changes: 4 additions & 1 deletion Editor/Scripts/SerializedDictionaryDrawer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@

namespace AYellowpaper.SerializedCollections.Editor
{
[CustomPropertyDrawer(typeof(SerializedDictionary<,>))]
#if ODIN_INSPECTOR
[Sirenix.OdinInspector.Editor.DrawerPriority(Sirenix.OdinInspector.Editor.DrawerPriorityLevel.SuperPriority)]
#endif
[CustomPropertyDrawer(typeof(SerializedDictionary<,>), true)]
public class SerializedDictionaryDrawer : PropertyDrawer
{
public const string KeyName = nameof(SerializedKeyValuePair<int, int>.Key);
Expand Down
97 changes: 71 additions & 26 deletions Editor/Scripts/SerializedDictionaryInstanceDrawer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ namespace AYellowpaper.SerializedCollections.Editor
{
public class SerializedDictionaryInstanceDrawer
{
private const float MinKeyValueLabelWidth = 40f;

private FieldInfo _fieldInfo;
private ReorderableList _unexpandedList;
private SingleEditingData _singleEditingData;
Expand Down Expand Up @@ -156,17 +158,30 @@ private void UpdateAfterInput()
{
InitializeSettingsIfNeeded();
ProcessState();
CheckIfNewDictionary();
CheckPaging();
var elementsPerPage = EditorUserSettings.Get().ElementsPerPage;
int pageCount = Mathf.Max(1, Mathf.CeilToInt((float)DefaultState.ListSize / elementsPerPage));
ToggleSearchBar(_propertyData.AlwaysShowSearch ? true : SCEditorUtility.ShouldShowSearch(pageCount));
}

// TODO: This works for now, but isn't perfect. This checks if the serialized dictionary was reassigned with new(), simply by comparing the count. Should be instead done by reference equality in the future
private void CheckIfNewDictionary()
{
if (_singleEditingData.IsValid && _singleEditingData.LookupTable.GetCount() != _activeState.ListSize)
{
var dictionary = SCEditorUtility.GetPropertyValue(ListProperty, ListProperty.serializedObject.targetObject);
_singleEditingData.LookupTable = GetLookupTable(dictionary);
_singleEditingData.LookupTable.RecalculateOccurences();
}
}

private void InitializeSettingsIfNeeded()
{
void InitializeSettings(bool fieldFlag)
{
var genericArgs = _fieldInfo.FieldType.GetGenericArguments();
var dictionaryType = FindGenericBaseType(typeof(SerializedDictionary<,>), _fieldInfo.FieldType);
var genericArgs = dictionaryType.GetGenericArguments();
var firstProperty = ListProperty.GetArrayElementAtIndex(0);
var keySettings = CreateDisplaySettings(GetElementProperty(firstProperty, fieldFlag), genericArgs[fieldFlag == SCEditorUtility.KeyFlag ? 0 : 1]);
var settings = _propertyData.GetElementData(fieldFlag).Settings;
Expand All @@ -183,6 +198,19 @@ void InitializeSettings(bool fieldFlag)
}
}

private static Type FindGenericBaseType(Type generic, Type toCheck)
{
while (toCheck != null && toCheck != typeof(object))
{
var cur = toCheck.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck;
if (generic == cur) {
return toCheck;
}
toCheck = toCheck.BaseType;
}
return null;
}

private void CheckPaging()
{
// TODO: Is there a better solution to check for Revert/delete/add?
Expand Down Expand Up @@ -253,7 +281,7 @@ private ReorderableList MakeList()
private ReorderableList MakeUnexpandedList()
{
var list = new ReorderableList(SerializedDictionaryDrawer.NoEntriesList, typeof(int));
list.drawHeaderCallback = DrawUnexpandedHeader;
list.drawHeaderCallback = OnDrawUnexpandedHeader;
return list;
}

Expand All @@ -276,7 +304,7 @@ private void OnDrawNoneElement(Rect rect)

private (DisplayType displayType, bool canToggleListDrawer) CreateDisplaySettings(SerializedProperty property, Type type)
{
bool hasCustomEditor = SCEditorUtility.HasDrawerForType(type);
bool hasCustomEditor = SCEditorUtility.HasDrawerForProperty(property, type);
bool isGenericWithChildren = property.propertyType == SerializedPropertyType.Generic && property.hasVisibleChildren;
bool isArray = property.isArray && property.propertyType != SerializedPropertyType.String;
bool canToggleListDrawer = isArray || (isGenericWithChildren && hasCustomEditor);
Expand All @@ -288,18 +316,6 @@ private void OnDrawNoneElement(Rect rect)
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();
Expand All @@ -326,6 +342,20 @@ private void OnDrawHeader(Rect rect)

UpdateAfterInput();
}

private void OnDrawUnexpandedHeader(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();

UpdateAfterInput();
}

private void DoMainHeader(Rect rect)
{
Expand Down Expand Up @@ -405,14 +435,26 @@ private void ToggleAlwaysShowSearchPropertyData()

private void DoKeyValueRect(Rect rect)
{
float width = EditorGUIUtility.labelWidth + 22;
var width = GetDesiredKeyLabelWidth(rect.width, 22);
Rect leftRect = rect.WithWidth(width);
Rect rightRect = leftRect.AppendRight(rect.width - width);

if (Event.current.type == EventType.Repaint && _propertyData != null)
if (_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 (Event.current.type == EventType.Repaint)
{
_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);
}
var changeSizeRect = leftRect.AppendRight(5);
changeSizeRect.x -= 2;
EditorGUI.BeginChangeCheck();
float newWidth = SCEditorUtility.DoHorizontalScale(changeSizeRect, _propertyData.KeyLabelWidth > 0f ? _propertyData.KeyLabelWidth : width);
if (EditorGUI.EndChangeCheck())
{
_propertyData.KeyLabelWidth = Mathf.Max(newWidth, MinKeyValueLabelWidth);
SavePropertyData();
}
}

if (ListProperty.minArraySize > 0)
Expand All @@ -424,6 +466,14 @@ private void DoKeyValueRect(Rect rect)
EditorGUI.DrawRect(rect.AppendDown(1, -1), SerializedDictionaryDrawer.BorderColor);
}

private float GetDesiredKeyLabelWidth(float maxWidth, float offset = 0f)
{
float desiredWidth = _propertyData is { KeyLabelWidth: > 0 }
? _propertyData.KeyLabelWidth
: EditorGUIUtility.labelWidth;
return Mathf.Clamp(desiredWidth + offset, MinKeyValueLabelWidth, maxWidth - MinKeyValueLabelWidth);
}

private void DoSearch(Rect rect)
{
EditorGUI.DrawRect(rect.AppendLeft(1), SerializedDictionaryDrawer.BorderColor);
Expand Down Expand Up @@ -555,7 +605,7 @@ 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);
return CalculateHeightOfElement(element, _propertyData.GetElementData(SerializedDictionaryDrawer.KeyFlag).EffectiveDisplayType == DisplayType.List, _propertyData.GetElementData(SerializedDictionaryDrawer.ValueFlag).EffectiveDisplayType == DisplayType.List);
}

private void OnDrawElement(Rect rect, int index, bool isActive, bool isFocused)
Expand All @@ -568,7 +618,7 @@ private void OnDrawElement(Rect rect, int index, bool isActive, bool isFocused)
int actualIndex = _pagedIndices[index];

SerializedProperty kvp = _activeState.GetPropertyAtIndex(actualIndex);
Rect keyRect = rect.WithSize(EditorGUIUtility.labelWidth - lineLeftSpace, EditorGUIUtility.singleLineHeight);
Rect keyRect = rect.WithSize(GetDesiredKeyLabelWidth(rect.width) - 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);

Expand Down Expand Up @@ -673,11 +723,6 @@ 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;
}
}
}
109 changes: 84 additions & 25 deletions Editor/Scripts/Utility/SCEditorUtility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,6 @@ internal static class SCEditorUtility
public const bool KeyFlag = true;
public const bool ValueFlag = false;

public static bool GetPersistentBool(string path, bool defaultValue)
{
return EditorPrefs.GetBool(EditorPrefsPrefix + path, defaultValue);
}

public static bool HasKey(string path)
{
return EditorPrefs.HasKey( EditorPrefsPrefix + path );
}

public static void SetPersistentBool(string path, bool value)
{
EditorPrefs.SetBool(EditorPrefsPrefix + path, value);
}

public static float CalculateHeight(SerializedProperty property, DisplayType displayType)
{
return CalculateHeight(property, displayType == DisplayType.List ? true : false);
Expand Down Expand Up @@ -64,11 +49,6 @@ public static IEnumerable<SerializedProperty> GetChildren(SerializedProperty pro
} while (property.NextVisible(recursive) && !SerializedProperty.EqualContents(property, end));
}

public static int GetActualArraySize(SerializedProperty arrayProperty)
{
return GetChildren(arrayProperty).Count() - 1;
}

public static PropertyData GetPropertyData(SerializedProperty property)
{
var data = new PropertyData();
Expand All @@ -90,15 +70,15 @@ public static bool ShouldShowSearch(int pages)
return settings.AlwaysShowSearch ? true : pages >= settings.PageCountForSearch;
}

public static bool HasDrawerForType(Type type)
public static bool HasDrawerForProperty(SerializedProperty property, Type type)
{
Type attributeUtilityType = typeof(SerializedProperty).Assembly.GetType("UnityEditor.ScriptAttributeUtility");
if (attributeUtilityType == null)
return false;
var getDrawerMethod = attributeUtilityType.GetMethod("GetDrawerTypeForType", BindingFlags.Static | BindingFlags.NonPublic);
var getDrawerMethod = attributeUtilityType.GetMethod("GetDrawerTypeForPropertyAndType", BindingFlags.Static | BindingFlags.NonPublic);
if (getDrawerMethod == null)
return false;
return getDrawerMethod.Invoke(null, new object[] { type }) != null;
return getDrawerMethod.Invoke(null, new object[] { property, type }) != null;
}

internal static void AddGenericMenuItem(GenericMenu genericMenu, bool isOn, bool isEnabled, GUIContent content, GenericMenu.MenuFunction action)
Expand Down Expand Up @@ -134,8 +114,71 @@ internal static bool TryGetTypeFromProperty(SerializedProperty property, out Typ
return false;
}
}

internal static float DoHorizontalScale(Rect rect, float value)
{
var controlId = GUIUtility.GetControlID(FocusType.Passive);
var isMovingMouse = Event.current.type == EventType.MouseDrag;
DoButtonControl(rect, controlId, false, false, GUIContent.none, GUIStyle.none);

if (controlId == GUIUtility.hotControl && isMovingMouse)
{
value += Event.current.delta.x;
GUI.changed = true;
}

EditorGUIUtility.AddCursorRect(rect, MouseCursor.ResizeHorizontal);

return value;
}

internal static bool DoButtonControl(Rect rect, int id, bool on, bool hover, GUIContent content, GUIStyle style)
{
Event current = Event.current;
switch (current.type)
{
case EventType.MouseDown:
if (HitTest(rect, current.mousePosition))
{
GUIUtility.hotControl = id;
current.Use();
}
break;
case EventType.MouseUp:
if (GUIUtility.hotControl == id)
{
GUIUtility.hotControl = 0;
current.Use();
if (HitTest(rect, current.mousePosition))
{
GUI.changed = true;
return !on;
}
}
break;
case EventType.MouseDrag:
if (GUIUtility.hotControl == id)
{
current.Use();
}
break;
case EventType.KeyDown:
bool flag = current.alt || current.shift || current.command || current.control;
if ((current.keyCode == KeyCode.Space || current.keyCode == KeyCode.Return || current.keyCode == KeyCode.KeypadEnter) && !flag && GUIUtility.keyboardControl == id)
{
current.Use();
GUI.changed = true;
return !on;
}
break;
case EventType.Repaint:
style.Draw(rect, content, id, on, hover);
break;
}
return on;
}

internal static bool HitTest(Rect rect, Vector2 point) => point.x >= rect.xMin && point.x < rect.xMax && point.y >= rect.yMin && point.y < rect.yMax;


public static object GetPropertyValue(SerializedProperty prop, object target)
Expand Down Expand Up @@ -163,10 +206,10 @@ public static object GetValue(object source, string name)
if (source == null)
return null;
var type = source.GetType();
var f = type.GetField(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
var f = type.GetFieldRecursive(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
if (f == null)
{
var p = type.GetProperty(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
var p = type.GetPropertyRecursive(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
if (p == null)
return null;
return p.GetValue(source, null);
Expand All @@ -182,5 +225,21 @@ public static object GetValue(object source, string name, int index)
enm.MoveNext();
return enm.Current;
}

private static FieldInfo GetFieldRecursive(this Type type, string name, BindingFlags bindingFlags)
{
var fieldInfo = type.GetField(name, bindingFlags);
if (fieldInfo == null && type.BaseType != null)
return type.BaseType.GetFieldRecursive(name, bindingFlags);
return fieldInfo;
}

private static PropertyInfo GetPropertyRecursive(this Type type, string name, BindingFlags bindingFlags)
{
var propertyInfo = type.GetProperty(name, bindingFlags);
if (propertyInfo == null && type.BaseType != null)
return type.BaseType.GetPropertyRecursive(name, bindingFlags);
return propertyInfo;
}
}
}
Loading