Skip to content

Instantly share code, notes, and snippets.

@MadLittleMods
Created May 20, 2015 04:23
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MadLittleMods/e1433225d1a31f4b4d92 to your computer and use it in GitHub Desktop.
Save MadLittleMods/e1433225d1a31f4b4d92 to your computer and use it in GitHub Desktop.
Unity GUI text area: `MLMEditorGUI.TextAreaWithTabs(Rect position, string text)`
using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Reflection;
using self = MLMEditorGUI;
using System.Collections.Generic;
public class MLMEditorGUI : MonoBehaviour {
static FieldInfo TextEditor_m_HasFocus__FieldInfo;
static PropertyInfo GUIUtility_textFieldInput__PropertyInfo;
static MethodInfo GUIUtility_CheckOnGUI__MethodInfo;
static MethodInfo TextEditor_UpdateScrollOffsetIfNeeded__MethodInfo;
static MethodInfo GUIContent_Temp__MethodInfo;
static FieldInfo EditorGUI_s_RecycledEditor__FieldInfo;
static FieldInfo EditorGUI_activeEditor__FieldInfo;
static MethodInfo RecycledTextEditor_EndEditing__MethodInfo;
static MethodInfo RecycledTextEditor_BeginEditing__MethodInfo;
static MLMEditorGUI() {
BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy;
self.TextEditor_m_HasFocus__FieldInfo = typeof(TextEditor).GetField("m_HasFocus", bindingFlags);
self.GUIUtility_textFieldInput__PropertyInfo = typeof(GUIUtility).GetProperty("textFieldInput", bindingFlags);
self.GUIUtility_CheckOnGUI__MethodInfo = typeof(GUIUtility).GetMethod("CheckOnGUI", bindingFlags);
self.TextEditor_UpdateScrollOffsetIfNeeded__MethodInfo = typeof(TextEditor).GetMethod("UpdateScrollOffsetIfNeeded", bindingFlags);
self.GUIContent_Temp__MethodInfo = typeof(GUIContent).GetMethod("Temp", bindingFlags, null, new [] { typeof(string) }, null);
self.EditorGUI_s_RecycledEditor__FieldInfo = typeof(EditorGUI).GetField("s_RecycledEditor", bindingFlags);
self.EditorGUI_activeEditor__FieldInfo = typeof(EditorGUI).GetField("activeEditor", bindingFlags);
self.RecycledTextEditor_EndEditing__MethodInfo = typeof(EditorGUI).GetNestedType("RecycledTextEditor", bindingFlags).GetMethod("EndEditing", bindingFlags);
self.RecycledTextEditor_BeginEditing__MethodInfo = typeof(EditorGUI).GetNestedType("RecycledTextEditor", bindingFlags).GetMethod("BeginEditing", bindingFlags);
}
public static string TextAreaWithTabs(Rect position, string text)
{
//GUIContent gUIContent = GUIContent.Temp(text);
GUIContent gUIContent = (GUIContent)self.GUIContent_Temp__MethodInfo.Invoke(null, new object[]{ text });
self.DoTextField(position, GUIUtility.GetControlID(FocusType.Keyboard, position), gUIContent, true, -1, GUI.skin.textArea);
return gUIContent.text;
}
public static void DoTextField(Rect position, int id, GUIContent content, bool multiline, int maxLength, GUIStyle style)
{
if (maxLength >= 0 && content.text.Length > maxLength)
{
content.text = content.text.Substring(0, maxLength);
}
//GUIUtility.CheckOnGUI();
self.GUIUtility_CheckOnGUI__MethodInfo.Invoke(null, null);
TextEditor stateObject = (TextEditor)GUIUtility.GetStateObject(typeof(TextEditor), id);
stateObject.content.text = content.text;
stateObject.SaveBackup();
stateObject.position = position;
stateObject.style = style;
stateObject.multiline = multiline;
stateObject.controlID = id;
stateObject.ClampPos();
if (GUIUtility.keyboardControl == id && Event.current.type != EventType.Layout)
{
//stateObject.UpdateScrollOffsetIfNeeded();
if(self.TextEditor_UpdateScrollOffsetIfNeeded__MethodInfo != null) {
self.TextEditor_UpdateScrollOffsetIfNeeded__MethodInfo.Invoke(stateObject, null);
}
}
self.HandleTextFieldEventForDesktop(position, id, content, multiline, maxLength, style, stateObject);
}
// Keep track when the tab key was down
// So that on a KeyUp event, we can check if it corresponds correctly
// as the controlId would have moved on to the next focusable GUI item when tab is pushed
static bool wasTabDown = false;
public static void HandleTextFieldEventForDesktop(Rect position, int id, GUIContent content, bool multiline, int maxLength, GUIStyle style, TextEditor editor)
{
Event @event = Event.current;
bool flag = false;
string textAreaName = "TextArea" + id;
switch (@event.type)
{
case EventType.MouseDown:
{
if (position.Contains(@event.mousePosition))
{
GUIUtility.hotControl = id;
GUIUtility.keyboardControl = id;
//editor.m_HasFocus = true;
self.TextEditor_m_HasFocus__FieldInfo.SetValue(editor, true);
editor.MoveCursorToPosition(Event.current.mousePosition);
if (Event.current.clickCount == 2 && GUI.skin.settings.doubleClickSelectsWord)
{
editor.SelectCurrentWord();
editor.DblClickSnap(TextEditor.DblClickSnapping.WORDS);
editor.MouseDragSelectsWholeWords(true);
}
if (Event.current.clickCount == 3 && GUI.skin.settings.tripleClickSelectsLine)
{
editor.SelectCurrentParagraph();
editor.MouseDragSelectsWholeWords(true);
editor.DblClickSnap(TextEditor.DblClickSnapping.PARAGRAPHS);
}
@event.Use();
}
if (GUIUtility.keyboardControl == id)
{
//GUIUtility.textFieldInput = true;
self.GUIUtility_textFieldInput__PropertyInfo.SetValue(null, true, null);
}
if (flag)
{
GUI.changed = true;
content.text = editor.content.text;
if (maxLength >= 0 && content.text.Length > maxLength)
{
content.text = content.text.Substring(0, maxLength);
}
@event.Use();
}
return;
}
case EventType.MouseUp:
{
if (GUIUtility.hotControl == id)
{
editor.MouseDragSelectsWholeWords(false);
GUIUtility.hotControl = 0;
@event.Use();
}
if (GUIUtility.keyboardControl == id)
{
//GUIUtility.textFieldInput = true;
self.GUIUtility_textFieldInput__PropertyInfo.SetValue(null, true, null);
}
if (flag)
{
GUI.changed = true;
content.text = editor.content.text;
if (maxLength >= 0 && content.text.Length > maxLength)
{
content.text = content.text.Substring(0, maxLength);
}
@event.Use();
}
return;
}
case EventType.KeyUp:
case EventType.MouseMove:
case EventType.ScrollWheel:
{
// Keep focus on this input when tab is pushed
if (self.wasTabDown)
{
if(@event.type == EventType.KeyUp && @event.keyCode == KeyCode.Tab) {
GUIUtility.hotControl = id;
GUIUtility.keyboardControl = id;
//editor.m_HasFocus = true;
self.TextEditor_m_HasFocus__FieldInfo.SetValue(editor, true);
}
self.wasTabDown = false;
}
if (GUIUtility.keyboardControl == id)
{
//GUIUtility.textFieldInput = true;
self.GUIUtility_textFieldInput__PropertyInfo.SetValue(null, true, null);
}
if (flag)
{
GUI.changed = true;
content.text = editor.content.text;
if (maxLength >= 0 && content.text.Length > maxLength)
{
content.text = content.text.Substring(0, maxLength);
}
@event.Use();
}
return;
}
case EventType.MouseDrag:
{
if (GUIUtility.hotControl == id)
{
if (!@event.shift)
{
editor.SelectToPosition(Event.current.mousePosition);
}
else
{
editor.MoveCursorToPosition(Event.current.mousePosition);
}
@event.Use();
}
if (GUIUtility.keyboardControl == id)
{
//GUIUtility.textFieldInput = true;
self.GUIUtility_textFieldInput__PropertyInfo.SetValue(null, true, null);
}
if (flag)
{
GUI.changed = true;
content.text = editor.content.text;
if (maxLength >= 0 && content.text.Length > maxLength)
{
content.text = content.text.Substring(0, maxLength);
}
@event.Use();
}
return;
}
case EventType.KeyDown:
{
if (GUIUtility.keyboardControl != id)
{
return;
}
if (!editor.HandleKeyEvent(@event))
{
char chr = @event.character;
//if (@event.keyCode == KeyCode.Tab || @event.character == '\t') {
// editor.Insert(chr);
// flag = true;
// return;
//}
if (chr == '\n' && !multiline && !@event.alt)
{
return;
}
Font font = style.font;
if (!font)
{
font = GUI.skin.font;
}
if(chr == '\t') {
self.wasTabDown = true;
}
if (font.HasCharacter(chr) || chr == '\n' || chr == '\t')
{
// Shift tab should remove a tab if present
if(chr == '\t' && @event.shift) {
if(editor.pos > 0 && editor.content.text[editor.pos-1] == '\t') {
editor.Backspace();
}
}
else {
editor.Insert(chr);
}
flag = true;
if (GUIUtility.keyboardControl == id)
{
//GUIUtility.textFieldInput = true;
self.GUIUtility_textFieldInput__PropertyInfo.SetValue(null, true, null);
}
GUI.changed = true;
content.text = editor.content.text;
if (maxLength >= 0 && content.text.Length > maxLength)
{
content.text = content.text.Substring(0, maxLength);
}
@event.Use();
return;
}
else
{
if (chr == 0)
{
if (Input.compositionString.Length > 0)
{
editor.ReplaceSelection(string.Empty);
flag = true;
}
@event.Use();
}
if (GUIUtility.keyboardControl == id)
{
//GUIUtility.textFieldInput = true;
self.GUIUtility_textFieldInput__PropertyInfo.SetValue(null, true, null);
}
if (flag)
{
GUI.changed = true;
content.text = editor.content.text;
if (maxLength >= 0 && content.text.Length > maxLength)
{
content.text = content.text.Substring(0, maxLength);
}
@event.Use();
}
return;
}
}
else
{
@event.Use();
flag = true;
content.text = editor.content.text;
if (GUIUtility.keyboardControl == id)
{
//GUIUtility.textFieldInput = true;
self.GUIUtility_textFieldInput__PropertyInfo.SetValue(null, true, null);
}
if (flag)
{
GUI.changed = true;
content.text = editor.content.text;
if (maxLength >= 0 && content.text.Length > maxLength)
{
content.text = content.text.Substring(0, maxLength);
}
@event.Use();
}
return;
}
}
case EventType.Repaint:
{
if (GUIUtility.keyboardControl == id)
{
editor.DrawCursor(content.text);
}
else
{
style.Draw(position, content, id, false);
}
if (GUIUtility.keyboardControl == id)
{
//GUIUtility.textFieldInput = true;
self.GUIUtility_textFieldInput__PropertyInfo.SetValue(null, true, null);
}
if (flag)
{
GUI.changed = true;
content.text = editor.content.text;
if (maxLength >= 0 && content.text.Length > maxLength)
{
content.text = content.text.Substring(0, maxLength);
}
@event.Use();
}
return;
}
default:
{
switch (@event.type) {
case EventType.ValidateCommand:
{
string commandName = @event.commandName;
if (commandName != null)
{
switch (commandName)
{
case "UndoRedoPerformed":
{
editor.content.text = content.text;
@event.Use();
break;
}
case "Cut":
{
if (editor.hasSelection)
{
@event.Use();
}
break;
}
case "Copy":
{
if (editor.hasSelection)
{
@event.Use();
}
break;
}
case "Paste":
{
if (editor.CanPaste())
{
@event.Use();
}
break;
}
case "SelectAll":
{
@event.Use();
break;
}
}
}
break;
}
case EventType.ExecuteCommand:
{
if (GUIUtility.keyboardControl == id)
{
string commandName = @event.commandName;
if (commandName != null)
{
switch (commandName)
{
case "OnLostFocus":
{
var activeEditor = self.EditorGUI_activeEditor__FieldInfo.GetValue(null);
if (activeEditor != null)
{
//EditorGUI.activeEditor.EndEditing();
self.RecycledTextEditor_EndEditing__MethodInfo.Invoke(activeEditor, null);
}
@event.Use();
break;
}
case "Cut":
{
//editor.BeginEditing(id, text, position, style, multiline, passwordField);
self.RecycledTextEditor_BeginEditing__MethodInfo.Invoke(self.EditorGUI_s_RecycledEditor__FieldInfo.GetValue(null), new object[] {
id, content.text, position, style, multiline, false
});
editor.Cut();
flag = true;
break;
}
case "Copy":
{
editor.Copy();
@event.Use();
break;
}
case "Paste":
{
//editor.BeginEditing(id, text, position, style, multiline, passwordField);
self.RecycledTextEditor_BeginEditing__MethodInfo.Invoke(self.EditorGUI_s_RecycledEditor__FieldInfo.GetValue(null), new object[] {
id, content.text, position, style, multiline, false
});
editor.Paste();
flag = true;
break;
}
case "SelectAll":
{
editor.SelectAll();
@event.Use();
break;
}
}
}
}
break;
}
}
if (GUIUtility.keyboardControl == id)
{
//GUIUtility.textFieldInput = true;
self.GUIUtility_textFieldInput__PropertyInfo.SetValue(null, true, null);
}
if (flag)
{
GUI.changed = true;
content.text = editor.content.text;
if (maxLength >= 0 && content.text.Length > maxLength)
{
content.text = content.text.Substring(0, maxLength);
}
@event.Use();
}
return;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment