Skip to content

Commit

Permalink
Merge pull request #105 from arimger/develop
Browse files Browse the repository at this point in the history
Develop - 0.12.10
  • Loading branch information
arimger authored Mar 28, 2024
2 parents 4a72fd0 + 8dd946d commit 8eb47f9
Show file tree
Hide file tree
Showing 26 changed files with 250 additions and 115 deletions.
10 changes: 10 additions & 0 deletions Assets/Editor Toolbox/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
## 0.12.0 [28.03.2024]

### Changed:
- Fix fetching and initializing Toolbox features when assets are updating
- Display ReorderableList element handle at the top of available space
- Display "Collection is Empty" label for empty ReorderableLists
- New visual appearance for the SerializedDictionary properties
- Fix using Toolbox drawers in the SerializedDictionary properties
- Change the default SceneView selector key to `Tab`

## 0.12.9 [27.01.2024]

### Changed:
Expand Down
33 changes: 15 additions & 18 deletions Assets/Editor Toolbox/Editor/Drawers/Helpers/DrawerDataStorage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,31 @@ namespace Toolbox.Editor.Drawers
/// Internal system responsible for keeping and clearing data between <see cref="UnityEditor.Editor"/>s.
/// This small system works only for attribute-based drawers and should be defined as a static field.
/// </summary>
/// <typeparam name="T">Key-related object.</typeparam>
/// <typeparam name="T1">Data to store.</typeparam>
/// <typeparam name="T2">Any type needed for storage item creation. Pass <see cref="EventArgs.Empty"/> if no additional data is needed.</typeparam>
public abstract class DrawerDataStorage<T, T1, T2> : DrawerDataStorageBase
/// <typeparam name="TKey">Key-related object.</typeparam>
/// <typeparam name="TData">Data to store.</typeparam>
/// <typeparam name="TArgs">Any type needed for storage item creation. Pass <see cref="EventArgs.Empty"/> if no additional data is needed.</typeparam>
public abstract class DrawerDataStorage<TKey, TData, TArgs> : DrawerDataStorageBase
{
protected readonly Dictionary<string, T1> items = new Dictionary<string, T1>();
protected readonly Dictionary<string, TData> items = new Dictionary<string, TData>();

protected readonly Func<T, T2, T1> createMethod;
protected readonly Action<T1> removeMethod;
protected readonly Func<TKey, TArgs, TData> createMethod;
protected readonly Action<TData> removeMethod;


public DrawerDataStorage(Func<T, T2, T1> createMethod) : this(createMethod, null)
public DrawerDataStorage(Func<TKey, TArgs, TData> createMethod) : this(createMethod, null)
{ }

public DrawerDataStorage(Func<T, T2, T1> createMethod, Action<T1> removeMethod)
public DrawerDataStorage(Func<TKey, TArgs, TData> createMethod, Action<TData> removeMethod)
{
this.createMethod = createMethod;
this.removeMethod = removeMethod;
}


protected abstract string GetKey(T context);

protected abstract string GetKey(TKey context);

/// <summary>
/// Returns and if needed creates new item.
/// </summary>
public virtual T1 ReturnItem(T context, T2 args)
public virtual TData ReturnItem(TKey context, TArgs args)
{
var key = GetKey(context);
if (items.TryGetValue(key, out var item))
Expand All @@ -47,12 +44,12 @@ public virtual T1 ReturnItem(T context, T2 args)
}
}

public virtual T1 CreateItem(T context, T2 args)
public virtual TData CreateItem(TKey context, TArgs args)
{
return CreateItem(context, args, true);
}

public virtual T1 CreateItem(T context, T2 args, bool append)
public virtual TData CreateItem(TKey context, TArgs args, bool append)
{
var item = createMethod(context, args);
if (append)
Expand All @@ -63,13 +60,13 @@ public virtual T1 CreateItem(T context, T2 args, bool append)
return item;
}

public virtual T1 AppendItem(T context, T1 item)
public virtual TData AppendItem(TKey context, TData item)
{
var key = GetKey(context);
return items[key] = item;
}

