- Home /
EditorGUILayout.Foldout - no way to remember state?
This question has certainly been asked before, but it's never gotten answered, and I've tried everything I can think of.
How can I get a EditorGUILayout.Foldout to remember its state when the object that has the script that it's controlling has been deselected and then reselected?
If I initialize the boolean (foldout1) as true, the Foldout will always be open when I return to the object - if false, then it will always be folded up when I return.
@CustomEditor (test)
class testEditor extends Editor {
var foldout1 : boolean = true;
function OnInspectorGUI () {
foldout1 = EditorGUILayout.Foldout(foldout1, "Something");
if (foldout1) {
target.something = EditorGUILayout.IntSlider ("Something",target.something, 0, 100);
}
}
}
Would love to see a working example, and I bet I'm not the only one.
Answer by Bunny83 · Feb 12, 2012 at 02:51 AM
Editors are created if an object of it's type has been selected and are destroyed when the object gets deselected. If you want the editor to save the state individual for each object, you have to use a variable in the target script, which is not nice, since it blows up your class. However, you can surround the variable with:
#if UNITY_EDITOR
[HideInInspector]
var foldout : boolean = true;
#endif
This way the variable only exists when you run your game in the editor. When you create a build it will not be there.
But the easiest way is to use a static variable in your editor. This will remember the state for the editor, not an individual object. If that's what you want, static is what you need ;)
class testEditor extends Editor
{
static var foldout1 : boolean = true;
[...]
I also suggest to use a nested class inside your $$anonymous$$B to hold the editor data - just to reduce the clutter.
I suggest adding NonSerialized as well - to prevent the members from being saved to the scene.
Answer by davidosullivan · Mar 26, 2016 at 09:01 AM
What you can do is set the foldout to use the isExpanded property of one of the serializedObject properties you want to show inside the expanded area. this will work with any property type, it doesn't have to be one that actually expands, it could be a text field...
So if you had
class MyTestClass : MonoBehaviour
{
string myTextField;
}
You could then have a custom editor for that like this
[CustomEditor(typeof(MyTestClass),true)]
public class MyTestClassEditor : Editor
{
public override void OnInspectorGUI(){
SerializedProperty myTextField = serializedObject.FindProperty ("myTextField");
myTextField.isExpanded = EditorGUILayout.Foldout (myTextField.isExpanded, "My Foldout");
if(myTextField.isExpanded){
EditorGUILayout.PropertyField (myTextField);
}
}
}
This seems to be the best solution. You don't have to store any extra bool value or anything of the sort.
Unfortunately I can't seem to make this work for a List of objects. I can't work out why, but its seems to treat all objects in the list as having the same isExpanded Value.
Don't know if it worked before of not, but in 2018.2 it works beautifully, even for Lists. Great solution.
Answer by Mikilo · Dec 08, 2013 at 03:11 AM
As I can see, we are many to seek for a fold trick.
I did a little tool to fulfil the job.
using System.Collections.Generic;
public static class FoldRestorer
{
private static Dictionary<int, bool> folds = new Dictionary<int,bool>();
public static bool GetFold(int hash)
{
if (folds.ContainsKey(hash) == false)
{
folds[hash] = false;
return false;
}
return folds[hash];
}
public static void SetFold(int hash, bool value)
{
folds[hash] = value;
}
}
After the tool, the example through a property drawer.
It is a little bit messy, but it is optimized.
int hash = property.propertyPath.GetHashCode();
EditorGUI.BeginChangeCheck();
bool b = EditorGUI.Foldout(position, FoldRestorer.GetFold(hash), "Fold");
if (EditorGUI.EndChangeCheck() == true)
FoldRestorer.SetFold(hash, b);
It has been tested on Unity 4.3.
You can add a method into FoldRestorer to replace the block above and be more efficient.
Answer by Troyminator · Jan 03, 2013 at 08:00 PM
Just a warning to anyone who implements this:
#if UNITY_EDITOR
[HideInInspector]
var foldout : boolean = true;
#endif
This may be a ticking time bomb if you want your code to run on iOS, and possibly other platforms. It seems that Unity does not support having variables in the editor that do not appear in the runtime in every case. I tried using #if UNITY_EDITOR to compile out variables in the runtime version like this and it worked fine for Windows/Mac standalone builds, but when I moved it to iOS I got mysterious crashes during level load.
I'm not sure exactly what is going on, but I believe Unity is serializing those variables in the editor, but failing to deserialize them in the runtime (because they are compiled out) though the data is there in the input stream. This results in corrupted game object data and often leads to crashes.
It doesn't always happen, but one situation where it does happen almost consistently is with an array of classes marked Serializable, something like this:
[System.Serializable]
class MyClass
{
int var1;
#if UNITY_EDITOR
int var2;
#endif
}
class MyComponent : MonoBehaviour
{
public MyClass[] myItems;
}
I wrestled for most of a day with a crash bug caused by a similar bit of code. Removing the #if UNITY_EDITOR solved the problem for me, so I thought I'd share this in the hopes of saving someone else from the same fate.
Yes, good point. The way Unity's serializer works could break it. Unity will serialize the data but can't deserialize it in the build.
It's actually better to avoid such things in general ;) The inspector should work the same for every object, so i would always go for the static or EditorPrefs way.
Of course you can't store the state per object this way but if you really need that you could try using a dictionary (stored in the Editor class and not your runtime class) which holds the state for different objects. The only difficulty i see is getting a unique key for each object, but i guess GetInstanceID should do the job. It's not guaranteed to be unique across sessions, reimports and such but for this purpose it's enough. The static variables are cleared anyway each recompile ;)
@Bunny83, thanks for pointing me to the EditorPrefs, I was not aware that functionality existed. Looks to me the better alternative to using static fields for keeping the editor state, and I hope the settings get preserved across re-opening the Unity editor itself
To everyone else, I'd recommend using some external to the object storage for the editor state, because keeping editor-only data inside the game object is a bad design decision according to my perspective. It needlessly pollutes the object, causes serialization issues as seen above and increases the memory footprint of the component within the game -- the memory will be there even when the game is ran as standalone, without the editor environment.
Answer by Radmer · Aug 27, 2015 at 12:21 PM
Not sure if it's the best way to do it, but I achieved this effect by saving the state using the EditorPrefs class in the OnEnable and OnDestroy methods of the custom editor script:
void OnDestroy()
{
EditorPrefs.SetBool("FoldedOut", foldedout);
}
void OnEnable()
{
foldedout = EditorPrefs.GetBool("FoldedOut", false);
}
With this approach the state will also be saved between Unity sessions.
If it is used in only one place it works. You it is used in many places, it wont.
It's possible to just use your created hash as string for key to store in EditorPrefs, which seems like more suitable way to store those kind of things, and save between sessions. Combination is always the best way if it brings something useful.
Your answer
Follow this Question
Related Questions
Custom Inspector with field like Camera culling mask 2 Answers
EditorGUI, EditorGUILayout, GUI, GUILayout... pshhh... WHEN TO USE WHAT?! 3 Answers
Editor - GUILayout.BeginArea does not expand ScrollView 2 Answers
Player Settings messed up 1 Answer
Is there a way to change the text size for a Vector3 PropertyField in a custom Inspector? 0 Answers