- Home /
How can I consume key events to prevent them showing up in GUILayout.TextFields?
I've added a debug console to my game to allow for easy testing by, say, putting items into the player's inventory. It can be toggled open/closed by typing a backtick (`). Unity refers to this as KeyCode.BackQuote. The problem I'm having is that when I open the console, the backtick character ends up in the input field, which I don't want. I've tried to deal with this by using Event.current.Use() on all backtick events in OnGUI, but this doesn't seem to help. The only solution I've come up with is to use a regex to strip out any backtick characters, but this feels like fighting the fire rather than dealing with the problem. Is there a more elegant way of keeping the backtick out of the input field?
Example C# code:
using System.Collections.Generic;
using UnityEngine;
using System.Collections;
using System.Text.RegularExpressions;
public class CommandConsole : MonoBehaviour
{
private Rect GuiRect;
public List<string> messages = new List<string>();
private string inputLine = ""; //This is where text entered into the console ends up.
private Vector2 scrollPos = Vector2.zero;
private Color textColour;
public float fadeoutTimer = 5.0f;
public float fadeSpeed = 0.5f;
private float inactiveTimer;
private GUIStyle labelStyle;
public void Start()
{
GuiRect = new Rect(0, 0, Screen.width, 300);
}
void OnGUI() {
//Uncomment this odd bit of code and the problem goes away
//if (Event.current.type == EventType.KeyDown && Event.current.keyCode==KeyCode.None) {
// Event.current.Use();
// return;
//}
if (Event.current.keyCode == KeyCode.BackQuote) {
if (Event.current.type == EventType.KeyDown) {
if (GUI.GetNameOfFocusedControl()=="ConsoleInput") {
GUI.FocusControl("");
}
else {
labelStyle = GUI.skin.label;
labelStyle.border = new RectOffset(0, 0, 0, 0);
labelStyle.margin = new RectOffset(0, 0, 0, 0);
labelStyle.padding = new RectOffset(4, 4, 1, 1);
GUI.FocusControl("ConsoleInput");
inactiveTimer = fadeoutTimer;
textColour = Color.white;
inputLine=""; //Reset this here
}
}
Event.current.Use(); //I was hoping this would fix it
return; //Returning early doesn't help either
}
else {
//Fade out the console if we're done with it.
if (GUI.GetNameOfFocusedControl() != "ConsoleInput") {
if (inactiveTimer <= 0.0f) {
if (textColour.a > 0.0f) {
textColour.a -= fadeSpeed * Time.deltaTime;
}
}
else {
inactiveTimer -= fadeSpeed * Time.deltaTime;
}
}
}
Color previousGUIColor = GUI.color;
GUI.color = textColour;
GUI.SetNextControlName("");
GUILayout.BeginArea(this.GuiRect,GUI.skin.box);
scrollPos = GUILayout.BeginScrollView(scrollPos);
GUILayout.FlexibleSpace();
for (int i = messages.Count - 1; i >= 0; i--)
{
GUILayout.Label(messages[i], labelStyle);
}
GUILayout.EndScrollView();
GUILayout.BeginHorizontal();
GUI.SetNextControlName("ConsoleInput");
Debug.Log ("EventType: "+Event.current.type.ToString()+", Key: "+Event.current.keyCode.ToString());
Debug.Log ("Before: "+inputLine);
inputLine = GUILayout.TextField(inputLine);
Debug.Log("After: "+inputLine);
GUILayout.EndHorizontal();
GUILayout.EndArea();
GUI.color = previousGUIColor;
//Alternatively, putting this line back in also solves the problem
//inputLine=Regex.Replace(inputLine, "`", ""); //Ick
}
}
Update: More strange behaviour. With copious debug I've noticed that the GUILayout.TextField call adds the backtick to the string when Event.current is a KeyDown event with a keycode of KeyCode.None. That has me scratching my head, it doesn't seem to make much sense as an event. However, if I Use() that event and return, the backtick doesn't appear in inputLine at any point.
So that's another odd-looking solution I could go with. Does anybody know why Unity is producing KeyDown events with no key?
@perchik - Unfortunately, I don't think Event.$$anonymous$$eyboardEvent will help, it's just another way of interrogating Event.current (or creating more events, but I don't want to do that).
I don't see how it would still appear after you set it to an empty string. Can you post the whole code so we can test it out for you?
@vexe - It seems to be this odd $$anonymous$$eyCode.None event that's doing me in. So I set inputLine to the empty string when I open the console on the $$anonymous$$eyCode.BackQuote $$anonymous$$eyDown event, but then the later event comes along and the backtick appears.
I've added enough code for the UI to work (although the website appears to be hiding everything above the Start() method, which is odd), so you should be able to take a look. There's some debug in there, so you can see the point at which the backtick appears.
well, there's so many missing variable definitions.. but I'll see if I could fix them..
Answer by Jenesis · Mar 18, 2014 at 02:58 PM
I mentioned this problem in passing to a colleague, who gave me the answer straight away. Pressing the backtick key generates TWO KeyDown events:
1) The first event has keyCode set to KeyCode.BackQuote - this tells you which physical key on the keyboard was pressed (after being mapped through the keyboard layout, at least in Windows)
2) The second event has keyCode set to KeyCode.None and character set to '`' - this is the character that will be added to the text string in the GUILayout.TextField call
This is useful when different keys can give the same character. If you press the '1' key above the letters, the first event will have a keyCode of Alpha1, and the second event will have a character of '1'. If you press the '1' key on the keypad then the first event will have a keyCode of Keypad1, and the second event will have a character of '1' if NumLock is on, or (at least on my keyboard) 'End' if it isn't.
So although I was using the KeyCode.BackQuote event to toggle the console, it was the later None event with the character field set that was actually putting the '`' in the text field. So I've now modified my code to check for KeyDown events with a backtick character and ignore them.
Cool - thanks for sharing. But doesn't this mean that, if you press any other key (like 1 as you mentioned) you should also get like, two 1s?
Only the characters get used for text input.
Pressing the left Alt key generates an event with the keyCode set to LeftAlt, but no event with a character set, because Alt is a meta key and doesn't generate characters by itself. Therefore pressing Alt by itself doesn't put anything into your text box.
Answer by vexe · Mar 18, 2014 at 11:10 AM
Well that was tough :D - So I stripped it down to the wire and this seems to work, not sure exactly why though...
private bool hasFocus;
private const string CONSOLE_INPUT = "ConsoleInput";
void OnGUI()
{
if (Event.current.type == EventType.keyDown)
if (Event.current.keyCode == KeyCode.BackQuote) {
ToggleFocus();
inputLine = "";
}
GUILayout.BeginArea(GuiRect, GUI.skin.box);
{
GUILayout.FlexibleSpace();
GUILayout.BeginHorizontal();
{
GUI.SetNextControlName(CONSOLE_INPUT);
string value = GUILayout.TextField(inputLine);
if (hasFocus) inputLine = value; // this was the key solution
}
GUILayout.EndHorizontal();
}
GUILayout.EndArea();
}
private void ToggleFocus()
{
hasFocus = GUI.GetNameOfFocusedControl() == "ConsoleInput";
if (hasFocus) Unfocus();
else Focus();
}
private void Focus()
{
GUI.FocusControl(CONSOLE_INPUT);
}
private void Unfocus()
{
GUI.FocusControl("");
}
I highly recommend that you use another GUI alternative (for very obvious reasons, like you saw...) - I've used NGUI and it's really nice (in fact I've done something exactly the same - a small terminal for my inventory - I didn't face any of this trouble)
The 'if (hasFocus)' line does indeed keep the backtick out of the string, but now I can't type anything into the console - it's all ignored. I think this is because your hasFocus variable is backwards, - you set it in ToggleFocus before the toggle, so it's really doesNotHaveFocus. That means that you're throwing away all keypresses when the ConsoleInput component has focus.
And yes, another GUI system would certainly help. At this point I'm just trying to understand what's going on (especially given the $$anonymous$$eyCode.None strangeness).
lol crap I was so anxious to get the tick to work I didn't even test the input :D - I'll fix it
Alright it's acting up again :/ perhaps your regex is not so bad after all... I'm not gonna spend any more time on it - neither should you, either go with your regex or better yet start with NGUI right away. UnityGUI is a waste of time and effort! ... Cheers!