- Home /
EditorWindow: Window works, why does ModalWindow not?
The code beneath works perfectly when I replace GUI.ModalWindow
by GUI.Window
.
public class Test2Window : EditorWindow
{
Rect window_position = new Rect(100, 100, 100, 100);
[MenuItem("SCCD/Open Test")]
public static void Init()
{
Test2Window window = (Test2Window) EditorWindow.GetWindow(typeof(Test2Window), false);
window.title = "Editor Window";
}
private void WindowFunction(int id)
{
GUILayout.Button("test");
}
public void OnGUI() {
this.BeginWindows();
GUI.ModalWindow(0, this.window_position, WindowFunction, "");
this.EndWindows();
}
}
With GUI.ModalWindow
however I get the errors:
ArgumentException: Getting control 0's position in a group with only 0 controls when doing mouseDrag
(mouseDrag
is replaced by the current event, so most of the time it's just mouseMove
)
So is this a bug? Or what magic do I have to perform to get a simple modal window working in an EditorWindow
?
EDIT: if I use GUI
instead of GUILayout
to position the button inside the window, then I do not get the error anymore, however I do not get a window either.
Apparently someone on the forums had the same problem: http://forum.unity3d.com/threads/gui-modalwindow-in-an-editorwindow-throws-errors.193364/. No fix though.
@Unitraxx: I doubt it ^^
$$anonymous$$odalWindow is a quite new thing and ment for the runtime. It is ment to prevent any other GUI interaction while active. The most common example would be dialog boxes. However EditorWindows work a bit different compared to the Unity runtime. That's why you actually have to use BeginWindows and EndWindows. Since there's no layout version of the $$anonymous$$odalWindow they might haven't made it compatible with the layout system.
As workaround you could simply use a normal Window and disable everything else (GUI.enabled = false). For that i would implemeent a custom popup class that wraps such an modal window and simply implement a Stack / List of those windows. If the Stack / List is empty enable the usual GUI. If there is one or more windows on the Stack disable everything except the topmost window object.
Answer by Bunny83 · Dec 06, 2014 at 06:38 PM
Here's a workaround modal window system for editor windows ^^
using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
public class ModalWindow
{
private static int m_WinIDManager = 1337;
protected int m_WinID = m_WinIDManager++;
protected bool enabled = true;
public event System.Action<ModalWindow> OnWindow;
public string title;
public Rect position;
public bool ShouldClose = false;
public int ID { get { return m_WinID; } }
public ModalWindow(Rect aInitialPos, string aTitle)
{
position = aInitialPos;
title = aTitle;
}
public ModalWindow(Rect aInitialPos, string aTitle, System.Action<ModalWindow> aCallback)
{
position = aInitialPos;
title = aTitle;
OnWindow += aCallback;
}
public virtual void OnGUI()
{
// For some strange reason Unity only disables the window border but not the content
// so we save the enabled state and use it inside the window callback down below.
enabled = GUI.enabled;
position = GUI.Window(m_WinID, position, DrawWindow, title);
}
protected virtual void DrawWindow(int id)
{
// restore the enabled state
GUI.enabled = enabled;
if (OnWindow != null)
OnWindow(this);
}
public virtual void Close()
{
ShouldClose = true;
}
}
public class ModalSystem
{
private List<ModalWindow> m_List = new List<ModalWindow>();
public bool IsWindowOpen { get { return m_List.Count > 0; } }
public ModalWindow Top
{
get { return IsWindowOpen ? m_List[m_List.Count - 1] : null; }
}
public void Draw()
{
// remove closed windows
if (Event.current.type == EventType.Layout)
{
for (int i = m_List.Count - 1; i >= 0; i--)
if (m_List[i].ShouldClose)
m_List.RemoveAt(i);
}
if (m_List.Count > 0)
{
// draw all windows
for (int i = 0; i < m_List.Count; i++)
{
GUI.enabled = (i == m_List.Count - 1); // disable all except the last
GUI.BringWindowToFront(m_List[i].ID); // order them from back to front
GUI.FocusWindow(m_List[i].ID); // ||
m_List[i].OnGUI();
}
}
}
public void Add(ModalWindow aWindow)
{
m_List.Add(aWindow);
}
}
public class EditorWindowTest : EditorWindow
{
[MenuItem("Tools/TestWindow")]
public static void Init()
{
GetWindow<EditorWindowTest>();
}
ModalSystem modalWindows = new ModalSystem();
void OpenPopup(string aTitle)
{
var win = new ModalWindow(new Rect(30, 30, position.width - 60, position.height - 60), aTitle, (w) =>
{
if (GUILayout.Button("Open another popup"))
OpenPopup(aTitle + "->subwindow");
if (GUILayout.Button("Close"))
w.Close();
GUI.DragWindow();
});
modalWindows.Add(win);
}
void OnGUI()
{
GUI.enabled = !modalWindows.IsWindowOpen;
if (GUILayout.Button("Open Popup"))
{
OpenPopup("First");
}
if (GUILayout.Button("Some other GUI stuff"))
{
Debug.Log("Doing stuff...");
}
BeginWindows();
modalWindows.Draw();
EndWindows();
}
}
It's just quickly hacked together. Some things could be done in a better way. Since windows in general have some very strange things ongoing in the background you might even drop them all together ^^. If you manually disabling all other content (like in my system above) there should be no issue with overlapping. So you could draw each "window" simply as layout group with a box / window style. Of course window dragging has to be done manually if needed, but that's not that difficult to implement ^^.
This is just ment as example. The window ID generation is sufficient for most cases.
Keep in mind that those classes don't support serialization at the moment. So when you change playmode or scripts are recompiled the ModalSystem will be recreated and all open windows would vanish.
Nice to see an answer, thank you! I will see if I can make this work. What follows is a description of the stuff I tried out this last week and it contains a few questions that maybe you can answer (and solutions that you maybe find interesting?) : I was currently using an extra EditorWindow in combination with ShowPopup() and OnLostFocus(){Focus()}). This was sort of working but the sizing of the window isn't really working out. Currently I throw all content of the popupwindow in a BeginVertical() and then using GUILayoutUtility.GetLastRect() I set the size of the popupwindow. For this to work properly I need to add the border offsets of the popupwindow to the GetLastRect result. (Do you maybe have an idea which GUI style is used, when showing an EditorWindow using ShowPopup()? This would make it a bit easier to do the border correction.) Anyway even with this approach I still have the problem that the width of the popup window isn't satisfactory. The strings in my selection grid aren't fully visible. I kinda assumed that GUILayout would take care of size and allow all labels to be fully visible. Any idea regarding that?
Some more interesting stuff: after a lot of digging around in the API I found PopupWindow. This really looked promising until I started using it with a simple example. I remember it working somewhat O$$anonymous$$, but it was constantly throwing exceptions. $$anonymous$$y main question here is: where do I find the progress on features? Or the ability to report/follow up bugs? I found a page with feature requests where you can vote on, but that didn't really help me unfortunately.
When I get some more time to fool around with the example you provided, it's very likely I'll post some more comments here. And thank you again!
@Unitraxx: EditorWindows are quite complicated. Usual Floating windows have on the top a ContainerWindow which has a SplitView in it. The SplitView contains a DockArea and the DockArea the actual EditorWindow object.
Popup windows (ShowPopup) have the layout ContainerWindow->HostView->EditorWindow. The DockArea is derived from HostView. It is responsible for displaying the tabs at the top. The ordinary HostView can contain only one EditorWindow. When the HostView is drawn it encloses the OnGUI call of your EditorWindow with a GUILayout.BeginVertical with a custom GUIStyle.
It uses the "hostview" style like this:
background = "hostview";
background.padding.top = 0;
where background is a GUIStyle.
The PopupWindow class (and the PopupWindowContent) are pretty pointless ^^. It's just an ordinary EditorWindow shown with ShowAsDropDown.
$$anonymous$$ay i ask what you actually need a modal window for? $$anonymous$$y solution shows the modal window just inside the EditorWindow. Unity-wide modal windows doesn't make much sense since it would prevent the user from doing and intermediate actions. Such an approach should be avoided. Editor extensions that would enforce something like that wouldn't last long in my projects ^^.
However Unity has an internal method called "$$anonymous$$ake$$anonymous$$odal" (inside EditorWindow) which can make a ContainerWindow modal. It's used for the SaveAssets dialog. But as i said it's internal so you can only use it with reflection. $$anonymous$$eep in $$anonymous$$d if you mess up something in your window Unity is unusable if you can't close the window.
In my tool I basically want to pop up a list of options and the user has to make a choice before being able to advance. Anyway, how do you know all this? Where do you find out this ContainerWindow->HostView->EditorWindow structure? Is there more documentation I'm not aware of? Or can you look into all this directly with the pro version?
A modal window inside the EditorWindow would indeed be preferred so I'm definitely going to try out what you proposed. I however work with a different formalism/program$$anonymous$$g methodology. (I use a higher level of abstraction to develop my tool, it's the subject of my $$anonymous$$asters thesis. $$anonymous$$odularity is key and so far this method hasn't been used with game engines so far. I built a compiler that generates C# code from my formalism. The tool I'm building is actually going to provide a visual editing environment for this formalism.)
Anyway thank you for your information, I'll keep you updated!
@Unitraxx:
Sounds interesting :)
I use ILSpy but any .NET reflector clone will do. Just open the UnityEditor.dll (and UnityEngine.dll) found at
Unity/Editor/Data/$$anonymous$$anaged/
There are several visual scripting approaches out there for Unity. As far as i remember the antares universe project also partially generates C# code similar to Visual Studio's form editor. However i'm not a friend of such editors :) Such editors usually change the way you work with Unity completely but every now and then you hit a border and you're required to write custom nodes.
I understand those limitations and that's also why I try to avoid the term visual scripting. It isn't aimed at solely using visual elements to develop, it will actually require a large amount of code to still be written. It takes care of events, hierarchy, parallelism and stuff like that. This method (Domain Specific Language and Visual $$anonymous$$odelling if you would want to look up things) is actually already used for the development of critical software like power plants, air planes, car electronics etc. $$anonymous$$y thesis is aimed specifically at proving the usability of this for the creation of complex game AI in commercial game engines. The concept of custom nodes simply doesn't apply. Anyway it's still in research we can't predict if it will be of any use. What's likely to happen is that people expect it to be a tool for non-programmers, while that's not at all the target audience. Also since it's just a masters thesis subject, it's pretty likely that it will never get completely finished.
Your answer
Follow this Question
Related Questions
How to force Unity to Repaint an EditorWindow? 1 Answer
Editor GUI Foldout header style customization 0 Answers
Custom Curve Editor? 0 Answers
How to draw button in editor window rotated by 90 degrees? 0 Answers
display editor script in the game window 0 Answers