From 4a2025f6ec566272fe1eaaba6bfec17d3c460026 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 19 Nov 2024 15:55:38 -0800 Subject: [PATCH 1/2] fix auto saving toggle --- Editor/Editor/InspectorAutoSave.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Editor/Editor/InspectorAutoSave.cs b/Editor/Editor/InspectorAutoSave.cs index 26f3820..495f13f 100644 --- a/Editor/Editor/InspectorAutoSave.cs +++ b/Editor/Editor/InspectorAutoSave.cs @@ -66,10 +66,10 @@ private static void Update() if (s_target != null) { - if (s_dirtyStopWatch.IsRunning && s_dirtyStopWatch.ElapsedMilliseconds < SaveDelayMillis) - return; - - SaveAsset(); + if (s_dirtyStopWatch.IsRunning && s_dirtyStopWatch.ElapsedMilliseconds >= SaveDelayMillis) + { + SaveAsset(); + } } } From 885823759f98586174f719ff6ecc865394dd98c9 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 19 Nov 2024 14:41:56 -0800 Subject: [PATCH 2/2] fix glitching nested structs in parameter drawers --- CHANGELOG.md | 5 +++ Editor/Editor/ParamProperty.cs | 36 +++++++++++++++++-- Editor/Editor/ParameterReferenceDrawer.cs | 26 +++++++------- .../ParameterScriptableObjectInspector.cs | 4 ++- package.json | 2 +- 5 files changed, 55 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc5f783..c90300f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ All package updates & migration steps will be listed in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [4.2.1] - 2024-11-19 +### Fixed +- Issue where nested structs in scriptable object drawers have glitching UI. +- Disable autosaving toggle wasn't fully disabling. + ## [4.2.0] - 2024-10-31 ### Added - Additional checks during parameter generation to ensure the generated code matches. diff --git a/Editor/Editor/ParamProperty.cs b/Editor/Editor/ParamProperty.cs index 6885963..823cd9d 100644 --- a/Editor/Editor/ParamProperty.cs +++ b/Editor/Editor/ParamProperty.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Reflection; using UnityEditor; using UnityEngine; @@ -48,6 +49,8 @@ public ScriptableObject ScriptableObject } } + public SerializedObject SerializedScriptableObject => _serializedScriptableObject; + /// /// Current position if the property is part of a list or array. Null otherwise. /// @@ -60,6 +63,20 @@ public ScriptableObject ScriptableObject private readonly SerializedProperty _guidProperty; + /* + * Unity recommended we cache our SerializedObject to avoid GUI glitches. + * + * Unity's response: + * Regarding the issue of Serializable fields becoming unselectable originates from your custom + * property drawer. Our investigation revealed that parts of IMGUI, including the ReorderableList, + * are stateful. This means they retain data about the SerializedObject and SerializedProperties. + * In your property drawer, you are generating a new SerializedObject each time and disposing of it + * immediately. This process invalidates the state, causing multiple drawings of the list and + * disrupting input states. + */ + private readonly SerializedObject _serializedScriptableObject; + private static Dictionary s_serializedScriptableObjectByGuid = new (); + public ParamProperty(FieldInfo fieldInfo, SerializedProperty property) { Property = property; @@ -86,14 +103,29 @@ public ParamProperty(FieldInfo fieldInfo, SerializedProperty property) if (!string.IsNullOrWhiteSpace(GUID)) { - if (ScriptableObject == null) + var scriptableObject = ScriptableObject; + if (scriptableObject == null) { Error = $"Missing asset {GUID}"; } - else if (!InterfaceType.IsInstanceOfType(ScriptableObject)) + else if (!InterfaceType.IsInstanceOfType(scriptableObject)) { Error = $"Asset no longer matches generic type {InterfaceType}"; } + else + { + if (!s_serializedScriptableObjectByGuid.TryGetValue(GUID, out var serializedScriptableObject)) + { + serializedScriptableObject = new SerializedObject(scriptableObject); + s_serializedScriptableObjectByGuid[GUID] = serializedScriptableObject; + } + else + { + // call to ensure the values are up to date with the target in case it was modified in another inspector/drawer + serializedScriptableObject.Update(); + } + _serializedScriptableObject = serializedScriptableObject; + } } } } diff --git a/Editor/Editor/ParameterReferenceDrawer.cs b/Editor/Editor/ParameterReferenceDrawer.cs index a9c7d39..37255c9 100644 --- a/Editor/Editor/ParameterReferenceDrawer.cs +++ b/Editor/Editor/ParameterReferenceDrawer.cs @@ -146,7 +146,7 @@ private float InnerInspectorHeight(ParamProperty paramProperty, SerializedProper return 0; float height = 0; - var serializedObject = new SerializedObject(paramProperty.ScriptableObject); + var serializedObject = paramProperty.SerializedScriptableObject; var prop = serializedObject.GetIterator(); bool children = true; while (prop.NextVisible(children)) @@ -160,7 +160,6 @@ private float InnerInspectorHeight(ParamProperty paramProperty, SerializedProper height += EditorGUI.GetPropertyHeight(prop, new GUIContent(prop.displayName), true); height += EditorGUIUtility.standardVerticalSpacing; } - serializedObject.Dispose(); return height; } @@ -206,7 +205,6 @@ private bool DrawGUI(Rect position, SerializedProperty property, ParamProperty p objectFieldPosition.y, newButtonWidth, objectFieldPosition.height); objectFieldPosition.width -= newButtonWidth; EditorGUI.BeginChangeCheck(); - bool objectChanged = false; newObject = null; // object field var scriptableObject = paramProperty.ScriptableObject; @@ -214,10 +212,11 @@ private bool DrawGUI(Rect position, SerializedProperty property, ParamProperty p if (EditorGUI.EndChangeCheck()) { // this means the user interacted and set the value on the field - // this helps determine if the user deliberately an object or nulled the field. - objectChanged = true; + // this helps determine if the user deliberately set an object or nulled the field. newObject = fieldObject as ParameterScriptableObject; - scriptableObject = newObject; + + // return early so that the ParamProperty can be updated with the correct scriptable object + return true; } if (GUI.Button(buttonRect, "New")) @@ -250,9 +249,10 @@ private bool DrawGUI(Rect position, SerializedProperty property, ParamProperty p { var createdObject = ScriptableObject.CreateInstance(parameterInterface.ScriptableObjectType()); AssetDatabase.CreateAsset(createdObject, savePath); - objectChanged = true; - scriptableObject = createdObject; newObject = createdObject as ParameterScriptableObject; + + // return early so that the ParamProperty can be updated with the correct scriptable object + return true; } } @@ -281,8 +281,7 @@ private bool DrawGUI(Rect position, SerializedProperty property, ParamProperty p } else if (canExpandDrawer && property.isExpanded) { - var serializedObject = new SerializedObject(scriptableObject); - var prop = serializedObject.GetIterator(); + var prop = paramProperty.SerializedScriptableObject.GetIterator(); EditorGUI.indentLevel++; bool children = true; while (prop.NextVisible(children)) @@ -307,7 +306,7 @@ private bool DrawGUI(Rect position, SerializedProperty property, ParamProperty p { height = EditorGUI.GetPropertyHeight(prop, propLabel, true); position.height = height; - DrawExpandedField(serializedObject, position, prop, propLabel); + DrawExpandedField(paramProperty.SerializedScriptableObject, position, prop, propLabel); } position.y += height + EditorGUIUtility.standardVerticalSpacing; } @@ -322,10 +321,9 @@ private bool DrawGUI(Rect position, SerializedProperty property, ParamProperty p */ if (GUI.changed) { - serializedObject.ApplyModifiedProperties(); + paramProperty.SerializedScriptableObject.ApplyModifiedProperties(); InspectorAutoSave.DispatchDelayedSave(); } - serializedObject.Dispose(); } if (paramProperty.ElementPosition.HasValue) @@ -336,7 +334,7 @@ private bool DrawGUI(Rect position, SerializedProperty property, ParamProperty p s_firstTargetObject = null; } - return objectChanged; + return false; } /// diff --git a/Editor/Editor/ParameterScriptableObjectInspector.cs b/Editor/Editor/ParameterScriptableObjectInspector.cs index 0832243..f6182e6 100644 --- a/Editor/Editor/ParameterScriptableObjectInspector.cs +++ b/Editor/Editor/ParameterScriptableObjectInspector.cs @@ -148,7 +148,9 @@ public override void OnInspectorGUI() } // collect properties & errors - List<(SerializedProperty, string)> properties = new List<(SerializedProperty, string)>(); + List<(SerializedProperty, string)> properties = new(); + // call to ensure the values are up to date with the target in case it was modified in another inspector/drawer + serializedObject.Update(); var serializedProp = serializedObject.GetIterator(); bool children = true; while (serializedProp.NextVisible(children)) diff --git a/package.json b/package.json index c631b60..26c3ca8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "com.pocketgems.scriptableobject.flatbuffer", - "version": "4.2.0", + "version": "4.2.1", "displayName": "Scriptable Object - FlatBuffer", "description": "Seamless syncing between Scriptable Objects and CSVs. Scriptable Object data built to Google FlatBuffers for optimal runtime loading & access.", "unity": "2021.3",