public fiDeserializedObject(fiSerializedObject serializedState) { Type targetType = serializedState.Target.Target.GetType(); var serializationOperator = new fiSerializationOperator() { SerializedObjects = serializedState.ObjectReferences }; // Fetch the serializer that the target uses Type serializerType = BehaviorTypeToSerializerTypeMap.GetSerializerType(targetType); var serializer = (BaseSerializer)fiSingletons.Get(serializerType); var inspectedType = InspectedType.Get(targetType); Members = new List <fiDeserializedMember>(); foreach (fiSerializedMember member in serializedState.Members) { InspectedProperty property = inspectedType.GetPropertyByName(member.Name); if (property != null) { object deserialized = serializer.Deserialize( fsPortableReflection.AsMemberInfo(property.StorageType), member.Value, serializationOperator); Members.Add(new fiDeserializedMember() { InspectedProperty = property, Value = deserialized, ShouldRestore = member.ShouldRestore }); } } }
/// <summary> /// The data path accesses an element inside of an IDictionary instance. /// The dictionary instance resides at |property|. /// </summary> public ObjectDataPath(InspectedProperty property, object dictKey) { byProperty = property; byListIndex = -1; byDictKey = dictKey; byType = null; }
/// <summary> /// The data path accesses an element inside of an IList instance /// (including arrays). The list instance resides at |property|. /// </summary> public ObjectDataPath(InspectedProperty property, int listIndex) { byProperty = property; byListIndex = listIndex; byDictKey = null; byType = null; }
private static bool SaveStateForProperty(ISerializedObject obj, InspectedProperty property, BaseSerializer serializer, ISerializationOperator serializationOperator, out string serializedValue, ref bool success) { object currentValue = property.Read(obj); try { if (currentValue == null) { serializedValue = null; } else { serializedValue = serializer.Serialize(property.MemberInfo, currentValue, serializationOperator); } return(true); } catch (Exception e) { success = false; serializedValue = null; Debug.LogError("Exception caught when serializing property <" + property.Name + "> in <" + obj + "> with value " + currentValue + "\n" + e); return(false); } }
/// <summary> /// A helper method that draws the inspector for a field/property at the given location. /// </summary> private void EditProperty(ref Rect region, object element, InspectedProperty property, fiGraphMetadata metadata) { bool hasPrefabDiff = fiPrefabTools.HasPrefabDiff(element, property); if (hasPrefabDiff) { fiUnityInternalReflection.SetBoldDefaultFont(true); } // edit the property { var childMetadata = metadata.Enter(property.Name); fiGraphMetadataCallbacks.PropertyMetadataCallback(childMetadata.Metadata, property); Rect propertyRect = region; float propertyHeight = fiEditorGUI.EditPropertyHeight(element, property, childMetadata); propertyRect.height = propertyHeight; fiEditorGUI.EditProperty(propertyRect, element, property, childMetadata); region.y += propertyHeight; } if (hasPrefabDiff) { fiUnityInternalReflection.SetBoldDefaultFont(false); } }
/// <summary> /// Returns true if the given property should be displayed in the inspector. This method /// assumes that the property type is inspectable. /// </summary> private static bool ShouldDisplayProperty(InspectedProperty property) { var memberInfo = property.MemberInfo; // note: we do opt-out before opt-in so that we can still serialize // a field but not display it in the inspector (as the serialize // annotations automatically cause a field to be displayed) if (memberInfo.IsDefined(typeof(HideInInspector), /*inherit:*/ true) || memberInfo.IsDefined(typeof(NotSerializedAttribute), /*inherit:*/true) || fiInstalledSerializerManager.SerializationOptOutAnnotations.Any(t => memberInfo.IsDefined(t, /*inherit*/true))) { return false; } if (memberInfo.IsDefined(typeof(ShowInInspectorAttribute), /*inherit:*/ true) || (property.IsStatic == false && fiInstalledSerializerManager.SerializationOptInAnnotations.Any(t => memberInfo.IsDefined(t, /*inherit*/true)))) { return true; } if (property.MemberInfo is PropertyInfo && fiSettings.InspectorRequireShowInInspector) { return false; } return InspectedType.IsSerializedByFullInspector(property) || InspectedType.IsSerializedByUnity(property); }
/// <summary> /// Returns true if the given property should be serialized. /// </summary> /// <remarks>This assumes that the property is r/w!</remarks> public static bool IsSerializedByFullInspector(InspectedProperty property) { if (property.IsStatic) { return false; } // Do not bother serializing BaseObject derived types - they will handle it properly // themselves. Serializing them will only lead to wasted storage space. if (typeof(BaseObject).Resolve().IsAssignableFrom(property.StorageType.Resolve())) { return false; } MemberInfo member = property.MemberInfo; // if it has NonSerialized, then we *don't* serialize it if (fsPortableReflection.HasAttribute<NonSerializedAttribute>(member) || fsPortableReflection.HasAttribute<NotSerializedAttribute>(member)) { return false; } // Don't serialize it if it has one of the custom opt-out annotations either var optOut = fiInstalledSerializerManager.SerializationOptOutAnnotations; for (int i = 0; i < optOut.Length; ++i) { if (member.IsDefined(optOut[i], true)) { return false; } } // if we have a [SerializeField] or [Serializable] attribute, then we *do* serialize if (fsPortableReflection.HasAttribute<SerializeField>(member) || fsPortableReflection.HasAttribute<SerializableAttribute>(member)) { return true; } // Serialize if we have a custom opt-in annotation var optIn = fiInstalledSerializerManager.SerializationOptInAnnotations; for (int i = 0; i < optIn.Length; ++i) { if (member.IsDefined(optIn[i], /*inherit:*/ true)) { return true; } } if (property.MemberInfo is PropertyInfo) { // perf: property.IsAutoProperty is lazily computed, so if we have SerializeAutoProperties set // to false we can avoid computing any auto-property checks by putting the // SerializeAutoProperties check before the IsAutoProperty check // If we're not serializing auto-properties, then we will not serialize any properties by default if (fiSettings.SerializeAutoProperties == false) return false; // If it's not an auto property, then we are not going to serialize it by default if (property.IsAutoProperty == false) return false; } return property.IsPublic; }
/// <summary> /// Initializes a new instance of the <see cref="KeyPropertyKeyBinding" /> class. /// </summary> /// <param name="inspectedProperty"> /// The inspected property. /// </param> /// <param name="columnBinding"> /// The column binding. /// </param> /// <exception cref="ArgumentException"> /// If the <paramref name="inspectedProperty" /> does not have an entity key. /// </exception> internal KeyPropertyKeyBinding(InspectedProperty inspectedProperty, IColumnBinding columnBinding) : base(inspectedProperty, columnBinding) { if (!inspectedProperty.HasKey) { throw new ArgumentException("Invalid property, missing entity key", nameof(inspectedProperty)); } this.get = inspectedProperty.Getter; this.set = inspectedProperty.Setter; }
/// <summary> /// Helper function that just ignores a few FI internal types for serialization since the /// backup solution serializes all inspected properties, not just those that are serialized /// </summary> private static bool ShouldIgnoreForPersist(InspectedProperty property) { string name = property.Name; return (name.Contains("ISerializedObject.") || name == "_objectReferences" || name == "_serializedStateKeys" || name == "_serializedStateValues" || name == "_restored"); }
public static float EditPropertyHeightDirect(InspectedProperty property, object propertyValue, fiGraphMetadataChild metadataChild) { fiGraphMetadata metadata = metadataChild.Metadata; var editor = PropertyEditor.Get(property.StorageType, property.MemberInfo).FirstEditor; GUIContent propertyLabel = new GUIContent(property.DisplayName); // Either the foldout is active or we are not displaying a foldout. // Either way, we want to report the full height of the property. return(editor.GetElementHeight(propertyLabel, propertyValue, metadata.Enter("EditProperty", metadata.Context))); }
/// <summary> /// Returns true if the property type itself is inspectable. This does /// not necessarily mean that the property should be displayed in the /// inspector -- just that the FI editing engine can handle it. /// </summary> private static bool IsPropertyTypeInspectable(InspectedProperty property) { // We never inspect delegates if (typeof(Delegate).IsAssignableFrom(property.StorageType)) { return false; } if (property.MemberInfo is FieldInfo) { // Don't inspect compiler generated fields (an example would be a // backing field for an automatically generated property). if (property.MemberInfo.IsDefined(typeof(CompilerGeneratedAttribute), /*inherit:*/ false)) { return false; } } else if (property.MemberInfo is PropertyInfo) { var propertyInfo = (PropertyInfo)property.MemberInfo; // If we cannot read from the property, then there is no sense in // displaying it -- we will have no value to display if (propertyInfo.CanRead == false) { return false; } // hack?: We only display r/w properties declared on Unity types // note: This may rely on the fact that we collect members // locally per inheritance level (does DeclaringType // change? I'm not sure). // note: We also check for UnityEditor since some users use FI in // non-standard ways -- ie, potentially for types that are // not available at runtime and hence may be in the // UnityEditor namespace. var @namespace = propertyInfo.DeclaringType.Namespace; if (@namespace != null && (@namespace.StartsWith("UnityEngine") || @namespace.StartsWith("UnityEditor"))) { if (propertyInfo.CanWrite == false) { return false; } } // If the property is named "Item", it might be the this[int] // indexer, which in that case we don't serialize it We cannot // just compare with "Item" because of explicit interfaces, where // the name of the property will be the full method name. if (propertyInfo.Name.EndsWith("Item")) { ParameterInfo[] parameters = propertyInfo.GetIndexParameters(); if (parameters.Length > 0) { return false; } } } return true; }
/// <summary> /// Writes the specified property. /// </summary> /// <param name="serializer"> /// The serializer. /// </param> /// <param name="inspectedProperty"> /// The inspected property. /// </param> /// <param name="any"> /// The object to which the property belongs. /// </param> private static void WriteProperty(Serializer serializer, InspectedProperty inspectedProperty, object any) { var value = inspectedProperty.Getter(any); if (value == null) { Encoder.WriteTag(serializer.BinaryWriter, Tags.Null); } else { serializer.Write(inspectedProperty.PropertyType, value.GetType(), value); } }
/// <summary> /// Encodes the specified property. /// </summary> /// <param name="serializer"> /// The serializer. /// </param> /// <param name="inspectedProperty"> /// The inspected property. /// </param> /// <param name="encoderInfo"> /// The encoder info. /// </param> /// <param name="any"> /// The object to which the property belongs. /// </param> private static void EncodeProperty(Serializer serializer, InspectedProperty inspectedProperty, EncoderInfo encoderInfo, object any) { var value = inspectedProperty.Getter(any); if (value == null) { Encoder.WriteTag(serializer.BinaryWriter, Tags.Null); } else { serializer.Encode(encoderInfo, value, true); } }
public static void EditProperty(Rect region, object container, InspectedProperty property, fiGraphMetadataChild metadata) { EditorGUI.BeginChangeCheck(); object propertyValue = property.Read(container); object updatedValue = EditPropertyDirect(region, property, propertyValue, metadata, container); if (EditorGUI.EndChangeCheck()) { property.Write(container, updatedValue); // Make sure we propagate the changes up the edit stack. For example, if this property // is on a struct on a struct, then the top-level struct will not get modified without // propagation of the change check. GUI.changed = true; } }
public bool Read(object obj, out object result) { if (byType != null) { result = obj; return(true); } // The property was potentially found on a different type. We need to // update it to associate with this type. It's very possible the // property will not even apply to context, in which case Write // becomes a no-op. InspectedProperty propertyToUse = byProperty; if (byProperty.MemberInfo.DeclaringType != obj.GetType()) { var childProp = InspectedType.Get(obj.GetType()).GetPropertyByName(byProperty.Name); if (childProp != null) { propertyToUse = childProp; } else { result = null; return(false); } } var read = propertyToUse.Read(obj); if (byListIndex >= 0) { result = ((IList)read)[byListIndex]; return(true); } if (byDictKey != null) { result = ((IDictionary)read)[byDictKey]; return(true); } result = read; return(true); }
/// <summary> /// Gets the key binding for the inspected property specified. /// </summary> /// <param name="property"> /// The inspected property. /// </param> /// <param name="columnBinding"> /// The column binding. /// </param> /// <returns> /// The key binding. /// </returns> private static IKeyBinding GetKeyBindingForProperty(InspectedProperty property, IColumnBinding columnBinding) { if (property.PropertyType == typeof(Key)) { return(new KeyPropertyKeyBinding(property, columnBinding)); } if (property.PropertyType == typeof(string)) { return(new StringPropertyKeyBinding(property, columnBinding)); } if (property.PropertyType == typeof(Guid)) { return(new GuidPropertyKeyBinding(property, columnBinding)); } throw new PersistenceException(String.Format(CultureInfo.InvariantCulture, @"Unsupported id property type {0}", property.PropertyType)); }
private static void RevertPrefabContextMenu(Rect region, object context, InspectedProperty property) { if (Event.current.type == EventType.ContextClick && region.Contains(Event.current.mousePosition) && // This can be a relatively heavy function call, so we check it last. If the rect bounds // check ends up consuming lots of time, then HasPrefabDiff has a small fast-path section // that can short-circuit the bounds check. fiPrefabTools.HasPrefabDiff(context, property)) { Event.current.Use(); var content = new GUIContent("Revert " + property.DisplayName + " to Prefab Value"); GenericMenu menu = new GenericMenu(); menu.AddItem(content, /*on:*/ false, () => { fiPrefabTools.RevertValue(context, property); }); menu.ShowAsContext(); } }
/// <summary> /// Restores a backup that was previously created. /// </summary> public static void RestoreBackup(fiSerializedObject serializedState) { Type targetType = serializedState.Target.Target.GetType(); var inspectedType = InspectedType.Get(targetType); var serializationOperator = new fiSerializationOperator() { SerializedObjects = serializedState.ObjectReferences }; // Fetch the serializer that the target uses Type serializerType = BehaviorTypeToSerializerTypeMap.GetSerializerType(targetType); var serializer = (BaseSerializer)fiSingletons.Get(serializerType); foreach (fiSerializedMember member in serializedState.Members) { // user requested a skip for restoring this property if (member.ShouldRestore.Enabled == false) { continue; } InspectedProperty property = inspectedType.GetPropertyByName(member.Name); if (property != null) { Type storageType = property.StorageType; object restoredValue = serializer.Deserialize(storageType, member.Value, serializationOperator); property.Write(serializedState.Target.Target, restoredValue); } } if (serializedState.Target.Target is ISerializedObject) { var serializedObj = ((ISerializedObject)serializedState.Target.Target); serializedObj.SaveState(); serializedObj.RestoreState(); } }
/// <summary> /// Draws a GUI for editing the given property and returns the updated /// value. This does /// *not* write the updated value to a container. /// </summary> /// <param name="context"> /// An optional context that the property value came from. If this is not /// given, then a prefab context menu will not be displayable. /// </param> public static object EditPropertyDirect(Rect region, InspectedProperty property, object propertyValue, fiGraphMetadataChild metadataChild, object context) { fiGraphMetadata metadata = metadataChild.Metadata; // Show a "revert to prefab" value context-menu if possible if (context != null) { RevertPrefabContextMenu(region, context, property); } // get the label / tooltip GUIContent label = new GUIContent(property.DisplayName, InspectorTooltipAttribute.GetTooltip(property.MemberInfo)); var editorChain = PropertyEditor.Get(property.StorageType, property.MemberInfo); IPropertyEditor editor = editorChain.FirstEditor; EditorGUI.BeginDisabledGroup(property.CanWrite == false); propertyValue = editor.Edit(region, label, propertyValue, metadata.Enter("EditProperty", metadata.Context)); EditorGUI.EndDisabledGroup(); return(propertyValue); }
/// <summary> /// Sets an object property. /// </summary> /// <param name="inspectedProperty"> /// The inspected property. /// </param> /// <param name="target"> /// The target. /// </param> /// <param name="value"> /// The property value. /// </param> /// <param name="tag"> /// The tag. /// </param> internal override void SetObjectProperty(InspectedProperty inspectedProperty, object target, object value, Tags tag) { var entitySpec = value as EntitySpec; if (entitySpec != null) { var entityScanTarget = new EntityScanTarget(inspectedProperty, entitySpec, target); if (IsEntityReference(tag)) { this.entityScanner.Add(entityScanTarget); this.ObjectRefs.Add(entityScanTarget); } else if (tag == Tags.ObjectRef) { ((EntityScanTarget)entitySpec).AddScanTargetRef(entityScanTarget); } } else { base.SetObjectProperty(inspectedProperty, target, value, tag); } }
private static bool TryToCopyValues(ISerializedObject newInstance) { if (string.IsNullOrEmpty(newInstance.SharedStateGuid)) { return(false); } ISerializedObject originalInstance = null; lock (_skipSerializationQueue) { if (!_skipSerializationQueue.TryGetValue(newInstance.SharedStateGuid, out originalInstance)) { return(false); } _skipSerializationQueue.Remove(newInstance.SharedStateGuid); } // After a prefab is instantiated Unity will call a full // serialize/deserialize cycle on the object. We don't need to copy // values if the object references are the same. if (ReferenceEquals(newInstance, originalInstance)) { return(true); } var inspectedType = InspectedType.Get(originalInstance.GetType()); for (int i = 0; i < originalInstance.SerializedStateKeys.Count; ++i) { InspectedProperty property = inspectedType.GetPropertyByName(originalInstance.SerializedStateKeys[i]) ?? inspectedType.GetPropertyByFormerlySerializedName(originalInstance.SerializedStateKeys[i]); property.Write(newInstance, property.Read(originalInstance)); } return(true); }
/// <summary> /// Serializes the current state of the given object. /// </summary> /// <typeparam name="TSerializer">The type of serializer to use for the serialization /// process.</typeparam> /// <param name="obj">The object that should be serialized.</param> /// <returns>True if serialization was entirely successful, false if something bad happened along the way.</returns> public static bool SaveState <TSerializer>(ISerializedObject obj) where TSerializer : BaseSerializer { bool success = true; // short-circuit for null serializer if (typeof(TSerializer) == typeof(NullSerializer)) { return(success); } var callbacks = obj as ISerializationCallbacks; if (callbacks != null) { callbacks.OnBeforeSerialize(); } // fetch the selected serializer var serializer = fiSingletons.Get <TSerializer>(); // setup the serialization operator var serializationOperator = fiSingletons.Get <ListSerializationOperator>(); serializationOperator.SerializedObjects = new List <UnityObject>(); // get the properties that we will be serializing var properties = InspectedType.Get(obj.GetType()).GetProperties(InspectedMemberFilters.FullInspectorSerializedProperties); var serializedKeys = new List <string>(); var serializedValues = new List <string>(); for (int i = 0; i < properties.Count; ++i) { InspectedProperty property = properties[i]; object currentValue = property.Read(obj); try { if (currentValue == null) { serializedKeys.Add(property.Name); serializedValues.Add(null); } else { var serializedState = serializer.Serialize(property.MemberInfo, currentValue, serializationOperator); serializedKeys.Add(property.Name); serializedValues.Add(serializedState); } } catch (Exception e) { success = false; Debug.LogError("Exception caught when serializing property <" + property.Name + "> in <" + obj + "> with value " + currentValue + "\n" + e); } } // Write the updated data out to the object. // Note that we only write data out to the object if our serialized state has // changed. Unity will blindly rewrite the data on disk which will cause some // source control systems to check-out the files. If we are just updating // the content to the same content, we do not want to cause an accidental // checkout. if (AreListsDifferent(obj.SerializedStateKeys, serializedKeys)) { obj.SerializedStateKeys = serializedKeys; } if (AreListsDifferent(obj.SerializedStateValues, serializedValues)) { obj.SerializedStateValues = serializedValues; } if (AreListsDifferent(obj.SerializedObjectReferences, serializationOperator.SerializedObjects)) { obj.SerializedObjectReferences = serializationOperator.SerializedObjects; } // Calling SetDirty seems cause prefab instances to have prefab differences after a script // reload, so we only call SetDirty on ScriptableObjects (it's only really necessary for those as well) if (obj is ScriptableObject) { fiLateBindings.EditorUtility.SetDirty((ScriptableObject)obj); } if (callbacks != null) { callbacks.OnAfterSerialize(); } fiEditorSerializationManager.MarkSerialized(obj); return(success); }
/// <summary> /// Returns true if the given type can be serialized by Unity. This /// function is conservative and may not return true if the type can be /// serialized by unity. However, it will *not* return true if the type /// cannot be serialized by unity. /// </summary> public static bool IsSerializedByUnity(InspectedProperty property) { // Properties are *not* serialized by Unity if (property.MemberInfo is PropertyInfo) { return false; } // statics are *not* serialized by Unity if (property.IsStatic) { return false; } // readonly fields are *not* serialized by Unity FieldInfo fieldInfo = property.MemberInfo as FieldInfo; if (fieldInfo.IsInitOnly) { return false; } // If the attribute is not public and doesn't have a [SerializeField] // attribute, then Unity will not serialize it, regardless of type. if (property.IsPublic == false && property.MemberInfo.IsDefined(typeof(SerializeField), /*inherit:*/ true) == false) { return false; } Type type = property.StorageType; return // Basic primitive types IsSimpleTypeThatUnityCanSerialize(type) || // A non-generic UnityObject derived type (typeof(UnityObject).IsAssignableFrom(type) && type.Resolve().IsGenericType == false) || // Array (but not a multidimensional one) (type.IsArray && type.GetElementType().IsArray == false && IsSimpleTypeThatUnityCanSerialize(type.GetElementType())) || // Lists of already serializable types ( type.Resolve().IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>) && IsSimpleTypeThatUnityCanSerialize(type.GetGenericArguments()[0]) ); }
/// <summary> /// Serializes the current state of the given object. /// </summary> /// <typeparam name="TSerializer"> /// The type of serializer to use for the serialization process. /// </typeparam> /// <param name="obj">The object that should be serialized.</param> /// <returns> /// True if serialization was entirely successful, false if something bad /// happened along the way. /// </returns> public static bool SaveState <TSerializer>(ISerializedObject obj) where TSerializer : BaseSerializer { fiLog.Log(typeof(fiISerializedObjectUtility), "Serializing object of type {0}", obj.GetType()); bool success = true; var callbacks = obj as ISerializationCallbacks; if (callbacks != null) { callbacks.OnBeforeSerialize(); } // Skip serialization entirely if requested. if (!string.IsNullOrEmpty(obj.SharedStateGuid)) { SkipCloningValues(obj); if (callbacks != null) { callbacks.OnAfterSerialize(); } return(true); } // fetch the selected serializer var serializer = fiSingletons.Get <TSerializer>(); // setup the serialization operator var serializationOperator = fiSingletons.Get <ListSerializationOperator>(); serializationOperator.SerializedObjects = new List <UnityObject>(); // get the properties that we will be serializing var serializedKeys = new List <string>(); var serializedValues = new List <string>(); // PERF: Calling InspectedType().GetProperties() is extremely // expensive. If we're not in the editor, then we can avoid // making that call because the only time we serialize an // object is if it has existing FS data. If it has FS data, // then that data stores the set of properties which are // serialized, so we can just hijack that information when // doing the serialization to determine which properties to // serialize. if (fiUtility.IsEditor || obj.SerializedStateKeys == null || obj.SerializedStateKeys.Count == 0) { var properties = InspectedType.Get(obj.GetType()).GetProperties(InspectedMemberFilters.FullInspectorSerializedProperties); for (int i = 0; i < properties.Count; ++i) { InspectedProperty property = properties[i]; string serializedValue; if (SaveStateForProperty(obj, property, serializer, serializationOperator, out serializedValue, ref success)) { serializedKeys.Add(property.Name); serializedValues.Add(serializedValue); } } } else { var inspectedType = InspectedType.Get(obj.GetType()); for (int i = 0; i < obj.SerializedStateKeys.Count; ++i) { InspectedProperty property = inspectedType.GetPropertyByName(obj.SerializedStateKeys[i]) ?? inspectedType.GetPropertyByFormerlySerializedName(obj.SerializedStateKeys[i]); if (property == null) { continue; } string serializedValue; if (SaveStateForProperty(obj, property, serializer, serializationOperator, out serializedValue, ref success)) { serializedKeys.Add(property.Name); serializedValues.Add(serializedValue); } } } // Write the updated data out to the object. // Note that we only write data out to the object if our serialized // state has changed. Unity will blindly rewrite the data on disk // which will cause some source control systems to check-out the // files. If we are just updating the content to the same content, we // do not want to cause an accidental checkout. bool changed = false; if (AreListsDifferent(obj.SerializedStateKeys, serializedKeys)) { obj.SerializedStateKeys = serializedKeys; changed = true; } if (AreListsDifferent(obj.SerializedStateValues, serializedValues)) { obj.SerializedStateValues = serializedValues; changed = true; } if (AreListsDifferent(obj.SerializedObjectReferences, serializationOperator.SerializedObjects)) { obj.SerializedObjectReferences = serializationOperator.SerializedObjects; changed = true; } if (changed && fiUtility.IsEditor) { fiLateBindings.EditorApplication.InvokeOnEditorThread(() => { var unityObj = (UnityObject)obj; if (unityObj != null) { fiLateBindings.EditorUtility.SetDirty(unityObj); } }); } if (callbacks != null) { callbacks.OnAfterSerialize(); } return(success); }
/// <summary> /// Initializes a new instance of the <see cref="GuidPropertyKeyBinding" /> class. /// </summary> /// <param name="inspectedProperty"> /// The inspected property. /// </param> /// <param name="columnBinding"> /// The column binding. /// </param> internal GuidPropertyKeyBinding(InspectedProperty inspectedProperty, IColumnBinding columnBinding) : base(inspectedProperty, columnBinding) { this.get = inspectedProperty.Getter; this.set = inspectedProperty.Setter; }
public bool IsInterested(InspectedProperty property) { return property.CanWrite && InspectedType.IsSerializedByFullInspector(property) && InspectedType.IsSerializedByUnity(property) == false; }
/// <summary> /// Serializes the current state of the given object. /// </summary> /// <typeparam name="TSerializer">The type of serializer to use for the serialization /// process.</typeparam> /// <param name="obj">The object that should be serialized.</param> /// <returns>True if serialization was entirely successful, false if something bad happened along the way.</returns> public static bool SaveState <TSerializer>(ISerializedObject obj) where TSerializer : BaseSerializer { bool success = true; var callbacks = obj as ISerializationCallbacks; if (callbacks != null) { callbacks.OnBeforeSerialize(); } // fetch the selected serializer var serializer = fiSingletons.Get <TSerializer>(); // setup the serialization operator var serializationOperator = fiSingletons.Get <ListSerializationOperator>(); serializationOperator.SerializedObjects = new List <UnityObject>(); // get the properties that we will be serializing var serializedKeys = new List <string>(); var serializedValues = new List <string>(); // PERF: Calling InspectedType().GetProperties() is extremely expensive. If we're not in the editor, then we can avoid making // that call because the only time we serialize an object is if it has existing FS data. If it has FS data, then that data // stores the set of properties which are serialized, so we can just hijack that information when doing the serialization to // determine which properties to serialize. if (fiUtility.IsEditor || obj.SerializedStateKeys == null || obj.SerializedStateKeys.Count == 0) { var properties = InspectedType.Get(obj.GetType()).GetProperties(InspectedMemberFilters.FullInspectorSerializedProperties); for (int i = 0; i < properties.Count; ++i) { InspectedProperty property = properties[i]; string serializedValue; if (SaveStateForProperty(obj, property, serializer, serializationOperator, out serializedValue, ref success)) { serializedKeys.Add(property.Name); serializedValues.Add(serializedValue); } } } else { var inspectedType = InspectedType.Get(obj.GetType()); for (int i = 0; i < obj.SerializedStateKeys.Count; ++i) { InspectedProperty property = inspectedType.GetPropertyByName(obj.SerializedStateKeys[i]) ?? inspectedType.GetPropertyByFormerlySerializedName(obj.SerializedStateKeys[i]); if (property == null) { continue; } string serializedValue; if (SaveStateForProperty(obj, property, serializer, serializationOperator, out serializedValue, ref success)) { serializedKeys.Add(property.Name); serializedValues.Add(serializedValue); } } } // Write the updated data out to the object. // Note that we only write data out to the object if our serialized state has // changed. Unity will blindly rewrite the data on disk which will cause some // source control systems to check-out the files. If we are just updating // the content to the same content, we do not want to cause an accidental // checkout. if (AreListsDifferent(obj.SerializedStateKeys, serializedKeys)) { obj.SerializedStateKeys = serializedKeys; } if (AreListsDifferent(obj.SerializedStateValues, serializedValues)) { obj.SerializedStateValues = serializedValues; } if (AreListsDifferent(obj.SerializedObjectReferences, serializationOperator.SerializedObjects)) { obj.SerializedObjectReferences = serializationOperator.SerializedObjects; } // Calling SetDirty seems cause prefab instances to have prefab differences after a script // reload, so we only call SetDirty on ScriptableObjects (it's only really necessary for those as well) if (obj is ScriptableObject) { fiLateBindings.EditorUtility.SetDirty((ScriptableObject)obj); } if (callbacks != null) { callbacks.OnAfterSerialize(); } return(success); }
/// <summary> /// Determines whether the specified object is equal to this one. /// </summary> public bool Equals(InspectedProperty p) { // If parameter is null return false: if ((object)p == null) { return false; } // Return true if the fields match: return (StorageType == p.StorageType) && (Name == p.Name); }
/// <summary> /// Draws a GUI for editing the given property and returns the updated /// value. This does /// *not* write the updated value to a container. /// </summary> public static object EditPropertyDirect(Rect region, InspectedProperty property, object propertyValue, fiGraphMetadataChild metadataChild) { return(EditPropertyDirect(region, property, propertyValue, metadataChild, null)); }
public bool IsInterested(InspectedProperty property) { return IsPropertyTypeInspectable(property) && ShouldDisplayProperty(property); }
/// <summary> /// Initializes a new instance of the <see cref="InspectedPropertyKeyBinding" /> class. /// </summary> /// <param name="inspectedProperty"> /// The inspected property. /// </param> /// <param name="columnBinding"> /// The column binding. /// </param> protected InspectedPropertyKeyBinding(InspectedProperty inspectedProperty, IColumnBinding columnBinding) : base(inspectedProperty.InspectedType, columnBinding) { }
/// <summary> /// Returns true if the given property should be displayed in the inspector. This method /// assumes that the property type is inspectable. /// </summary> private static bool ShouldDisplayProperty(InspectedProperty property) { var memberInfo = property.MemberInfo; // If ShowInInspector is present, we will *always* display the attribute. if (memberInfo.IsDefined(typeof(ShowInInspectorAttribute), /*inherit:*/ true)) { return true; } // note: we do opt-out serialization annotations before opt-in annotations // so that we can still serialize a field but not display it in the // inspector (as the serialize annotations automatically cause a field // to be displayed) if (memberInfo.IsDefined(typeof(HideInInspector), /*inherit:*/ true) || memberInfo.IsDefined(typeof(NotSerializedAttribute), /*inherit:*/true) || fiInstalledSerializerManager.SerializationOptOutAnnotations.Any(t => memberInfo.IsDefined(t, /*inherit*/true))) { return false; } if (property.IsStatic == false && fiInstalledSerializerManager.SerializationOptInAnnotations.Any(t => memberInfo.IsDefined(t, /*inherit*/true))) { return true; } if (property.MemberInfo is PropertyInfo && fiSettings.InspectorRequireShowInInspector) { return false; } return // IsSerializedByFullInspector will return false for BaseObject types, so we want to // special case support for them being inspected. typeof(BaseObject).Resolve().IsAssignableFrom(property.StorageType.Resolve()) || InspectedType.IsSerializedByFullInspector(property) || InspectedType.IsSerializedByUnity(property); }
public void Write(object context, object value) { if (byType != null) { if (value != s_RemoveObject) { value = InspectedType.Get((Type)value).CreateInstance(); } byType[byType.Length - 1].Write(context, value); return; } // The property was potentially found on a different type. We need to // update it to associate with this type. It's very possible the // property will not even apply to context, in which case Write // becomes a no-op. InspectedProperty propertyToUse = byProperty; if (byProperty.MemberInfo.DeclaringType != context.GetType()) { var childProp = InspectedType.Get(context.GetType()).GetPropertyByName(byProperty.Name); if (childProp != null) { propertyToUse = childProp; } else { return; } } if (byListIndex >= 0) { var read = propertyToUse.Read(context); var list = (IList)read; var elementType = InspectedType.Get(propertyToUse.StorageType).ElementType; if (value == s_RemoveObject) { fiListUtility.RemoveAt(ref list, elementType, byListIndex); } else { while (byListIndex >= list.Count) { fiListUtility.Add(ref list, elementType); } list[byListIndex] = value; } // Changing list will not update the actual array reference, so // we have to write back to the stored object. if (list is Array) { propertyToUse.Write(context, list); } return; } if (byDictKey != null) { var read = propertyToUse.Read(context); var dict = (IDictionary)read; // TODO: Have a separate Write/Remove command, since you might // want to set a dict field to null. if (value == s_RemoveObject) { dict.Remove(byDictKey); } else { dict[byDictKey] = value; } return; } if (value != s_RemoveObject) { propertyToUse.Write(context, value); } else { propertyToUse.Write(context, null); } }
/// <summary> /// Returns true if the given property on the given object instance has a /// prefab override. /// </summary> /// <param name="instance">The object instance.</param> /// <param name="property">The property to check.</param> /// <returns> /// True if the property is prefab override, false otherwise. /// </returns> /// <remarks> /// Currently, this method only works for MonoBehavior targets. /// </remarks> public static bool HasPrefabDiff(object instance, InspectedProperty property) { // For prefab differences, we rely upon the internal Unity mechanisms // for identifying when an object has a prefab diff. We are able to // do this because we only support top-level prefab differences. // // One of the current issues with this mechanism is when an array is // serialized by Unity, and only part of the array tracks the prefab, // then the inspector will show the entire array in bold (when only // the one part should be). // We only want top-level components if (instance is MonoBehaviour == false) { return(false); } // If there is no prefab, then we don't show anything in bold. var prefabGameObject = (GameObject)PrefabUtility.GetPrefabParent(((MonoBehaviour)instance).gameObject); if (prefabGameObject == null) { return(false); } // If the prefab doesn't have this component, then the entire // component should be in bold. var prefab = prefabGameObject.GetComponent(instance.GetType()); if (prefab == null) { return(true); } // Get all of the property modifications on the object. If there are // no property modifications, then nothing should be in bold. PropertyModification[] mods = PrefabUtility.GetPropertyModifications((UnityObject)instance); if (mods == null) { return(false); } var serializedInstance = instance as ISerializedObject; if (serializedInstance != null) { foreach (PropertyModification mod in mods) { // A property modification can take one of two forms. It can // either be modifying a Unity serialized value or a Full // Inspector serialized value. // Check to see if it's a Full Inspector serialized value. If // it is, then we lookup the key that the modification is // associated with and, if we find said key, and that the key // is equal to the property we are checking for, then we // return true. string serializedPropertyName; if (TryExtractPropertyName(serializedInstance, mod, out serializedPropertyName) && serializedPropertyName == property.Name) { return(true); } // Check to see if it is a Unity serialized value. We have to // do a dotted comparison because the propertyPath may be // associated with, ie, an array, which in that case the path // is something like "values.Array._items[0]" while // property.Name is just "values". if (ContainsPropertyName(mod.propertyPath, property.Name)) { return(true); } } } return(false); }
/// <summary> /// Reverts the given property on the instance to the prefab value. /// </summary> /// <param name="instance"> /// The prefab instance to revert the value on. /// </param> /// <param name="property">The property to revert.</param> public static void RevertValue(object instance, InspectedProperty property) { // We only want top-level components if (instance is MonoBehaviour == false) { return; } // Not a prefab var prefabGameObject = (GameObject)PrefabUtility.GetPrefabParent(((MonoBehaviour)instance).gameObject); if (prefabGameObject == null) { return; } // Get all of the property modifications on the object. If there are // no property modifications, then there is nothing to revert. PropertyModification[] mods = PrefabUtility.GetPropertyModifications((UnityObject)instance); if (mods == null) { return; } ISerializedObject serializedInstance = (ISerializedObject)instance; bool removed = false; for (int i = 0; i < mods.Length; ++i) { PropertyModification mod = mods[i]; // A property modification can take one of two forms. It can // either be modifying a Unity serialized value or a Full // Inspector serialized value. // Check to see if it's a Full Inspector serialized value. If it // is, then we lookup the key that the modification is associated // with and, if we find said key, and that the key is equal to // the property we are checking for, then we return true. string serializedPropertyName; if (TryExtractPropertyName(serializedInstance, mod, out serializedPropertyName) && serializedPropertyName == property.Name) { removed = true; } // Check to see if it is a Unity serialized value. We have to do // a dotted comparison because the propertyPath may be associated // with, ie, an array, which in that case the path is something // like "values.Array._items[0]" while property.Name is just // "values". if (ContainsPropertyName(mod.propertyPath, property.Name)) { removed = true; } if (removed) { ArrayUtility.RemoveAt(ref mods, i); PrefabUtility.SetPropertyModifications((UnityObject)instance, mods); break; } } }
public bool IsInterested(InspectedProperty property) { return property.IsStatic && IsPropertyTypeInspectable(property); }
public static float EditPropertyHeight(object container, InspectedProperty property, fiGraphMetadataChild metadata) { object propertyValue = property.Read(container); return(EditPropertyHeightDirect(property, propertyValue, metadata)); }
public static Dictionary <ObjectDataPath[], object> Create(List <ObjectDataPath> toUs, object obj, HashSet <object> seenObjects) { /* * Debug.Log(string.Join(", ", toUs.Select(t => { * if (t.byProperty == null) return ""; * return t.byProperty.Name; * }).ToArray())); * Debug.Log("seenObjects.Count = " + seenObjects.Count); * Debug.Log("seenObjects.Contains " + obj + " = " + seenObjects.Contains(obj)); */ Dictionary <ObjectDataPath[], object> thisLevel = AllocateDict(); if (seenObjects.Add(obj) == false) { return(thisLevel); } // Write the type. thisLevel.Add(CreateNavigation(toUs, new ObjectDataPath(toUs.ToArray())), obj.GetType()); List <InspectedProperty> properties = InspectedType.Get(obj.GetType()).GetProperties(InspectedMemberFilters.InspectableMembers); for (int i = 0; i < properties.Count; ++i) { InspectedProperty property = properties[i]; object valueForProperty = property.Read(obj); if (IsPrimitiveValue(valueForProperty)) { thisLevel.Add(CreateNavigation(toUs, new ObjectDataPath(property)), valueForProperty); continue; } if (InspectedType.Get(property.StorageType).IsCollection) { if (valueForProperty is IList) { var listForProperty = (IList)valueForProperty; for (int j = 0; j < listForProperty.Count; ++j) { object listElement = listForProperty[j]; if (IsPrimitiveValue(listElement)) { thisLevel.Add(CreateNavigation(toUs, new ObjectDataPath(property, j)), listElement); continue; } var listChildValues = CreateWithPath(toUs, new ObjectDataPath(property, j), listElement, seenObjects); foreach (var entry in listChildValues) { thisLevel.Add(entry.Key, entry.Value); } } } else if (valueForProperty is IDictionary) { var dictForProperty = (IDictionary)valueForProperty; IDictionaryEnumerator enumerator = dictForProperty.GetEnumerator(); while (enumerator.MoveNext()) { object key = enumerator.Key; object value = enumerator.Value; if (value == null || value.GetType().IsPrimitive || value is string) { thisLevel.Add(CreateNavigation(toUs, new ObjectDataPath(property, key)), value); } else { toUs.Add(new ObjectDataPath(property, key)); var dictChildValues = Create(toUs, value, seenObjects); toUs.RemoveAt(toUs.Count - 1); foreach (var entry in dictChildValues) { thisLevel.Add(entry.Key, entry.Value); } } } } else { Debug.LogError("Please file a bug (with an example) requesting multiedit support for " + valueForProperty.GetType().CSharpName()); } continue; } // Navigate children. var childValues = CreateWithPath(toUs, new ObjectDataPath(property), valueForProperty, seenObjects); foreach (var entry in childValues) { thisLevel.Add(entry.Key, entry.Value); } } return(thisLevel); }
/// <summary> /// Initializes a new instance of the <see cref="EntityScanTarget" /> class. /// </summary> /// <param name="property"> /// The inspected property. /// </param> /// <param name="entitySpec"> /// The entity specification. /// </param> /// <param name="target"> /// The scan target. /// </param> internal EntityScanTarget(InspectedProperty property, EntitySpec entitySpec, object target) : base(property.PropertyType, entitySpec) { this.target = target; this.setter = property.Setter; }
public bool IsInterested(InspectedProperty property) { return false; }