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:
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 < T > 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)