Is it possible to prevent a custom EditorWindow from closing?
Perhaps by removing the close button?
I have a custom editor in which the user can enter a lot of settings and I want to prevent accidental closing of the window which could cause a lot of lost work.
If close is clicked I want to pop up a dialogue saying "Are you sure? "
I know I can catch the close event in OnDestroy but is there a way to prevent it closing if the button is clicked?
One method I thought of might be to hide the normal close button so it can’t be clicked and then add my own custom one, but I’m not sure if the close button can be hidden.
Unfortunately that’s not directly possible. An EditorWindow class is not an actual window. It just represent the content. This content might be embedded in a tab view which is in a ContainerWindow or in a SplitView. This could be again either in a ContainerWindow or in the MainWindow. The ContainerWindow can’t be controlled by the content. A single Window might be responsible for multiple things. If a ContainerWindow is closed the contained tabs / HostViews are destroyed along.
However there’s a solution to not loose your content ^^. I’ve found two approaches which can be done in OnDestroy:
Make all variables that should survive public in your EditorWindow (or mark them with SerializeField). That way they can be serialized. Now just use “Instantiate” on “this” to create a copy of your current EditorWindow instance. Finally just call “Show” and a new window with the same content will show up. If it’s a floating window it almost seems nothing has changed ^^.
Do the same but manually. Create a new instance of your EditorWindow with CreateInstance. Now copy everything you want to survive over to the new instance. In that case it doesn’t matter if the variables are public or private but you have to copy every variable on it’s own. The advantage is that you can copy things which aren’t serializable like Dictionaries, …
Keep in mind to add a flag in OnDestroy which will disable this “rescue” procedure. Otherwise you litteraly can’t “get rid” of the window / tab.
Note: In your EditorWindow was sitting in a tabview and you just closed the “tab”, after the “rescue” it will be located in a new window since it’s not yet possible (without reflection hacks) to add an EditorWindow to a SplitView (aka docking the window).
Final note: Of course you could go for a hybrid solution of 1. and 2. at the same time. Using Instantiate will copy all public serializable stuff and you just have to take care of the non serializable stuff.
Here’s a very quick and dirty example:
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
public class TestEditorWindow : EditorWindow
{
bool canBeClosed = false;
public string someContent = "";
[SerializeField] private string m_TmpKey = "";
[SerializeField] private string m_TmpContent = "";
private Dictionary<string, string> myDict = new Dictionary<string, string>();
[MenuItem("Tools/TestWindow")]
static void Init()
{
GetWindow<TestEditorWindow>();
}
void OnGUI()
{
canBeClosed = GUILayout.Toggle(canBeClosed, "Can Be Closed");
someContent = GUILayout.TextField(someContent);
foreach (var key in myDict.Keys)
{
var content = myDict[key];
GUILayout.BeginHorizontal();
var newKey = GUILayout.TextField(key,GUILayout.Width(150));
if (newKey != key)
{
myDict.Remove(key);
myDict.Add(newKey, content);
GUIUtility.ExitGUI();
}
GUILayout.Label("=", GUILayout.Width(30));
var newContent = GUILayout.TextField(content);
if (content != newContent)
{
myDict[key] = newContent;
GUIUtility.ExitGUI();
}
if (GUILayout.Button("del",GUILayout.Width(40)))
{
myDict.Remove(key);
GUIUtility.ExitGUI();
}
GUILayout.EndHorizontal();
}
GUILayout.BeginHorizontal("box");
m_TmpKey = GUILayout.TextField(m_TmpKey, GUILayout.Width(150));
GUILayout.Label("=", GUILayout.Width(30));
m_TmpContent = GUILayout.TextField(m_TmpContent);
if (GUILayout.Button("Add", GUILayout.Width(40)))
{
if (myDict.ContainsKey(m_TmpKey))
ShowNotification(new GUIContent("Key '"+m_TmpKey+"' already exists"));
else
myDict.Add(m_TmpKey, m_TmpContent);
}
GUILayout.EndHorizontal();
}
void OnDestroy()
{
if (!canBeClosed)
RescueContent();
}
void RescueContent()
{
var newWin = Instantiate<TestEditorWindow>(this);
newWin.myDict = myDict;
newWin.Show();
}
}