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);
        }
Example #2
0
        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();
        }
Example #3
0
        //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);
        }
Example #4
0
        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;
                            }
                        }
                    }
                }
            }
        }
Example #5
0
        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);
                }
            });
        }
Example #7
0
        /// <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;
            }
        }
Example #8
0
 /// <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));
 }