Get KeyCode events in editor without object selected

I’m trying to write an editor extension that gets keyboard events. There’s two ways I’ve found:

using UnityEngine;
using UnityEditor;
using System.Collections;

[CustomEditor(typeof(Transform))]
public class MyScript : Editor
{
	void OnSceneGUI()
	{
		if (Event.current.isKey && Event.current.type == EventType.keyDown)
		{
			if (Event.current.keyCode == KeyCode.Keypad1)
			{
				Debug.Log("1 pressed");
			}
		}
	}
}

The code above works, but requires that a gameobject is selected in the scene. Otherwise it will not work.

The second option involves using keyboard shortcuts for menu items:

using UnityEngine;
using UnityEditor;
using System.Collections;

public class MyScript : MonoBehaviour
{
	[MenuItem("CustomMenu/Button _1")]
	static void Button()
	{
		Debug.Log("1 pressed");
	}
}

The problem with that is that it’s very limited in its capabilities. For example, I can’t check that the number 1 in the example is from the numpad. I also can’t check for certain key combinations such as Ctrl + Numpad 1 because %1 does not work for numpad input.

I need to check for the KeyCodelike in the first example, but not have to have an object selected in the scene. I found this answer but it does not expose an Event for which I can check the KeyCode.

How can I get KeyCode events in the editor scene view without requiring an object to be selected?

Thanks to kruncher on the Unity IRC, here’s a working solution!

using UnityEditor;
using UnityEngine;

[InitializeOnLoad]
public static class YourCustomSceneView
{
	static YourCustomSceneView()
	{
		SceneView.onSceneGUIDelegate += OnSceneGUI;
	}

	private static void OnSceneGUI(SceneView sceneView)
	{
		// Do your general-purpose scene gui stuff here...
		// Applies to all scene views regardless of selection!

		// You'll need a control id to avoid messing with other tools!
		int controlID = GUIUtility.GetControlID(FocusType.Passive);

		if (Event.current.GetTypeForControl(controlID) == EventType.keyDown)
		{
			if (Event.current.keyCode == KeyCode.Keypad1)
			{
				Debug.Log("1 pressed!");

				// Causes repaint & accepts event has been handled
				Event.current.Use();
			}
		}
	}
}

To disable it, you can use SceneView.onSceneGUIDelegate -= OnSceneGUI;

You can also check if a modifier key is down at the same time as checking the keycode. For example, with the Control modifier key, use Event.current.control

Some thoughts on the above method since there isn’t much documentation on this topic:

Firstly, when dealing with SceneView and OnSceneGUI, remember to, as with any event subscription, to attempt to unsubscribe first, just in case there are lingering subscriptions hanging around, especially if this was called in OnEnable(), which is the most common way of dealing with Editor scripts like this:

public void OnEnable() {
SceneView.onSceneGUIDelegate -= OnSceneGUI;
SceneView.onSceneGUIDelegate += OnSceneGUI;
}

Secondly, keep in mind that this event only fires when there has most-recently been input to the scene window/tab, such as clicking in the Scene. On the other hand, if you played with controls in other GUI windows, for example, like the inspector or your own custom EditorWindow/Inspector, the OnSceneGUI event will simply not fire until you click back on the scene window/area to return focus to it. This means you will have to duplicate input for any window using OnGUI if you want the keyboard shortcut to be fired from anywhere.