Custom hot keys manager

Hi everyone
So i wanted to make a little editor window where you can set up hotkeys and execute any functions you want using UnityEvents. Well i didn’t really get far, i have the EditorWindow that seems to work fine but then my first problem is that first of all it only “works” when i have only one KeyCode. The second problem is that I can’t use UnityEvent.Invoke() in edit mode, I tried pretty much everything to make it work but it doesn’t, I’ve set in the unity event to execute in edit mode and at runtime but that didn’t solve it either.

Well here is my code:

using UnityEngine;
using UnityEngine.Events;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;

public class ShortcutManager : EditorWindow
{
	[MenuItem ("Manager/Short Cuts")]
	public static void Init ()
	{
		ShortcutManager window = (ShortcutManager)EditorWindow.GetWindow (typeof(ShortcutManager));
		window.Show ();
	}

	[System.Serializable]
	public class Shortcut
	{
		public List<KeyCode> m_shortCut;
		public UnityEvent m_event = new UnityEvent ();
	};

	private Vector2 scrollPos;

	public List<Shortcut> m_shortCuts;

	private Event currentEvent;

	void OnGUI ()
	{
		scrollPos = EditorGUILayout.BeginScrollView (scrollPos);

		EditorGUILayout.LabelField ("Global shortcut manager", EditorStyles.boldLabel);
		EditorGUILayout.Space ();

		SerializedObject so = new SerializedObject (this);
		SerializedProperty m_shortCutProperty = so.FindProperty ("m_shortCuts");
		EditorGUILayout.PropertyField (m_shortCutProperty, true);
		so.ApplyModifiedProperties ();

		if (GUILayout.Button ("Invoke"))
			m_shortCuts [0].m_event.Invoke ();

		EditorGUILayout.EndScrollView ();

		currentEvent = Event.current;
	}

	void Update ()
	{
		CheckShortCuts ();
		if (Input.GetKeyDown (KeyCode.A))
			m_shortCuts [0].m_event.Invoke ();
	}

	void CheckShortCuts ()
	{
		if (currentEvent == null || !currentEvent.isKey || currentEvent.type != EventType.KeyDown)
			return;

		foreach (Shortcut shortCut in m_shortCuts)
		{
			for (int count = 0; count < shortCut.m_shortCut.Count; ++count)
			{
				if (!((int)currentEvent.character == (int)shortCut.m_shortCut [count]))
					break;
				
				if (count == shortCut.m_shortCut.Count - 1)
				{
					Debug.Log ("Yay?");
					shortCut.m_event.Invoke ();
				}
			}
		}
	}
}

Well for the first problem i know it is embarrassingly bad but i can’t figure it out how to do it better, on lines 63-66 i do the check for all the key codes as you can see i use a foreach loop and check if every key is pressed on after the other but it didn’t work, it only does if there is only 1 key code in the list.

For the second problem i did even a test with the button called Invoke that should invoke the 1 item of the shortCuts list. I’ll post a picture of the Unity event in the Editorwindow, it is pointing to a public function that just debugs a string, but there I can’t figure out how to make it work in edit mode, i sure hope it’s possible D:.

Well thanks in advance and any other suggestions are warmly welcome,

-Jorge

You have several problems here. First of all forget about the Input class when it comes to editor scripts. The Input class only works at runtime.

Next thing is OnGUI does work, but it’s context specific. That means your editor windows OnGUI method does only process events when the window has the keyboard focus. It’s not clear how your hotkeys should behave exactly. Also at the moment your hotkey is restricted to only a single key. For hotkey you usually include optional modifier key combination (CTRL, ALT, SHIFT) otherwise (if you have application wide or system wide hotkeys) you would trigger the hotkey at times where you don’t want it (for example when you enter text in a text field).

Your use of the Event class is just strange. The Event class should only be used inside OnGUI. Storing the Event for later use makes not much sense. Do the handling inside OnGUI. OnGUI might be called several times in a row with different events. At the moment you only get the “last” event that OnGUI had processed.

You also never invalidate “currentEvent” in some way, so as long as “currentEvent” contains a valid key you contantly execute your method until OnGUI processes a new, different event.

It seems you want to check multiple keys at once for one hotkey. That’s actually quite difficult because OnGUI and Event work on “events” not on “states”. So if you press key1 OnGUI would recognise that key if you later press key2 it again recognises that key but doesn’t know about key1 anymore since you didn’t save the key state. At runtime the Input class provides you with key states, however there’s nothing built-in that does this in the editor (except for the modifier keys).

So some suggestions to solve those problems:

  • To get the Events when the SceneView is in focus you might want to subscribe a callback to SceneView.onSceneGUIDelegate. The whole usage of Unity’s Event system might give you more trouble as there’s not a reliable way to get all Events in Unity independent from which window has the focus. You could use an approach as mentioned in this answer, but it’s platform dependent and only works on Windows. You would need an alternative on other platforms.
  • You need to keep track of the key state somewhere. The easiest solution is to create a boolean array which has an element for every KeyCode. Whenever you get a KeyDown event you set the appropriate boolean to true, when you get a KeyUp event you set it back to false. That way you can track the state of all keys at the same time. Keep in mind that key ghosting can get you in trouble.
  • Most hotkeys should do something when the SceneView is in focus, so the onSceneGUIDelegate usually is enough. However if you want system wide hotkey support you have to look out for native OS solutions.