public virtual void ClearItem(T context)
public virtual void ClearItem(TKey context)
{
var key = GetKey(context);
if (removeMethod != null)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,35 +1,17 @@
using System.Collections.Generic;

namespace Toolbox.Editor.Drawers
namespace Toolbox.Editor.Drawers
{
public abstract class DrawerDataStorageBase
{
static DrawerDataStorageBase()
{
ToolboxEditorHandler.OnEditorReload += () =>
{
for (var i = 0; i < storages.Count; i++)
{
storages[i].ClearItems();
}
};
}


protected DrawerDataStorageBase()
{
storages.Add(this);
DrawerStorageManager.AppendStorage(this);
}

~DrawerDataStorageBase()
{
storages.Remove(this);
DrawerStorageManager.RemoveStorage(this);
}


private static readonly List<DrawerDataStorageBase> storages = new List<DrawerDataStorageBase>();


/// <summary>
/// Called each time Editor is completely destroyed.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;

namespace Toolbox.Editor.Drawers
{
internal static class DrawerStorageManager
{
static DrawerStorageManager()
{
ToolboxEditorHandler.OnEditorReload -= ClearStorages;
ToolboxEditorHandler.OnEditorReload += ClearStorages;
}

private static readonly List<DrawerDataStorageBase> storages = new List<DrawerDataStorageBase>();

internal static void ClearStorages()
{
ClearStorages(null);
}

internal static void ClearStorages(Func<DrawerDataStorageBase, bool> predicate)
{
for (var i = 0; i < storages.Count; i++)
{
var storage = storages[i];
if (predicate != null && !predicate(storage))
{
continue;
}

storage.ClearItems();
}
}

internal static void AppendStorage(DrawerDataStorageBase storage)
{
storages.Add(storage);
}

internal static bool RemoveStorage(DrawerDataStorageBase storage)
{
return storages.Remove(storage);
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,20 @@ static ReorderableListAttributeDrawer()
{
storage = new PropertyDataStorage<ReorderableListBase, ReorderableListAttribute>(false, (p, a) =>
{
return ToolboxEditorGui.CreateList(p,
var list = ToolboxEditorGui.CreateList(p,
a.ListStyle,
a.ElementLabel,
a.FixedSize,
a.Draggable,
a.HasHeader,
a.HasLabels,
a.Foldable);
return list;
});
}

private static readonly PropertyDataStorage<ReorderableListBase, ReorderableListAttribute> storage;


/// <summary>
/// Draws a <see cref="ReorderableList"/> if given property was previously cached or creates completely new instance.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ private Type GetParentType(ReferencePickerAttribute attribute, SerializedPropert
private void CreateTypeProperty(SerializedProperty property, Type parentType, ReferencePickerAttribute attribute, Rect position)
{
TypeUtilities.TryGetTypeFromManagedReferenceFullTypeName(property.managedReferenceFullTypename, out var currentType);
typeField.OnGui(position, true, (type) =>
typeField.OnGui(position, attribute.AddTextSearchField, (type) =>
{
try
{
Expand Down Expand Up @@ -78,6 +78,11 @@ private void UpdateTypeProperty(SerializedProperty property, Type targetType, Re
property.serializedObject.Update();
property.managedReferenceValue = obj;
property.serializedObject.ApplyModifiedProperties();

//NOTE: fix for invalid cached properties, e.g. changing parent's managed reference can change available children
// since we cannot check if cached property is "valid" we need to clear the whole cache
//TODO: reverse it and provide dedicated event when a managed property is changed through a dedicated handler
DrawerStorageManager.ClearStorages();
}

private Rect PrepareTypePropertyPosition(bool hasLabel, in Rect labelPosition, in Rect inputPosition, bool isPropertyExpanded)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,20 +46,19 @@ static SerializedDictionaryDrawer()
list.drawElementCallback += (rect, index, isActive, isFocused) =>
{
var element = pairsProperty.GetArrayElementAtIndex(index);
var content = list.GetElementContent(element, index);
var kProperty = element.FindPropertyRelative("key");
var vProperty = element.FindPropertyRelative("value");

using (var propertyScope = new PropertyScope(element, content))
var content = list.GetElementContent(element, index);
using (new EditorGUILayout.HorizontalScope())
{
if (!propertyScope.IsVisible)
{
return;
}

//draw key/value children and use new, shortened labels
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(element.FindPropertyRelative("key"), new GUIContent("K"));
EditorGUILayout.PropertyField(element.FindPropertyRelative("value"), new GUIContent("V"));
EditorGUI.indentLevel--;
var kOption = GUILayout.Width(Style.kGroupWidth);
DrawDictionaryProperty(kProperty, Style.kLabel, Style.kLabelWidth, kOption);

var vLabel = vProperty.hasVisibleChildren
? Style.vLabel
: GUIContent.none;
DrawDictionaryProperty(vProperty, vLabel, Style.vLabelWidth);
}
};
return list;
Expand All @@ -68,6 +67,19 @@ static SerializedDictionaryDrawer()

private static readonly PropertyDataStorage<ReorderableListBase, CreationArgs> storage;

private static void DrawDictionaryProperty(SerializedProperty property, GUIContent label, float labelWidth, params GUILayoutOption[] options)
{
using (new EditorGUILayout.VerticalScope(Style.propertyGroupStyle, options))
{
var indent = property.hasVisibleChildren ? 1 : 0;
using (new ChangeIndentScope(indent))
{
EditorGUIUtility.labelWidth = labelWidth;
ToolboxEditorGui.DrawToolboxProperty(property, label);
EditorGUIUtility.labelWidth = -1;
}
}
}

public override void OnGui(SerializedProperty property, GUIContent label)
{
Expand All @@ -92,11 +104,19 @@ public override bool UseForChildren()
return false;
}


private static class Style
{
internal static readonly float kLabelWidth = 40.0f;
internal static readonly float kGroupWidth = 200.0f;
internal static readonly float vLabelWidth = 150.0f;

internal static readonly float warningIconOffset = 25.0f;
internal static readonly string warningMessage = "keys are not unique, it will break deserialization";

internal static readonly GUIContent kLabel = new GUIContent("Key");
internal static readonly GUIContent vLabel = new GUIContent("Value");

internal static readonly GUIStyle propertyGroupStyle = new GUIStyle(EditorStyles.helpBox);
}

private struct CreationArgs
Expand Down
26 changes: 26 additions & 0 deletions Assets/Editor Toolbox/Editor/Internal/ChangeIndentScope.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System;
using UnityEditor;

namespace Toolbox.Editor.Internal
{
internal class ChangeIndentScope : IDisposable
{
private readonly int indentChange;

public ChangeIndentScope(int indentChange)
{
this.indentChange = indentChange;
Prepare(indentChange);
}

public void Prepare(int indentChange)
{
EditorGUI.indentLevel += indentChange;
}

public void Dispose()
{
EditorGUI.indentLevel -= indentChange;
}
}
}
11 changes: 11 additions & 0 deletions Assets/Editor Toolbox/Editor/Internal/ChangeIndentScope.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 0 additions & 6 deletions Assets/Editor Toolbox/Editor/Internal/ReorderableList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ public class ReorderableList : ReorderableListBase

public ElementHeightCallbackDelegate elementHeightCallback;


/// <summary>
/// Offset between a dragging handle and the real mouse position.
/// </summary>
Expand All @@ -24,7 +23,6 @@ public class ReorderableList : ReorderableListBase

private Rect middleRect;


public ReorderableList(SerializedProperty list)
: base(list)
{ }
Expand All @@ -45,7 +43,6 @@ public ReorderableList(SerializedProperty list, string elementLabel, bool dragga
: base(list, elementLabel, draggable, hasHeader, fixedSize, hasLabels, foldable)
{ }


protected override void DoListMiddle()
{
var rect = GUILayoutUtility.GetRect(0, MiddleHeight, GUILayout.ExpandWidth(true));
Expand Down Expand Up @@ -354,7 +351,6 @@ protected override int GetCoveredElementIndex(Vector2 mousePosition)
return middleRect.Contains(mousePosition) ? GetCoveredElementIndex(mousePosition.y) : -1;
}


private float GetRowHeight(int index)
{
return GetElementHeight(index) + ElementSpacing;
Expand Down Expand Up @@ -396,7 +392,6 @@ private Rect GetRowRect(int index, Rect listRect)
return new Rect(listRect.x, listRect.y + GetElementYOffset(index), listRect.width, GetElementHeight(index));
}


public void DoList(Rect rect)
{
var headerRect = new Rect(rect.x, rect.y, rect.width, HeaderHeight);
Expand All @@ -411,7 +406,6 @@ public void DoList(Rect rect)
}
}


public float MiddleHeight
{
get
Expand Down
Loading

0 comments on commit 8eb47f9

Please sign in to comment.