using System; using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEngine; namespace PrefsGUI.Utility { public interface ISerializableDictionaryForUI { public IEnumerable> GetSameKeyIndexGroups(); int SerializableItemCount { get; } } [Serializable] public class SerializableDictionary : IDictionary, ISerializationCallbackReceiver, ISerializableDictionaryForUI { [Serializable] public struct KeyValue { public TKey key; public TValue value; public KeyValue(TKey key, TValue value) => (this.key, this.value) = (key, value); } [SerializeField] private List _list = new(); private readonly Dictionary _dictionary; private bool _isDictionaryDirty = true; internal List SerializeList { get => _list; set { _list = value; UpdateDictionaryFromList(); } } public SerializableDictionary() => _dictionary = new Dictionary(); public SerializableDictionary(IDictionary dictionary) => _dictionary = new Dictionary(dictionary); public SerializableDictionary(IDictionary dictionary, IEqualityComparer comparer) => _dictionary = new Dictionary(dictionary, comparer); public SerializableDictionary(IEnumerable> collection) => _dictionary = new Dictionary(collection); public SerializableDictionary( IEnumerable> collection, IEqualityComparer comparer) => _dictionary = new Dictionary(collection, comparer); public SerializableDictionary(IEqualityComparer comparer) => _dictionary = new Dictionary(comparer); protected void SetDictionaryDirty() => _isDictionaryDirty = true; #region Dictionary Methods public bool ContainsValue(TValue value) => _dictionary.ContainsValue(value); public bool TryAdd(TKey key, TValue value) { var success = _dictionary.TryAdd(key, value); if (success) { SetDictionaryDirty(); } return success; } #endregion #region IEnumerable IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); #endregion #region IEnumerable>, public IEnumerator> GetEnumerator() => _dictionary.GetEnumerator(); #endregion #region ICollection> public int Count => _dictionary.Count; public bool IsReadOnly => ((ICollection>)_dictionary).IsReadOnly; public void Add(KeyValuePair item) { _dictionary.Add(item.Key, item.Value); SetDictionaryDirty(); } public void Clear() { _dictionary.Clear(); SetDictionaryDirty(); } public bool Contains(KeyValuePair item) => _dictionary.Contains(item); public void CopyTo(KeyValuePair[] array, int arrayIndex) { ((ICollection>)_dictionary).CopyTo(array, arrayIndex); } public bool Remove(KeyValuePair item) { if (!((ICollection>)_dictionary).Remove(item)) return false; SetDictionaryDirty(); return true; } #endregion #region IDictionary public TValue this[TKey key] { get => _dictionary[key]; set { _dictionary[key] = value; SetDictionaryDirty(); } } public ICollection Keys => _dictionary.Keys; public ICollection Values => _dictionary.Values; public void Add(TKey key, TValue value) { _dictionary.Add(key, value); SetDictionaryDirty(); } public bool ContainsKey(TKey key) => _dictionary.ContainsKey(key); public bool Remove(TKey key) { var success = _dictionary.Remove(key); SetDictionaryDirty(); return success; } public bool TryGetValue(TKey key, out TValue value) => _dictionary.TryGetValue(key, out value); #endregion #region ISerializationCallbackReceiver public void OnBeforeSerialize() => UpdateDictionaryFromList(); public void OnAfterDeserialize() => UpdateDictionaryFromList(); #endregion #region ISerializableDictionaryForUI public IEnumerable> GetSameKeyIndexGroups() { return _list.Select((kv, index) => (kv.key, index)) .GroupBy(kv => kv.key, _dictionary.Comparer) .Where(g => g.Count() > 1) .Select(g => g.Select(kv => kv.index).OrderBy(index => index)); } public int SerializableItemCount => _list.Count; #endregion private void UpdateDictionaryFromList() { _dictionary.Clear(); if (_list == null) return; foreach (var item in _list.Where(kv => kv.key != null)) { _dictionary.TryAdd(item.key, item.value); } } private void UpdateListFromDictionary() { if (!_isDictionaryDirty) return; _isDictionaryDirty = false; _list.Clear(); _list.AddRange(_dictionary.Select(kv => new KeyValue(kv.Key, kv.Value))); } } }