Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
1 capture
13 Jun 22 - 13 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
4
Question by vexe · Feb 11, 2014 at 04:36 PM · editoreditorwindowgenerics

Unity doesn't support generic editor windows? (GetWindow )

So I made a cool GameObject selection window that picks up all GOs in the scene, you could search, filter, etc.

It looks something like this:

alt text

Now I'm trying to parametrize it, make it generic and inject what's needed - Having "Re-usability" and good coding practice in mind.

The idea is for example, let's say I wanted a selection window for GameObjects, is to call SelectionWindow < GameObject > .Show(this, () => GameObject.FindObjectsOfType);

Or maybe I want to view all scenes in my project, I would pass SelectionWindow < string > .Show(this, () => Utils.GetAssets<object>("Assets", "*.unity", SearchOption.AllDirectories)); etc...

'this' is the caller, which should be implementing an ISelectionWindowUser interface.

The delegate, is the item assignment delegate - basically what to show up in the window.

What I'm failing at however, is showing up the window:

 public static void Show(ISelectionWindowUser<T> user, Func<T[]> refreshAction)
 {
     var window = GetWindow<SelectionWindow<T>>(); // BREAKS! NullReferenceException :((
     //var window = GetWindow(typeof(SelectTargetGoWindow<T>)) as SelectTargetGoWindow<T>; // also tried this...
     window.Init(user, refreshAction);
     window.ShowUtility();
 }

Full code:

SelectionWindow.cs

 using UnityEngine;
 using System.Linq;
 using System.Collections.Generic;
 using UnityEditor;
 using System.Text.RegularExpressions;
 using System;
 using Object = UnityEngine.Object;

 public class SelectionWindow<T> : EditorWindow where T : Object
 {
     private ISelectionWindowUser<T> user;
     private Func<T[]> refreshAction;
     private bool hasInitStyles;
     private Vector2 scrollPosition;
     private string search = "";
     private const float INDENT_WIDTH = 20f;
     private T[] items;
     private T[] filteredItems;

     private StyleDue styleDue;

     // GUI Styles...

     private void InitStyles()
     {
         // Initializing styles...
     }

     public static void Show(ISelectionWindowUser<T> user, Func<T[]> refreshAction)
     {
         var window = GetWindow(typeof(SelectionWindow<T>)) as SelectionWindow<T>;

         //var window = GetWindow<SelectTargetGoWindow<T>>();
         window.Init(user, refreshAction);
         window.ShowUtility();
     }
     private void Init(ISelectionWindowUser<T> user, Func<T[]> refreshAction)
     {
         this.user = user;
         this.refreshAction = refreshAction;
         styleDue = new StyleDue(Utils.HexToColor("CCCCCC"), Utils.HexToColor("BABABA"));
     }

     void OnGUI()
     {
         if (!hasInitStyles) {
             hasInitStyles = true;
             InitStyles();
         }

         GUIHelper.HorizontalBlock(() =>
         {
             GUILayout.Label("GameObjects", GameObjectsLabel);
             search = EditorGUILayout.TextField("", search);
             filteredItems = items.Where(go => Regex.IsMatch(go.name, search, RegexOptions.IgnoreCase)).ToArray();
             if (GUILayout.Button(new GUIContent("↶", "Refresh"), RefreshButton, GUILayout.Width(20)))
                 Refresh();
         });

         scrollPosition = GUIHelper.ScrollViewBlock(scrollPosition, false, false, () =>
         {
             foreach (var item in filteredItems) {
                 bool selected = item == user.target;
                 var nextStyle = styleDue.NextStyle;
                 GUIHelper.HorizontalBlock(selected ? SelectedStyle : nextStyle, () =>
                 {
                     GUILayout.Space(INDENT_WIDTH);
                     var spaceRect = GUILayoutUtility.GetLastRect();
                     GUILayout.Label(item.name, selected ? SelectedLabel : UnselectedLabel);
                     var labelRect = GUILayoutUtility.GetLastRect();
                     var buttonRect = GUIHelper.CombineRects(spaceRect, labelRect);
                     if (!selected) {
                         EditorGUIUtility.AddCursorRect(buttonRect, MouseCursor.Link);
                         if (GUI.Button(buttonRect, "", GUIStyle.none)) {
                             user.target = item;
                         }
                     }
                 });
             }
         });
     }

     void Refresh()
     {
         items = refreshAction();
     }
     void OnFocus()
     {
         Refresh();
     }
 }


ISelectionWindowUser.cs

 public interface ISelectionWindowUser<T> where T : Object
 {
     T target { get; set; }
 }

