Created
May 20, 2015 04:23
-
-
Save MadLittleMods/e1433225d1a31f4b4d92 to your computer and use it in GitHub Desktop.
Unity GUI text area: `MLMEditorGUI.TextAreaWithTabs(Rect position, string text)`
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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