// MIT License - Copyright (c) 2025 wallstop // Full license text: https://github.com/wallstop/unity-helpers/blob/main/LICENSE namespace WallstopStudios.UnityHelpers.Editor.CustomDrawers { using System; using System.Collections.Generic; using System.Reflection; using UnityEditor; using WallstopStudios.UnityHelpers.Core.Attributes; using WallstopStudios.UnityHelpers.Core.Helper; using WallstopStudios.UnityHelpers.Editor.Settings; /// /// Provides shared helpers for initializing SerializableDictionary/SerializableSet foldout states. /// internal static class WSerializableCollectionFoldoutUtility { private static readonly HashSet InitializedKeys = new(); internal static void EnsureFoldoutInitialized( SerializedProperty property, FieldInfo fieldInfo, SerializableCollectionType collectionType ) { if (property == null) { return; } if (!MarkInitialized(property)) { return; } bool startExpanded = ResolveStartExpanded(fieldInfo, collectionType); if (!startExpanded || property.isExpanded) { return; } property.isExpanded = true; } private static bool MarkInitialized(SerializedProperty property) { SerializedObject serializedObject = property.serializedObject; bool added = false; if (serializedObject == null) { added |= InitializedKeys.Add( new FoldoutInitializationKey(null, property.propertyPath) ); return added; } UnityEngine.Object[] targets = serializedObject.targetObjects; if (targets == null || targets.Length == 0) { added |= InitializedKeys.Add( new FoldoutInitializationKey( serializedObject.targetObject, property.propertyPath ) ); return added; } for (int index = 0; index < targets.Length; index++) { added |= InitializedKeys.Add( new FoldoutInitializationKey(targets[index], property.propertyPath) ); } return added; } private static bool ResolveStartExpanded( FieldInfo fieldInfo, SerializableCollectionType collectionType ) { WSerializableCollectionFoldoutAttribute attribute = fieldInfo?.GetCustomAttribute(); if (attribute != null) { return attribute.StartExpanded; } return collectionType switch { SerializableCollectionType.Dictionary => !UnityHelpersSettings.ShouldStartSerializableDictionaryCollapsed(), SerializableCollectionType.Set => !UnityHelpersSettings.ShouldStartSerializableSetCollapsed(), _ => false, }; } private readonly struct FoldoutInitializationKey : IEquatable { public FoldoutInitializationKey(UnityEngine.Object target, string propertyPath) { TargetId = target != null ? target.GetInstanceID() : 0; PropertyPath = propertyPath ?? string.Empty; } private int TargetId { get; } private string PropertyPath { get; } public bool Equals(FoldoutInitializationKey other) { return TargetId == other.TargetId && string.Equals(PropertyPath, other.PropertyPath, StringComparison.Ordinal); } public override bool Equals(object obj) { return obj is FoldoutInitializationKey other && Equals(other); } public override int GetHashCode() { return Objects.HashCode(TargetId, PropertyPath); } } internal enum SerializableCollectionType { Dictionary = 0, Set = 1, } } }