- Home /
Invoking a file dialogue from a PropertyDrawer
When invoking EditorUtility.OpenFilePanel from within PropertyDrawer.OnGUI my editor hits an error: InvalidOperationException: Stack empty.
Here is the propertydrawer code:
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
EditorGUI.BeginProperty(position, label, property);
{
GUILayout.BeginHorizontal();
{
GUILayout.Label(property.displayName);
SerializedProperty stringGuidProperty = property.FindPropertyRelative("StringGuid");
EditorGUILayout.SelectableLabel(stringGuidProperty.stringValue);
// Select a file via a file browser and read the SerialID from that file
if (GUILayout.Button("Copy Instance ID From File"))
{
string path = EditorUtility.OpenFilePanel("Select Instance to Load", "", "xml");
if (path.Length != 0)
{
string id = DataSerializer.GetSerialIDFromFile(path);
stringGuidProperty.stringValue = id;
}
}
}
GUILayout.EndHorizontal();
}
EditorGUI.EndProperty();
}
While debugging this everything seems to work. The correct ID is assigned to the stringGuidProperty except when GUILayout.EndHorizontal() is executed an error is thrown which seems to prevent the property from being applied.
If I change the line opening the file panel to a hardcoded string everything works fine without any errors and the correct ID is loaded.
Here is the full error:
InvalidOperationException: Stack empty.
System.Collections.Generic.Stack`1[T].ThrowForEmptyStack () (at <0463b2ef957545c0a51b42f372cd4fbb>:0)
System.Collections.Generic.Stack`1[T].Pop () (at <0463b2ef957545c0a51b42f372cd4fbb>:0)
UnityEditor.PropertyDrawer.OnGUISafe (UnityEngine.Rect position, UnityEditor.SerializedProperty property, UnityEngine.GUIContent label) (at <bbfbd5a71eea45d1a1354233c800516b>:0)
UnityEditor.PropertyHandler.OnGUI (UnityEngine.Rect position, UnityEditor.SerializedProperty property, UnityEngine.GUIContent label, System.Boolean includeChildren, UnityEngine.Rect visibleArea) (at <bbfbd5a71eea45d1a1354233c800516b>:0)
UnityEditor.PropertyHandler.OnGUI (UnityEngine.Rect position, UnityEditor.SerializedProperty property, UnityEngine.GUIContent label, System.Boolean includeChildren) (at <bbfbd5a71eea45d1a1354233c800516b>:0)
UnityEditor.PropertyHandler.OnGUILayout (UnityEditor.SerializedProperty property, UnityEngine.GUIContent label, System.Boolean includeChildren, UnityEngine.GUILayoutOption[] options) (at <bbfbd5a71eea45d1a1354233c800516b>:0)
UnityEditor.EditorGUILayout.PropertyField (UnityEditor.SerializedProperty property, UnityEngine.GUIContent label, System.Boolean includeChildren, UnityEngine.GUILayoutOption[] options) (at <bbfbd5a71eea45d1a1354233c800516b>:0)
UnityEditor.EditorGUILayout.PropertyField (UnityEditor.SerializedProperty property, UnityEngine.GUILayoutOption[] options) (at <bbfbd5a71eea45d1a1354233c800516b>:0)
Based on the error it seems like opening a file dialogue from a property drawer is somehow interfering with the how the GUI pushes and pops gui layers.
Unity supports asset selection windows by default though so there must be some way to do this sort of thing?
It looks like you can do this within a custom Editor. That's not really what I wanted though - this type should by default have a specific editor and it makes more sense for the type's propertydrawer to encapsulate weird editing behavior. I'm a complete beginner on Unity editor design philosophy though...
While searching for an answer I found ObjectField but it relies on the thing being selected being an actual Asset that unity knows about whereas I'm referencing files.
Anyway if someone comes along with decent editor knowledge and can explain better why OpenFilePanel doesn't work or a reasonable workaround that still uses propertydrawer I will mark that as the accepted answer.
It used to work in Unity 2018.4. I have just updraged to 2020 and I have the same error. I have even tried using an external browser tool that I use for runtime, and the result is the same.
On another (old!) post they recommend using GUIUtility.ExitGUI()
. Indeed it suppresses the error, because it throws a silent exception just before, but then the property value does not get serialized, since we exit prematurely.
That's why I also called property.serializedObject.ApplyModifiedProperties();
before exiting.
I don't really like this hack, but I guess it will have to do.
My final code in OnGUI():
if( GUI.Button( indentedPos, new GUIContent( property.stringValue) ) )
{
string newPath = EditorUtility.OpenFilePanel( "Choose FilePath", property.stringValue, ".json" );
if( !string.IsNullOrEmpty( newPath ) )
{
property.stringValue = newPath;
property.serializedObject.ApplyModifiedProperties();
}
GUIUtility.ExitGUI();
}
Answer by Namey5 · Dec 26, 2021 at 12:15 PM
It is possible that the GUILayout.Horizontal sections are messing with the file utility - have you tried moving the actual call to EditorUtility.OpenFilePanel to the outer scope?
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
EditorGUI.BeginProperty(position, label, property);
{
SerializedProperty stringGuidProperty = property.FindPropertyRelative("StringGuid");
bool openFile = false;
GUILayout.BeginHorizontal();
{
GUILayout.Label(property.displayName);
EditorGUILayout.SelectableLabel(stringGuidProperty.stringValue);
// Cache the button press
openFile = GUILayout.Button("Copy Instance ID From File");
}
GUILayout.EndHorizontal();
// Select a file via a file browser and read the SerialID from that file
if (openFile)
{
string path = EditorUtility.OpenFilePanel("Select Instance to Load", "", "xml");
if (path.Length != 0)
{
string id = DataSerializer.GetSerialIDFromFile(path);
stringGuidProperty.stringValue = id;
}
}
}
EditorGUI.EndProperty();
}
Your answer
Follow this Question
Related Questions
Should EditorGUILayout.PropertyField work with serializable classes? 1 Answer
How to execute a script each time I add a certain prefab to the scene? 2 Answers
GetPropertyHeight infinite recursion on drawer 1 Answer
Array of vectors lost in runtime, but not null 0 Answers
OnInteractivePreviewGUI multiple selection. Works but spams errors. 0 Answers