public bool TryGetRedirectedProperty(string childName, out InspectorProperty property) { this.EnsureUpdated(); property = null; if (childName.Length == 0 || childName[0] != '{') { return(false); } try { var key = (TKey)DictionaryKeyUtility.GetDictionaryKeyValue(childName, typeof(TKey)); var keyList = this.keys[0]; for (int i = 0; i < keyList.Count; i++) { if (PropertyValueEntry <TKey> .EqualityComparer(key, keyList[i])) { property = this.Property.Children[i].Children["Value"]; return(true); } } } catch (Exception) { return(false); } return(false); }
private void UpdateValueEntry() { // Ensure we have the right sort of value entry if (this.Info.PropertyType != PropertyType.Value) { // Groups and methods have no value entries if (this.ValueEntry != null || this.BaseValueEntry != null) { this.ValueEntry = null; this.BaseValueEntry = null; this.RefreshSetup(); } return; } this.BaseValueEntry.Update(); if (!this.Info.TypeOfValue.IsValueType) { Type containedType = this.BaseValueEntry.TypeOfValue; if (containedType != this.BaseValueEntry.BaseValueType) { if (this.ValueEntry == null || (this.ValueEntry.IsAlias && this.ValueEntry.TypeOfValue != containedType) || (!this.ValueEntry.IsAlias && this.ValueEntry.TypeOfValue != this.ValueEntry.BaseValueType)) { this.ValueEntry = PropertyValueEntry.CreateAlias(this.BaseValueEntry, containedType); this.RefreshSetup(); } } else if (this.ValueEntry != this.BaseValueEntry) { this.ValueEntry = this.BaseValueEntry; this.RefreshSetup(); } } else if (this.ValueEntry == null) { this.ValueEntry = this.BaseValueEntry; this.RefreshSetup(); } this.ValueEntry.Update(); }
//internal void ForceUpdatePropertyNameAndPath(int? newIndex) //{ // if (newIndex != null) // { // this.Index = newIndex.Value; // } // this.unityPropertyPath = null; // this.prefabModificationPath = null; // if (this.Parent != null) // { // this.Path = this.Parent.Children.GetPath(this.Index); // } // else // { // this.Path = this.Info.PropertyName; // } // // Children may be null, as this may be called before the property has ever updated itself // if (this.Children != null) // { // this.Children.ClearCaches(); // for (int i = 0; i < this.Children.Count; i++) // { // this.Children[i].ForceUpdatePropertyNameAndPath(null); // } // } //} internal static InspectorProperty Create(PropertyTree tree, InspectorProperty parent, InspectorPropertyInfo info, int index, bool isSecretRoot) { // Validate parameters first if (tree == null) { throw new ArgumentNullException("tree"); } if (info == null) { throw new ArgumentNullException("info"); } if (parent != null) { if (tree != parent.Tree) { throw new ArgumentException("The given tree and the given parent's tree are not the same tree."); } if (index < 0 || index >= parent.Children.Count) { throw new IndexOutOfRangeException("The given index for the property to create is out of bounds."); } } // Now start building a property InspectorProperty property = new InspectorProperty(); // Set some basic values property.Tree = tree; property.Info = info; property.Parent = parent; property.Index = index; property.Context = new PropertyContextContainer(property); // Find property path { if (parent != null) { property.Path = parent.Children.GetPath(index); } else { property.Path = info.PropertyName; } if (property.Path == null) { Debug.Log("Property path is null for property " + ObjectNames.NicifyVariableName(info.PropertyName.TrimStart('#', '$')) + "!"); } } // Find parent value property if (parent != null) { InspectorProperty current = property; do { current = current.Parent; }while (current != null && current.BaseValueEntry == null); property.ParentValueProperty = current; } // Set parent type and values if (property.ParentValueProperty != null) { property.ParentType = property.ParentValueProperty.ValueEntry.TypeOfValue; property.ParentValues = new ImmutableList(property.ParentValueProperty.ValueEntry.WeakValues); } else { property.ParentType = tree.TargetType; property.ParentValues = new ImmutableList(tree.WeakTargets); } // Find serializing/owning property { InspectorProperty current = property.ParentValueProperty; while (current != null && !current.ValueEntry.TypeOfValue.InheritsFrom(typeof(UnityEngine.Object))) { current = current.ParentValueProperty; } if (current != null) { property.SerializationRoot = current; } else { property.SerializationRoot = isSecretRoot ? property : tree.SecretRootProperty; } } // Set name and label { property.Name = info.PropertyName; var mi = property.Info.GetMemberInfo() as MethodInfo; if (mi != null) { var name = property.Name; var parensIndex = name.IndexOf('('); if (parensIndex >= 0) { name = name.Substring(0, parensIndex); } property.NiceName = name.TrimStart('#', '$').SplitPascalCase(); } else { property.NiceName = ObjectNames.NicifyVariableName(property.Name.TrimStart('#', '$')); } property.Label = new GUIContent(property.NiceName); } // Create a value entry if necessary if (property.Info.PropertyType == PropertyType.Value) { property.BaseValueEntry = PropertyValueEntry.Create(property, info.TypeOfValue, isSecretRoot); property.ValueEntry = property.BaseValueEntry; } // Do NOT update the property here. Property updating may cause this property to be requested before // it has been registered, resulting in an infinite loop. It is the calling code's responsibility to // update the property before usage. if (!isSecretRoot) { property.RefreshProcessedAttributes(); property.ChildResolver = tree.PropertyResolverLocator.GetResolver(property); property.Children = new PropertyChildren(property); } return(property); }
private void EnsureUpdated() { if (this.valueEntry.Property.Tree.UpdateID != this.lastUpdateID) { this.dictIndexMap.Clear(); this.lastUpdateID = this.valueEntry.Property.Tree.UpdateID; for (int i = 0; i < this.keys.Length; i++) { // Swap lists and keep the old one for a change comparison var oldKeyList = this.keys[i]; var keyList = this.oldKeys[i]; this.oldKeys[i] = oldKeyList; this.keys[i] = keyList; keyList.Clear(); var dict = this.valueEntry.Values[i]; if (object.ReferenceEquals(dict, null)) { continue; } this.dictIndexMap[dict] = i; var castDict = dict as Dictionary <TKey, TValue>; if (castDict != null) { // Reduce garbage allocation foreach (var pair in castDict.GFIterator()) { keyList.Add(pair.Key); } } else { foreach (var key in dict.Keys) { keyList.Add(key); } } if (keyList.Count > 1) { var comparer = DictionaryKeyUtility.KeyComparer <TKey> .Default; var a = keyList[0]; for (int j = 1; j < keyList.Count; j++) { var b = keyList[j]; if (comparer.Compare(a, b) > 0) { keyList.Sort(comparer); break; } a = b; } } if (keyList.Count != oldKeyList.Count) { this.OnChangedUpdatePaths(); } else { for (int j = 0; j < keyList.Count; j++) { if (!PropertyValueEntry <TKey> .EqualityComparer(keyList[j], oldKeyList[j])) { this.OnChangedUpdatePaths(); break; } } } } } }
internal static InspectorProperty Create(PropertyTree tree, InspectorProperty parent, InspectorPropertyInfo info, int index) { // Validate parameters first if (tree == null) { throw new ArgumentNullException("tree"); } if (info == null) { if (parent == null) { throw new ArgumentException("A parent is expected when the given InspectorPropertyInfo is null."); } if (parent.Children.IsCollection == false) { throw new ArgumentException("The children of the given parent must be from a collection when the given InspectorPropertyInfo is null."); } } if (parent != null) { if (tree != parent.Tree) { throw new ArgumentException("The given tree and the given parent's tree are not the same tree."); } if (parent.Children is DisabledPropertyChildren) { throw new ArgumentException("A given parent must be able to have children to create a child property for it."); } if (index < 0 || index >= parent.Children.Count) { throw new IndexOutOfRangeException("The given index for the property to create is out of bounds."); } } else { index = -1; } // Now start building a property InspectorProperty property = new InspectorProperty(); property.Tree = tree; if (parent != null) { property.Path = parent.Children.GetPath(index); } else { property.Path = info.PropertyName; } property.Parent = parent; property.Index = index; property.Context = new PropertyContextContainer(property); if (property.Path == null) { Debug.Log("Property path is null for property " + property.NiceName + "!"); } { InspectorProperty current = property; do { current = current.Parent; }while (current != null && current.BaseValueEntry == null); property.ParentValueProperty = current; } if (property.ParentValueProperty != null) { property.ParentType = property.ParentValueProperty.ValueEntry.TypeOfValue; property.ParentValues = new ImmutableList(property.ParentValueProperty.ValueEntry.WeakValues); } else { property.ParentType = tree.TargetType; property.ParentValues = new ImmutableList(tree.WeakTargets); } if (info != null) { property.Info = info; property.Name = info.PropertyName; property.NiceName = ObjectNames.NicifyVariableName(property.Name.TrimStart('#')); property.Label = new GUIContent(property.NiceName); } else { // Collection elements inherit the info of the collection itself // and have their name set a little further down property.Info = parent.Info; } if (property.Info.PropertyType == PropertyType.ValueType || property.Info.PropertyType == PropertyType.ReferenceType) { property.BaseValueEntry = PropertyValueEntry.Create(property, InspectorProperty.GetBaseContainedValueType(property)); property.ValueEntry = property.BaseValueEntry; } if (info == null) { property.ForceUpdatePropertyNameAndPath(index); } // Do NOT update the property here. Property updating may cause this property to be requested before // it has been registered, resulting in an infinite loop. It is the calling code's responsibility to // update the property before usage. return(property); }
private ValueSetter <TDictionary, EditableKeyValuePair <TKey, TValue> > CreateSetter(int childIndex) { return((ref TDictionary dict, EditableKeyValuePair <TKey, TValue> value) => { this.EnsureUpdated(); var keys = this.keys[this.dictIndexMap[dict]]; if (childIndex >= keys.Count) { this.Update(); keys = this.keys[this.dictIndexMap[dict]]; } TKey oldKey = keys[childIndex]; TValue oldValue; dict.TryGetValue(oldKey, out oldValue); TKey newKey = value.Key; TValue newValue = value.Value; bool keysAreEqual = PropertyValueEntry <TKey> .EqualityComparer(oldKey, newKey); if (!keysAreEqual) { // Key has changed if (dict.ContainsKey(newKey)) { // Ignore if new key already exists in dictionary // and assign a temporary invalid key this.tempKeys[childIndex] = new TempKeyInfo() { Key = newKey, IsInvalid = true }; } else if (!this.ValueApplyIsTemporary) { bool isPrefab = this.Property.SupportsPrefabModifications; this.tempKeys.Remove(childIndex); dict.Remove(oldKey); dict.Add(newKey, newValue); if (isPrefab) { for (int i = 0; i < this.Property.Tree.WeakTargets.Count; i++) { this.Property.Tree.PrefabModificationHandler.RegisterPrefabDictionaryRemoveKeyModification(this.Property, i, oldKey); this.Property.Tree.PrefabModificationHandler.RegisterPrefabDictionaryAddKeyModification(this.Property, i, newKey); } } // // Changing just one key may have changed the entire ordering of the dictionary. // Keep everything valid by refreshing all properties. // this.childInfos.Clear(); this.Property.Children.ClearAndDisposeChildren(); // // Get the value entry which now represents the new key, and register a value // modification for it immediately, so as not to lose the old value. // // ( Calling update with a new value compared to the prefab registers the // appropriate value modifications immediately ) // if (isPrefab) { this.Update(); string keyStr = DictionaryKeyUtility.GetDictionaryKeyString(newKey); var keyEntry = this.Property.Children[keyStr]; keyEntry.Update(true); foreach (var child in keyEntry.Children.Recurse()) { child.Update(true); } } } else { this.tempKeys[childIndex] = new TempKeyInfo() { Key = newKey, IsInvalid = false }; } } else if (!PropertyValueEntry <TValue> .EqualityComparer(oldValue, newValue)) { // Only value has changed, this is much simpler dict[newKey] = newValue; } if (value.IsTempKey && keysAreEqual) { // The temp key set has set the same key back, so it's not invalid any more; it was cancelled this.tempKeys.Remove(childIndex); } }); }
/// <summary> /// Sets the actual target tree value. /// </summary> protected override void SetActualValueImplementation(int index, EditableKeyValuePair <TKey, TValue> value) { var parentProperty = this.Property.Parent; var parentEntry = (IPropertyValueEntry <TDictionary>)parentProperty.ValueEntry; var handler = (IDictionaryHandler <TKey>)parentEntry.GetDictionaryHandler(); var dict = parentEntry.Values[index]; TKey oldKey = handler.GetKey(index, this.Property.Index); TValue oldValue; dict.TryGetValue(oldKey, out oldValue); TKey newKey = value.Key; TValue newValue = value.Value; if (!PropertyValueEntry <TKey> .EqualityComparer(oldKey, newKey)) { // Key has changed; ignore if new key already exists in dictionary if (dict.ContainsKey(newKey)) { this.hasTempInvalidKey = true; this.tempInvalidKey = newKey; } else { bool isPrefab = this.SerializationBackend == SerializationBackend.ODIN && this.Property.Tree.HasPrefabs; this.hasTempInvalidKey = false; this.tempInvalidKey = default(TKey); dict.Remove(oldKey); dict.Add(newKey, newValue); if (isPrefab && handler.SupportsPrefabModifications) { this.Property.Tree.RegisterPrefabDictionaryRemoveKeyModification(parentProperty, index, oldKey); this.Property.Tree.RegisterPrefabDictionaryAddKeyModification(parentProperty, index, newKey); } // // Changing just one key may have changed the entire ordering of the dictionary. // Keep everything valid by refreshing the names and paths of every single child // property of the dictionary. // handler.ForceUpdate(); parentProperty.Children.ClearPathCache(); parentProperty.Children.Update(); for (int i = 0; i < parentProperty.Children.Count; i++) { parentProperty.Children[i].ForceUpdatePropertyNameAndPath(i); } // // Get the value entry which now represents the new key, and register a value // modification for it immediately, so as not to lose the old value. // if (isPrefab) { string childName = DictionaryKeyUtility.GetDictionaryKeyString(newKey); var child = parentProperty.Children[childName]; child.ValueEntry.Update(); child = child.Children["Value"]; child.ValueEntry.Update(); if (handler.SupportsPrefabModifications) { this.Property.Tree.RegisterPrefabValueModification(child, index, forceImmediate: true); } } } } else if (!PropertyValueEntry <TValue> .EqualityComparer(oldValue, newValue)) { // Only value has changed, this is much simpler dict[newKey] = newValue; } }
/// <summary> /// Not yet documented. /// </summary> public bool Equals(EditableKeyValuePair <TKey, TValue> other) { // We consider these to be equal if only the key is equal return(PropertyValueEntry <TKey> .EqualityComparer(this.Key, other.Key)); }