User code:

     if (GUILayout.Button(new GUIContent("Select target", "Select a target game object to inspect"), SelectButton, GUILayout.Height(20))) {
         SelectionWindow<GameObject>.Show(this, () => GameObject.FindObjectsOfType<GameObject>());
     }

As mentioned before the user should implement ISelectionWindowUser where T is what he's interested in (GameObject, etc)

I'll be really sad if I hear that Unity doesn't support this, I mean come on...

Again, my problem is this GetWindow < SelectionWindow < GameObject > >();

Any help would be very appreciated!

Thanks.

EDIT:

Full error:

 NullReferenceException: Object reference not set to an instance of an object
 UnityEditor.EditorWindow.GetWindow (System.Type t, Boolean utility, System.String title, Boolean focus) (at C:/BuildAgent/work/d3d49558e4d408f4/artifacts/EditorGenerated/EditorWindow.cs:423)
 UnityEditor.EditorWindow.GetWindow[SelectionWindow`1] (Boolean utility, System.String title, Boolean focus) (at C:/BuildAgent/work/d3d49558e4d408f4/artifacts/EditorGenerated/EditorWindow.cs:461)
 UnityEditor.EditorWindow.GetWindow[SelectionWindow`1] () (at C:/BuildAgent/work/d3d49558e4d408f4/artifacts/EditorGenerated/EditorWindow.cs:434)
 SelectionWindow`1[UnityEngine.GameObject].Show (ISelectionWindowUser`1 user, System.Func`1 refreshAction) (at Assets/Editor/SelectionWindow.cs:89)
 ShowInspectorWindow.<OnGUI>m__22 () (at Assets/Editor/ShowInspectorWindow.cs:125)
 GUIHelper.HorizontalBlock (System.Action block) (at Assets/Editor/GUIHelper.cs:54)
 ShowInspectorWindow.OnGUI () (at Assets/Editor/ShowInspectorWindow.cs:111)
 System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at /Users/builduser/buildslave/monoAndRuntimeClassLibs/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:222)
 Rethrow as TargetInvocationException: Exception has been thrown by the target of an invocation.
 System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at /Users/builduser/buildslave/monoAndRuntimeClassLibs/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:232)
 System.Reflection.MethodBase.Invoke (System.Object obj, System.Object[] parameters) (at /Users/builduser/buildslave/monoAndRuntimeClassLibs/build/mcs/class/corlib/System.Reflection/MethodBase.cs:115)
 UnityEditor.HostView.Invoke (System.String methodName, System.Object obj) (at C:/BuildAgent/work/d3d49558e4d408f4/Editor/Mono/GUI/DockArea.cs:231)
 UnityEditor.HostView.Invoke (System.String methodName) (at C:/BuildAgent/work/d3d49558e4d408f4/Editor/Mono/GUI/DockArea.cs:224)
 UnityEditor.HostView.OnGUI () (at C:/BuildAgent/work/d3d49558e4d408f4/Editor/Mono/GUI/DockArea.cs:120)

stuck.png (5.7 kB)
Comment
Add comment · Show 2
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image whydoidoit · Feb 11, 2014 at 04:45 PM 0
Share

Interesting - can you use CreateInstance to create a that editor window?

avatar image vexe · Feb 11, 2014 at 04:51 PM 0
Share

Thanks for your fast reply! - Creating a new instance yielded the same error. I'll try your 2nd suggestion.

3 Replies

· Add your reply
  • Sort: 
avatar image
7
Best Answer

Answer by Loius · Feb 11, 2014 at 06:39 PM

While you can't create a generic window, you could have a generic class with all your functionality in it wrapped by a non-generic window that accepts a type parameter to create a strongly-typed version of your generic class.

I've been using Reflection and System.Type shenanigans a lot lately gosh it's fun. I made a very basic example, you should be able to plug'n'play without too much work:

 using UnityEngine;
 using UnityEditor;
 
 // this just lets you use "myImplementation as ISelectionWindow" instead of going through reflection to convert to a real selectionwindow
 // i value the code legibility over the extra interface
 public interface ISelectionWindow {
     void OnGUI();
 }
 
 public class SelectionWindow<T> : ISelectionWindow {
     // your code here
     public void OnGUI() {
         GUILayout.Label("I'm of type " + typeof(T));
     }
 }
 
 public class SelectionWindowWrapper : EditorWindow {
     System.Type _type=null;
     // I wasn't able to quickly find a solution to "have a variable that could represent a specific generic class of any type"
     // but object works fine - note the little o, this is C#'s object not Unity's Object
     object myImplementation=null;
 
     [MenuItem("Window/Custom Tools/Selection Window")] // for testing purposes
     public static void Init() {
         SelectionWindowWrapper sww = EditorWindow.CreateInstance<SelectionWindowWrapper>();
         sww.Show();
     }
 
     public System.Type Type {
     get { return _type.GetGenericArguments()[0]; }
         set {
             // These two lines are how you get your specific implementation
             _type = typeof(SelectionWindow<>).MakeGenericType(value);
             myImplementation = System.Activator.CreateInstance(_type);
         }
     }
     
     void OnGUI() {
         if ( null == myImplementation ) { // provide selection options or prevent this condition
             if ( GUILayout.Button("GameObject") ) Type = typeof(GameObject); // notice capital T, we're using the setter
             return;
         }
         (myImplementation as ISelectionWindow).OnGUI();
     }
 }
Comment
Add comment · Show 4 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image vexe · Feb 12, 2014 at 02:44 AM 1
Share

Whaooo!! you sneaky!! I love that!! IT ALSO WOR$$anonymous$$ED!! XD THAN$$anonymous$$ YOU!!

For the user and func, I had to do this - here's the full Init method now:

 public void Init<T>(ISelectionWindowUser<T> user, Func<T[]> func)
 {
     var t = typeof(T);

     windowType = typeof(SelectionWindow<>).$$anonymous$$akeGenericType(t);
     implementation = Activator.CreateInstance(windowType); // SelectionWindow<T>

     var flags = BindingFlags.NonPublic | BindingFlags.Instance;
     var fields = windowType.GetFields(flags);

     // set the user
     FieldInfo userField = fields.FirstOrDefault(f => f.FieldType == typeof(ISelectionWindowUser<>).$$anonymous$$akeGenericType(t));
     userField.SetValue(implementation, user);

     // set the delegate
     FieldInfo funcField = fields.FirstOrDefault(f => f.FieldType == typeof(Func<>).$$anonymous$$akeGenericType(t.$$anonymous$$akeArrayType()));
     funcField.SetValue(implementation, func);
 }
avatar image vexe · Feb 12, 2014 at 02:59 AM 1
Share

JUST LOO$$anonymous$$ AT IT! :)

alt text

stuck.png (20.2 kB)
avatar image vexe · Feb 12, 2014 at 03:19 AM 0
Share

lol why did I manually assign the fields? I already have an init method, so just:

 var init = methods.FirstOrDefault(m => m.Name == "Init");
 init.Invoke(implementation, new object[] { user, func });
avatar image Loius · Feb 12, 2014 at 03:38 AM 0
Share

Pretty slick. I won't lie, now that I've done created a generic-type window I'm probably going to end up porting my own gameobject filters into actual windows. :)

avatar image
2

Answer by whydoidoit · Feb 11, 2014 at 04:46 PM

I guess at worse you could try:

 public class SomeEditorWindow : SelectionWindow<GameObject> {
 }

And then GetWindow on SomeEditorWindow...

Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image vexe · Feb 11, 2014 at 05:27 PM 1
Share

ahh, it seems you're right. I took it down to the barebone. I created a GameObjectSelectionWindow which is just:

 public class GameObjectSelectionWindow : SelectionWindow<GameObject>
 {
 
 }

and then replaced var window = GetWindow < SelectionWindow < GameObject > >(); with var window = GetWindow < GameObjectSelectionWindow > (); and it seemed to have worked! This is plain stupid!.....

This ruins the whole idea, which is NOT writing new window classes for each type... Unity seems to does well in encouraging code repetition...

If you find out of a way to get it to work how I like it to, please let me know. Thanks. For now I'll convert your comment to answer and tick it.

avatar image
0

Answer by yazZ6va · Apr 25, 2021 at 09:23 AM

     public class SelectionWindow : EditorWindow
     {
         private IGenericWindow genericWindow;
         public static void Show<T>(T t) 
         {
             SelectionWindow window = EditorWindow.GetWindow<SelectionWindow>(false, "ADD");
             window.minSize = new Vector2(300.0f, 500.0f);
             GenericWindow<T> genericWindow = new GenericWindow<T>();
             genericWindow.t = t;
             window.genericWindow = genericWindow;
         }
         public void OnGUI()
         {
             genericWindow.OnGUI();
         }
     }
 
     public class GenericWindow<T> : IGenericWindow
     {
         public T t;
 
         public void OnGUI()
         {
         }
     }
 
     public interface IGenericWindow
     {
         void OnGUI();
     }
Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

21 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Huge Performance difference between docked and undocked EditorWindow 0 Answers

Editor Color Picker issues 0 Answers

How to override geometry of a procedurally generated sprite in Editor Mode 0 Answers

Editor: Drawing outside the own window 0 Answers

Multiple editor windows combined 0 Answers


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges