📜 ⬆️ ⬇️

Unity3D 3.x Get the current active window

Recently, our team faced a fairly simple task. We needed to do dragging things from inventory to other windows (equip, chest). If the two windows are located above each other, then the thing should fall into the window that is higher.

After a quick scan of the property list in the GUI class, I did not find anything suitable, then I looked at the GUIUtility, and even looked at GUILayout. In general, this property was not there. Googling for this request gives a few questions in Q & A and a couple of scant posts on the off. forum that ends with the answers in the style of "so you can not do, but you can manually track on what window clicked the mouse and fill the variable of the active window independently."
We did not come up with anything from what was offered there, but one guy gave me an interesting idea. We write code in C #, which means we can use all the pluses of this language, including C # Reflection

Guts

After downloading my favorite Dis # , I immediately got into the GUI.Window function code .

public static Rect Window(int id, Rect clientRect, GUI.WindowFunction func, string text) { return GUI.DoWindow(id, clientRect, func, GUIContent.Temp(text), GUI.skin.window, true); } internal static Rect DoWindow(int id, Rect clientRect, GUI.WindowFunction func, GUIContent title, GUIStyle style, bool forceRectOnLayout) { GUIUtility.CheckOnGUI(); GUI._Window _window = (GUI._Window)GUI._WindowList.instance.windows[id]; if (_window == null) { _window = new GUI._Window(id); GUI._WindowList.instance.windows[id] = _window; GUI.s_LayersChanged = true; } if (!_window.moved) _window.rect = clientRect; _window.moved = false; _window.opacity = 1.0F; _window.style = style; _window.title.text = title.text; _window.title.image = title.image; _window.title.tooltip = title.tooltip; _window.func = func; _window.used = true; _window.enabled = GUI.enabled; _window.color = GUI.color; _window.backgroundColor = GUI.backgroundColor; _window.matrix = GUI.matrix; _window.skin = GUI.skin; _window.contentColor = GUI.contentColor; _window.forceRect = forceRectOnLayout; return _window.rect; } 

Yeah, it means there is a list of windows, it remains to find out in what sequence they are drawn, for this we will look at the GUI function.BringWindowToFront

  public static void BringWindowToFront(int windowID) { GUIUtility.CheckOnGUI(); GUI._Window _window1 = GUI._WindowList.instance.Get(windowID); if (_window1 != null) { int i = 0; foreach (GUI._Window _window2 in GUI._WindowList.instance.windows.Values) { if (_window2.depth < i) i = _window2.depth; } _window1.depth = i - 1; GUI.s_LayersChanged = true; } } 

')
Everything is clear, in the class GUI there is a singleton class _WindowList which has a list of windows. Every window has a Depth . Rendering occurs in descending order Depth . All that remains is to find out what kind of this list.

  internal sealed class _WindowList { internal Hashtable windows; internal static GUI._WindowList instance; ....... 

So we learned :)

We write function for vybyvyrivaniya good

The function is well commented and hopefully does not need an explanation.
  /// <summary> ///        /// </summary> /// <returns> /// ID    /// </returns> /// <param name='id_list'> ///  ID  /// </param> int GetTopmostId(List<int> id_list) { //  GUI Type guiType = typeof(GUI); //    Type windowListType = guiType.Assembly.GetType("UnityEngine.GUI+_WindowList"); //  instance ,      ( ) FieldInfo windowListInstanceField = windowListType.GetField("instance", BindingFlags.NonPublic | BindingFlags.Static); //  ,      object windowListInstance = windowListInstanceField.GetValue(null); //     FieldInfo windowsField = windowListType.GetField("windows", BindingFlags.NonPublic | BindingFlags.Instance); //     Hashtable Hashtable hashtable = windowsField.GetValue(windowListInstance) as Hashtable; //      int min = -1; int window_id = -1; foreach(DictionaryEntry entry in hashtable) { int key = (int)entry.Key; if (id_list.Contains(key)) //       { //      int depth = (int)entry.Value.GetType().GetField("depth", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(entry.Value); if (min < 0 || depth < min) { min = depth; window_id = key; } } } return window_id; } 

Note: if you are going to call the OnGUI () event function, I recommend splitting it into two parts, and storing the Hashtable in a class variable, so as not to waste time each time figuring out a heap of types and fields.

For the minus: there is no alternative to this solution, if you need to know at what level the window is now, then this is the only way

Source: https://habr.com/ru/post/149603/


All Articles