using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using PrefsGUI.Utility; using UnityEditor; using UnityEditor.UIElements; using UnityEngine.Assertions; using UnityEngine.Pool; using UnityEngine.UIElements; namespace PrefsGUI.Editor.Utility { public class SerializableDictionaryInspectorUpdater { private const string ListPropertyName = "_list"; private static SerializedProperty GetListSerializedProperty(SerializedProperty property) { var listProp = property.FindPropertyRelative(ListPropertyName); Assert.IsNotNull(listProp); return listProp; } private readonly SerializedProperty _rootProperty; private IVisualElementScheduledItem _scheduleItem; public PropertyField Field { get; } public SerializableDictionaryInspectorUpdater(SerializedProperty property) { var name = property.displayName; _rootProperty = property; Field = new PropertyField(GetListSerializedProperty(property), name); Field.RegisterCallback(_ => { Field.RegisterCallback(OnSerializedPropertyChanged); Field.schedule.Execute(CheckAndUpdateUIForDuplicateKey); }); Field.RegisterCallback(_ => { Field.UnregisterCallback(OnSerializedPropertyChanged); _scheduleItem?.Pause(); _scheduleItem = null; }); } private void OnSerializedPropertyChanged(SerializedPropertyChangeEvent evt) { if (_scheduleItem != null) return; // プロパティのパスからDictionaryのKeyの変更を検知する // _rootProperty.propertyPathはアプリケーション側で自由につけれるので // 正規表現のメタ文字が入ってくる可能性があるためあらかじめ省く var path = evt.changedProperty.propertyPath; var rootPath = _rootProperty.propertyPath; if (!path.StartsWith($"{rootPath}.")) return; path = path[(rootPath.Length + 1)..]; var keyPath = $@"{ListPropertyName}\.Array\.data\[\d+\]\.key$"; if (!Regex.IsMatch(path, keyPath)) return; _scheduleItem = Field.schedule.Execute(CheckAndUpdateUIForDuplicateKey); } private void CheckAndUpdateUIForDuplicateKey() { _scheduleItem = null; if (_rootProperty.GetActualObject() is not ISerializableDictionaryForUI serializableDictionary) { return; } var itemCount = serializableDictionary.SerializableItemCount; // インデックスから、所属する重複キーのインデックスリストを求めるテーブル using var _ = ListPool>.Get(out var indexToSameKeyIndexGroup); indexToSameKeyIndexGroup.AddRange(Enumerable.Repeat>(null, itemCount)); foreach (var sameKeyIndexGroup in serializableDictionary.GetSameKeyIndexGroups()) { var list = ListPool.Get(); list.AddRange(sameKeyIndexGroup); foreach (var index in list) { indexToSameKeyIndexGroup[index] = list; } } // PropertyFieldごとにマークを脱着 var propertyFieldsAll = Field.Query().Build(); for(var i=0; i pf.bindingPath == path); if (propertyField == null) continue; VisualElement mark = propertyField.Q