summaryrefslogtreecommitdiff
path: root/Runtime/Export/TextEditor.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Runtime/Export/TextEditor.cs')
-rw-r--r--Runtime/Export/TextEditor.cs1203
1 files changed, 1203 insertions, 0 deletions
diff --git a/Runtime/Export/TextEditor.cs b/Runtime/Export/TextEditor.cs
new file mode 100644
index 0000000..02672ac
--- /dev/null
+++ b/Runtime/Export/TextEditor.cs
@@ -0,0 +1,1203 @@
+using UnityEngine;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace UnityEngine {
+
+public class TextEditor {
+#if UNITY_IPHONE || UNITY_ANDROID || UNITY_BB10 || UNITY_WP8 || UNITY_TIZEN
+ public TouchScreenKeyboard keyboardOnScreen = null;
+#endif
+
+ public int pos = 0;
+ public int selectPos = 0;
+ public int controlID = 0;
+ public GUIContent content = new GUIContent();
+ public GUIStyle style = GUIStyle.none;
+ public Rect position;
+ public bool multiline = false;
+ public bool hasHorizontalCursorPos = false;
+ public bool isPasswordField = false;
+ internal bool m_HasFocus;
+ public Vector2 scrollOffset = Vector2.zero; // The text field can have a scroll offset in order to display its contents
+
+ // are we up/downing?
+ public Vector2 graphicalCursorPos;
+ public Vector2 graphicalSelectCursorPos;
+
+ // Clear the cursor position for vertical movement...
+ void ClearCursorPos () {hasHorizontalCursorPos = false; m_iAltCursorPos = -1;}
+
+ // selection
+ bool m_MouseDragSelectsWholeWords = false;
+ int m_DblClickInitPos = 0;
+ DblClickSnapping m_DblClickSnap = DblClickSnapping.WORDS;
+ bool m_bJustSelected = false;
+
+ int m_iAltCursorPos = -1;
+
+ public enum DblClickSnapping : byte { WORDS, PARAGRAPHS };
+
+ public void OnFocus ()
+ {
+ if (multiline)
+ pos = selectPos = 0;
+ else
+ SelectAll ();
+ m_HasFocus = true;
+ }
+
+ public void OnLostFocus ()
+ {
+ m_HasFocus = false;
+ scrollOffset = Vector2.zero;
+ }
+
+ void GrabGraphicalCursorPos () {
+ if (!hasHorizontalCursorPos) {
+ graphicalCursorPos = style.GetCursorPixelPosition (position, content, pos);
+ graphicalSelectCursorPos = style.GetCursorPixelPosition (position, content, selectPos);
+ hasHorizontalCursorPos = false;
+ }
+ }
+
+ // Handle a key event.
+ // Looks up the platform-dependent key-action table & performs the event
+ // return true if the event was recognized.
+ public bool HandleKeyEvent (Event e) {
+ InitKeyActions ();
+ EventModifiers m = e.modifiers;
+ e.modifiers &= ~EventModifiers.CapsLock;
+ if (s_Keyactions.ContainsKey(e)) {
+ TextEditOp op = (TextEditOp)s_Keyactions[e];
+ PerformOperation (op);
+ e.modifiers = m;
+ return true;
+ }
+ e.modifiers = m;
+ return false;
+ }
+
+ // Deletes previous text on the line
+ public bool DeleteLineBack()
+ {
+ if (hasSelection)
+ {
+ DeleteSelection();
+ return true;
+ }
+ int p = pos;
+ int i = p;
+ while (i-- != 0)
+ if (content.text[i] == '\n') {
+ p = i + 1;
+ break;
+ }
+ if (i == -1)
+ p = 0;
+ if (pos != p)
+ {
+ content.text = content.text.Remove (p, pos - p);
+ selectPos = pos = p;
+ return true;
+ }
+ return false;
+ }
+
+ // Deletes the previous word
+ public bool DeleteWordBack()
+ {
+ if (hasSelection)
+ {
+ DeleteSelection();
+ return true;
+ }
+
+ int prevWordEnd = FindEndOfPreviousWord(pos);
+ if(pos != prevWordEnd)
+ {
+ content.text = content.text.Remove(prevWordEnd, pos - prevWordEnd);
+ selectPos = pos = prevWordEnd;
+ return true;
+ }
+ return false;
+ }
+
+ // Deletes the following word
+ public bool DeleteWordForward()
+ {
+ if (hasSelection)
+ {
+ DeleteSelection();
+ return true;
+ }
+
+ int nextWordStart = FindStartOfNextWord (pos);
+ if (pos < content.text.Length)
+ {
+ content.text = content.text.Remove(pos, nextWordStart - pos);
+ return true;
+ }
+ return false;
+ }
+
+ // perform a right-delete
+ public bool Delete () {
+ if (hasSelection) {
+ DeleteSelection ();
+ return true;
+ } else
+ if (pos < content.text.Length) {
+ content.text = content.text.Remove (pos, 1);
+ return true;
+ }
+ return false;
+ }
+ public bool CanPaste () {
+ return GUIUtility.systemCopyBuffer.Length != 0;
+ }
+ // Perform a left-delete
+ public bool Backspace () {
+ if (hasSelection) {
+ DeleteSelection ();
+ return true;
+ } else
+ if (pos > 0) {
+ content.text = content.text.Remove (pos - 1, 1);
+ selectPos = pos = pos - 1;
+ ClearCursorPos ();
+ return true;
+ }
+ return false;
+ }
+
+ /// Select all the text
+ public void SelectAll () {
+ pos = 0; selectPos = content.text.Length;
+ ClearCursorPos ();
+ }
+
+ /// Select none of the text
+ public void SelectNone () {
+ selectPos = pos;
+ ClearCursorPos ();
+ }
+
+ /// Does this text field has a selection
+ public bool hasSelection { get { return pos != selectPos; } }
+
+ /// Returns the selected text
+ public string SelectedText {
+ get {
+ int len = content.text.Length;
+ if (pos > len)
+ pos = len;
+ if (selectPos > len)
+ selectPos = len;
+ if (pos == selectPos)
+ return "";
+ if (pos < selectPos)
+ return content.text.Substring(pos, selectPos - pos);
+ else
+ return content.text.Substring(selectPos, pos - selectPos);
+ }
+ }
+
+ /// Delete the current selection. If there is no selection, this function does not do anything...
+ public bool DeleteSelection () {
+ int len = content.text.Length;
+ if (pos > len)
+ pos = len;
+ if (selectPos > len)
+ selectPos = len;
+ if (pos == selectPos)
+ return false;
+ if (pos < selectPos) {
+ content.text = content.text.Substring (0, pos) + content.text.Substring (selectPos, content.text.Length - selectPos);
+ selectPos = pos;
+ } else {
+ content.text = content.text.Substring (0, selectPos) + content.text.Substring (pos, content.text.Length - pos);
+ pos = selectPos;
+ }
+ ClearCursorPos ();
+
+ return true;
+ }
+
+ /// Replace the selection with /replace/. If there is no selection, /replace/ is inserted at the current cursor point.
+ public void ReplaceSelection (string replace) {
+ DeleteSelection ();
+ content.text = content.text.Insert (pos, replace);
+ selectPos = pos += replace.Length;
+ ClearCursorPos ();
+ }
+
+ /// Replacted the selection with /c/
+ public void Insert (char c) {
+ ReplaceSelection (c.ToString());
+ }
+
+ /// Move selection to alt cursor /position/
+ public void MoveSelectionToAltCursor()
+ {
+ if (m_iAltCursorPos == -1)
+ return;
+ int p = m_iAltCursorPos;
+ string tmp = SelectedText;
+ content.text = content.text.Insert (p, tmp);
+
+ if (p < pos)
+ {
+ pos += tmp.Length;
+ selectPos += tmp.Length;
+ }
+
+ DeleteSelection();
+
+ selectPos = pos = p;
+ ClearCursorPos ();
+
+ }
+
+ /// Move the cursor one character to the right and deselect.
+ public void MoveRight () {
+ ClearCursorPos ();
+ if (selectPos == pos) {
+ pos++;
+ ClampPos ();
+ selectPos = pos;
+ } else {
+ if (selectPos > pos)
+ pos = selectPos;
+ else
+ selectPos = pos;
+ }
+ }
+
+ /// Move the cursor one character to the left and deselect.
+ public void MoveLeft () {
+ if (selectPos == pos) {
+ pos--;
+ if (pos < 0)
+ pos = 0;
+ selectPos = pos;
+ } else {
+ if (selectPos > pos)
+ selectPos = pos;
+ else
+ pos = selectPos;
+ }
+ ClearCursorPos ();
+ return;
+ }
+
+ /// Move the cursor up and deselects.
+ public void MoveUp () {
+ if (selectPos < pos)
+ selectPos = pos;
+ else
+ pos = selectPos;
+ GrabGraphicalCursorPos ();
+ //graphicalCursorPos = style.GetCursorPixelPosition (position, content, pos);
+ graphicalCursorPos.y -= 1;
+ pos = selectPos = style.GetCursorStringIndex (position, content, graphicalCursorPos);
+ if (pos <= 0)
+ ClearCursorPos ();
+ }
+
+ /// Move the cursor down and deselects.
+ public void MoveDown () {
+ if (selectPos > pos)
+ selectPos = pos;
+ else
+ pos = selectPos;
+ GrabGraphicalCursorPos ();
+ //graphicalCursorPos = style.GetCursorPixelPosition (position, content, pos);
+ graphicalCursorPos.y += style.lineHeight + 5;
+ pos = selectPos = style.GetCursorStringIndex (position, content, graphicalCursorPos);
+ if (pos == content.text.Length)
+ ClearCursorPos ();
+
+ }
+
+
+ /// Moves the cursor to the start of the current line.
+ public void MoveLineStart () {
+ // we start from the left-most selected character
+ int p = selectPos < pos ? selectPos : pos;
+ // then we scan back to find the first newline
+ int i = p;
+ while (i-- != 0)
+ if (content.text[i] == '\n') {
+ selectPos = pos = i + 1;
+ return;
+ }
+ selectPos = pos = 0;
+ }
+
+ /// Moves the selection to the end of the current line
+ public void MoveLineEnd () {
+ // we start from the right-most selected character
+ int p = selectPos > pos ? selectPos : pos;
+ // then we scan forward to find the first newline
+ int i = p;
+ int strlen = content.text.Length;
+ while (i < strlen) {
+ if (content.text[i] == '\n') {
+ selectPos = pos = i;
+ return;
+ }
+ i++;
+ }
+ selectPos = pos = strlen;
+ }
+ /// Move to the start of the current graphical line. This takes word-wrapping into consideration.
+ public void MoveGraphicalLineStart () {
+ pos = selectPos = GetGraphicalLineStart (pos < selectPos ? pos : selectPos);
+ }
+ /// Move to the end of the current graphical line. This takes word-wrapping into consideration.
+ public void MoveGraphicalLineEnd () {
+ pos = selectPos = GetGraphicalLineEnd (pos > selectPos ? pos : selectPos);
+ }
+
+ /// Moves the cursor to the beginning of the text
+ public void MoveTextStart () {
+ selectPos = pos = 0;
+ }
+
+ /// Moves the cursor to the end of the text
+ public void MoveTextEnd () {
+ selectPos = pos = content.text.Length;
+ }
+
+ /// Move to the next paragraph
+ public void MoveParagraphForward () {
+ pos = pos > selectPos ? pos : selectPos;
+ if (pos < content.text.Length) {
+ selectPos = pos = content.text.IndexOf ('\n', pos + 1);
+ if (pos == -1)
+ selectPos = pos = content.text.Length;
+ }
+ }
+ /// Move to the previous paragraph
+ public void MoveParagraphBackward () {
+ pos = pos < selectPos ? pos : selectPos;
+ if (pos > 1) {
+ selectPos = pos = content.text.LastIndexOf ('\n', pos - 2) + 1;
+ } else
+ selectPos = pos = 0;
+ }
+
+ //
+
+ // Move the cursor to a graphical position. Used for moving the cursor on MouseDown events.
+ public void MoveCursorToPosition (Vector2 cursorPosition) {
+ selectPos = style.GetCursorStringIndex(position, content, cursorPosition + scrollOffset);
+
+ if (!Event.current.shift)
+ {
+ pos = selectPos;
+ }
+
+ ClampPos();
+ }
+ public void MoveAltCursorToPosition (Vector2 cursorPosition) {
+ m_iAltCursorPos = style.GetCursorStringIndex (position, content, cursorPosition + scrollOffset);
+ ClampPos();
+ }
+
+ public bool IsOverSelection(Vector2 cursorPosition) {
+ int p = style.GetCursorStringIndex (position, content, cursorPosition + scrollOffset);
+ return ((p < Mathf.Max(pos, selectPos)) && (p > Mathf.Min(pos, selectPos)));
+ }
+
+ // Do a drag selection. Used to expand the selection in MouseDrag events.
+ public void SelectToPosition (Vector2 cursorPosition) {
+ if (!m_MouseDragSelectsWholeWords)
+ pos = style.GetCursorStringIndex (position, content, cursorPosition + scrollOffset);
+ else // snap to words/paragraphs
+ {
+ int p = style.GetCursorStringIndex (position, content, cursorPosition + scrollOffset);
+
+
+
+ if (m_DblClickSnap == DblClickSnapping.WORDS)
+ {
+ if (p < m_DblClickInitPos)
+ {
+ pos = FindEndOfClassification(p,-1);
+ selectPos = FindEndOfClassification(m_DblClickInitPos,+1) ;
+ }
+ else
+ {
+ if (p >= content.text.Length)
+ p = content.text.Length -1;
+ pos = FindEndOfClassification(p,+1);
+ selectPos = FindEndOfClassification(m_DblClickInitPos - 1,-1);
+ }
+ } // paragraph
+ else
+ {
+ if (p < m_DblClickInitPos)
+ {
+ if (p > 0)
+ pos = content.text.LastIndexOf('\n', p - 2) + 1;
+ else
+ pos = 0;
+
+ selectPos = content.text.LastIndexOf('\n', m_DblClickInitPos);
+ }
+ else
+ {
+ if ( p < content.text.Length)
+ {
+ pos = content.text.IndexOf('\n', p + 1) + 1;
+
+ if (pos <= 0)
+ pos = content.text.Length;
+ }
+ else
+ pos = content.text.Length;
+
+ selectPos = content.text.LastIndexOf('\n', m_DblClickInitPos - 2) + 1;
+ }
+
+ }
+ }
+ }
+
+ /// Expand the selection to the left
+ public void SelectLeft () {
+ if (m_bJustSelected)
+ if (pos > selectPos)
+ { // swap
+ int tmp = pos;
+ pos = selectPos;
+ selectPos = tmp;
+ }
+ m_bJustSelected = false;
+
+ pos--;
+ if (pos < 0)
+ pos = 0;
+ }
+
+ public void SelectRight () {
+ if (m_bJustSelected)
+ if (pos < selectPos)
+ { // swap
+ int tmp = pos;
+ pos = selectPos;
+ selectPos = tmp;
+ }
+ m_bJustSelected = false;
+
+ pos++;
+ int stringlen = content.text.Length;
+ if (pos > stringlen)
+ pos = stringlen;
+ }
+
+ public void SelectUp () {
+ GrabGraphicalCursorPos ();
+ graphicalCursorPos.y -= 1;
+ pos = style.GetCursorStringIndex (position, content, graphicalCursorPos);
+ }
+
+ public void SelectDown () {
+ GrabGraphicalCursorPos ();
+ graphicalCursorPos.y += style.lineHeight + 5;
+ pos = style.GetCursorStringIndex (position, content, graphicalCursorPos);
+ }
+
+ /// Select to the end of the text
+ public void SelectTextEnd () {
+ // This is not quite like tha mac - there, when you select to end of text, the position of the cursor becomes somewhat i'll defined
+ // Hard to explain. In textedit, try: CMD-SHIFT-down, SHIFT-LEFT for case 1. then do CMD-SHIFT-down, SHIFT-RIGHT, SHIFT-LEFT for case 2.
+ // Anyways, it's fucked so we won't do that
+ pos = content.text.Length;
+ }
+
+ /// Select to the start of the text
+ public void SelectTextStart () {
+ // Same thing as SelectTextEnd...
+ pos = 0;
+ }
+
+ /// sets whether the text selection is done by dbl click or not
+ public void MouseDragSelectsWholeWords (bool on)
+ {
+ m_MouseDragSelectsWholeWords = on;
+ m_DblClickInitPos = pos;
+ }
+
+ public void DblClickSnap(DblClickSnapping snapping)
+ {
+ m_DblClickSnap = snapping;
+ }
+
+ int GetGraphicalLineStart (int p) {
+ Vector2 point = style.GetCursorPixelPosition (position, content, p);
+ point.x =0;
+ return style.GetCursorStringIndex (position, content, point);
+ }
+
+ int GetGraphicalLineEnd (int p) {
+ Vector2 point = style.GetCursorPixelPosition (position, content, p);
+ point.x += 5000;
+ return style.GetCursorStringIndex (position, content, point);
+ }
+
+ int FindNextSeperator (int startPos) {
+ int textLen = content.text.Length;
+ while (startPos < textLen && !isLetterLikeChar (content.text[startPos]))
+ startPos++;
+ while (startPos < textLen && isLetterLikeChar (content.text[startPos]))
+ startPos++;
+ return startPos;
+ }
+
+ static bool isLetterLikeChar (char c) {
+ return System.Char.IsLetterOrDigit (c) || c == '\'';
+ }
+
+ int FindPrevSeperator (int startPos) {
+ startPos --;
+ while (startPos > 0 && !isLetterLikeChar (content.text[startPos] ))
+ startPos--;
+
+ while (startPos >= 0 && isLetterLikeChar (content.text[startPos] ))
+ startPos--;
+ return startPos + 1;
+ }
+
+ /// Move to the end of the word.
+ /// If the cursor is over some space characters, these are skipped
+ /// Then, the cursor moves to the end of the following word.
+ /// This corresponds to Alt-RightArrow on a Mac
+ public void MoveWordRight () {
+ pos = pos > selectPos ? pos : selectPos;
+ pos = selectPos = FindNextSeperator (pos);
+ ClearCursorPos ();
+ }
+
+ public void MoveToStartOfNextWord () {
+ ClearCursorPos ();
+ if (pos != selectPos) {
+ MoveRight ();
+ return;
+ }
+ pos = selectPos = FindStartOfNextWord (pos);
+ }
+
+ public void MoveToEndOfPreviousWord () {
+ ClearCursorPos ();
+ if (pos != selectPos) {
+ MoveLeft ();
+ return;
+ }
+ pos = selectPos = FindEndOfPreviousWord (pos);
+ }
+
+
+
+ public void SelectToStartOfNextWord () {
+ ClearCursorPos ();
+ pos = FindStartOfNextWord (pos);
+ }
+
+ public void SelectToEndOfPreviousWord () {
+ ClearCursorPos ();
+ pos = FindEndOfPreviousWord (pos);
+ }
+
+ enum CharacterType {
+ LetterLike,
+ Symbol, Symbol2,
+ WhiteSpace
+ }
+
+ CharacterType ClassifyChar (char c) {
+ if (System.Char.IsWhiteSpace (c))
+ return CharacterType.WhiteSpace;
+ if (System.Char.IsLetterOrDigit (c) || c == '\'')
+ return CharacterType.LetterLike;
+ return CharacterType.Symbol;
+ }
+
+ /// Move to start of next word.
+ /// This corresponds to Ctrl-RightArrow on Windows
+ /// If the cursor is over a whitespace, it's moved forwards ''till the first non-whitespace character
+ /// If the cursor is over an alphanumeric character, it''s moved forward 'till it encounters space or a punctuation mark.
+ /// If the stopping character is a space, this is skipped as well.
+ /// If the cursor is over an punctuation mark, it's moved forward ''till it a letter or a space of a punctuation mark. If the stopping character is a space, this is skipped as well
+ public int FindStartOfNextWord (int p) {
+ int textLen = content.text.Length;
+ if (p == textLen)
+ return p;
+
+ // Find out which char type we're at...
+ char c = content.text[p];
+ CharacterType t = ClassifyChar (c);
+ if (t != CharacterType.WhiteSpace) {
+ p++;
+ while (p < textLen && ClassifyChar (content.text[p]) == t)
+ p++;
+ } else {
+ if ( c == '\t' || c == '\n')
+ return p + 1;
+ }
+
+ if (p == textLen)
+ return p;
+
+ // Skip spaces
+ c = content.text[p];
+ if (c == ' ') { // If we're at a space, skip over any number of spaces
+ while (p < textLen && System.Char.IsWhiteSpace(content.text[p]))
+ p++;
+ } else if (c == '\t' || c == '\n') { // If we're at a tab or a newline, just step one char ahead
+ return p;
+ }
+ return p;
+ }
+
+ int FindEndOfPreviousWord (int p) {
+ if (p == 0)
+ return p;
+ p--;
+
+ // Skip spaces
+ while (p > 0 && content.text[p] == ' ')
+ p--;
+
+ CharacterType t = ClassifyChar (content.text[p]);
+ if (t != CharacterType.WhiteSpace) {
+ while (p > 0 && ClassifyChar (content.text[p - 1]) == t)
+ p--;
+ }
+ return p;
+ }
+
+ public void MoveWordLeft () {
+ pos = pos < selectPos ? pos : selectPos;
+ pos = FindPrevSeperator (pos);
+ selectPos = pos;
+ }
+
+ public void SelectWordRight () {
+ ClearCursorPos ();
+ int cachedPos = selectPos;
+ if (pos < selectPos) {
+ selectPos = pos;
+ MoveWordRight ();
+ selectPos = cachedPos;
+ pos = pos < selectPos ? pos : selectPos;
+ return;
+ }
+ selectPos = pos;
+ MoveWordRight ();
+ selectPos = cachedPos;
+ }
+
+ public void SelectWordLeft () {
+ ClearCursorPos ();
+ int cachedPos = selectPos;
+ if (pos > selectPos) {
+ selectPos = pos;
+ MoveWordLeft ();
+ selectPos = cachedPos;
+ pos = pos > selectPos ? pos : selectPos;
+ return;
+ }
+ selectPos = pos;
+ MoveWordLeft ();
+ selectPos = cachedPos;
+ }
+
+ /// Expand the selection to the start of the line
+ /// Used on a mac for CMD-SHIFT-LEFT
+ public void ExpandSelectGraphicalLineStart () {
+ ClearCursorPos ();
+ if (pos < selectPos)
+ pos = GetGraphicalLineStart (pos);
+ else {
+ int temp = pos;
+ pos = GetGraphicalLineStart (selectPos);
+ selectPos = temp;
+ }
+ }
+
+ /// Expand the selection to the end of the line
+ /// Used on a mac for CMD-SHIFT-RIGHT
+ public void ExpandSelectGraphicalLineEnd () {
+ ClearCursorPos ();
+ if (pos > selectPos)
+ pos = GetGraphicalLineEnd (pos);
+ else {
+ int temp = pos;
+ pos = GetGraphicalLineEnd (selectPos);
+ selectPos = temp;
+ }
+ }
+
+ /// Move the selection point to the start of the line
+ /// Used on a Windows for SHIFT-Home
+ public void SelectGraphicalLineStart () {
+ ClearCursorPos ();
+ pos = GetGraphicalLineStart (pos);
+ }
+
+ /// Expand the selection to the end of the line
+ /// Used on a mac for SHIFT-End
+ public void SelectGraphicalLineEnd () {
+ ClearCursorPos ();
+ pos = GetGraphicalLineEnd (pos);
+
+ }
+
+ public void SelectParagraphForward () {
+ ClearCursorPos ();
+ bool wasBehind = pos < selectPos;
+ if (pos < content.text.Length) {
+ pos = content.text.IndexOf ('\n', pos + 1);
+ if (pos == -1)
+ pos = content.text.Length;
+ if (wasBehind && pos > selectPos)
+ pos = selectPos;
+ }
+ }
+
+ public void SelectParagraphBackward () {
+ ClearCursorPos ();
+ bool wasInFront = pos > selectPos;
+ if (pos > 1) {
+ pos = content.text.LastIndexOf ('\n', pos - 2) + 1;
+ if (wasInFront && pos < selectPos)
+ pos = selectPos;
+ } else
+ selectPos = pos = 0;
+ }
+
+ /// Select the word under the cursor
+ public void SelectCurrentWord () {
+ ClearCursorPos ();
+
+ int textLen = content.text.Length;
+ selectPos = pos;
+
+ // Handle that the text box can be empty
+ if (textLen == 0)
+ return;
+
+ if (pos >= textLen)
+ pos = textLen - 1;
+ if (selectPos >= textLen)
+ selectPos--;
+
+ if (pos < selectPos) {
+ pos = FindEndOfClassification (pos, -1);
+ selectPos = FindEndOfClassification (selectPos, +1);
+ } else {
+ pos = FindEndOfClassification (pos, +1);
+ selectPos = FindEndOfClassification (selectPos, -1);
+ }
+
+ m_bJustSelected = true;
+ }
+
+ int FindEndOfClassification (int p, int dir) {
+ int textLen = content.text.Length;
+
+ if ( p >= textLen || p < 0 )
+ return p;
+
+ CharacterType t = ClassifyChar(content.text[p]);
+ do {
+ p += dir;
+ if (p < 0)
+ return 0;
+ if (p >= textLen)
+ return textLen;
+ } while (ClassifyChar(content.text[p]) == t);
+ if (dir == 1)
+ return p;
+ return p + 1;
+ }
+
+ // Select the entire paragraph the cursor is on (separated by \n)
+ public void SelectCurrentParagraph () {
+ ClearCursorPos ();
+ int textLen = content.text.Length;
+
+ if (pos < textLen) {
+ pos = content.text.IndexOf ('\n', pos);
+ if (pos == -1)
+ pos = content.text.Length;
+ else
+ pos++;
+ }
+ if (selectPos != 0)
+ selectPos = content.text.LastIndexOf ('\n', selectPos - 1) + 1;
+ }
+
+ // TODO: get the height from the font
+
+ public void DrawCursor (string text) {
+ string realText = content.text;
+ int cursorPos = pos;
+ if (Input.compositionString.Length > 0)
+ {
+ content.text = text.Substring (0, pos) + Input.compositionString + text.Substring (selectPos);
+ cursorPos += Input.compositionString.Length;
+ }
+ else
+ content.text = text;
+
+ graphicalCursorPos = style.GetCursorPixelPosition (new Rect (0,0,position.width,position.height), content, cursorPos);
+
+ //Debug.Log("Cursor pos: " + graphicalCursorPos);
+
+ Rect r = style.padding.Remove (position);
+
+ Vector2 contentSize = new Vector2(style.CalcSize(content).x, style.CalcHeight(content, position.width));
+
+ // If there is plenty of room, simply show entire string
+ if (contentSize.x < position.width)
+ {
+ scrollOffset.x = 0;
+ }
+ else
+ {
+ //go right
+ if (graphicalCursorPos.x + 1 > scrollOffset.x + r.width)
+ // do we want html or apple behavior? this is html behavior
+ scrollOffset.x = graphicalCursorPos.x - r.width;
+ //go left
+ if (graphicalCursorPos.x < scrollOffset.x + style.padding.left)
+ scrollOffset.x = graphicalCursorPos.x - style.padding.left;
+ }
+ // ... and height/y as well
+ // If there is plenty of room, simply show entire string
+ // Debug.Log(contentSize.y + " < R : " + r);
+ if (contentSize.y < r.height)
+ {
+ scrollOffset.y = 0;
+ }
+ else
+ {
+ //go down
+ if (graphicalCursorPos.y + style.lineHeight > scrollOffset.y + r.height + style.padding.top)
+ scrollOffset.y = graphicalCursorPos.y - r.height - style.padding.top + style.lineHeight;
+ //go up
+ if (graphicalCursorPos.y < scrollOffset.y + style.padding.top )
+ scrollOffset.y = graphicalCursorPos.y - style.padding.top;
+ }
+
+ // This case takes many words to explain:
+ // 1. Text field has more text than it can fit vertically, and the cursor is at the very bottom (text field is scrolled down)
+ // 2. user e.g. deletes some lines of text at the bottom (backspace or select+delete)
+ // 3. now suddenly we have space at the bottom of text field, that is now not filled with any content
+ // 4. scroll text field up to fill in that space (this is what other text editors do)
+ if (scrollOffset.y > 0 && contentSize.y - scrollOffset.y < r.height)
+ scrollOffset.y = contentSize.y - r.height - style.padding.top - style.padding.bottom;
+
+ scrollOffset.y = scrollOffset.y<0?0:scrollOffset.y;
+
+ Vector2 originalContentOffset = style.contentOffset;
+ style.contentOffset -= scrollOffset;
+ style.Internal_clipOffset = scrollOffset;
+
+ // Debug.Log ("ScrollOffset : " + scrollOffset);
+
+ Input.compositionCursorPos = graphicalCursorPos + new Vector2(position.x, position.y + style.lineHeight) - scrollOffset;
+
+ if(Input.compositionString.Length > 0)
+ style.DrawWithTextSelection (position, content, controlID, pos, pos + Input.compositionString.Length, true);
+ else
+ style.DrawWithTextSelection (position, content, controlID, pos, selectPos);
+
+ if (m_iAltCursorPos != -1)
+ style.DrawCursor(position, content, controlID, m_iAltCursorPos);
+
+ // reset
+ style.contentOffset = originalContentOffset;
+ style.Internal_clipOffset = Vector2.zero;
+
+ content.text = realText;
+ }
+
+
+ bool PerformOperation (TextEditOp operation) {
+
+ switch (operation) {
+// NOTE the TODOs below:
+ case TextEditOp.MoveLeft: MoveLeft (); break;
+ case TextEditOp.MoveRight: MoveRight (); break;
+ case TextEditOp.MoveUp: MoveUp (); break;
+ case TextEditOp.MoveDown: MoveDown (); break;
+ case TextEditOp.MoveLineStart: MoveLineStart (); break;
+ case TextEditOp.MoveLineEnd: MoveLineEnd (); break;
+ case TextEditOp.MoveWordRight: MoveWordRight (); break;
+ case TextEditOp.MoveToStartOfNextWord: MoveToStartOfNextWord (); break;
+ case TextEditOp.MoveToEndOfPreviousWord: MoveToEndOfPreviousWord (); break;
+ case TextEditOp.MoveWordLeft: MoveWordLeft (); break;
+ case TextEditOp.MoveTextStart: MoveTextStart (); break;
+ case TextEditOp.MoveTextEnd: MoveTextEnd (); break;
+ case TextEditOp.MoveParagraphForward: MoveParagraphForward (); break;
+ case TextEditOp.MoveParagraphBackward: MoveParagraphBackward (); break;
+// case TextEditOp.MovePageUp: return MovePageUp (); break;
+// case TextEditOp.MovePageDown: return MovePageDown (); break;
+ case TextEditOp.MoveGraphicalLineStart: MoveGraphicalLineStart (); break;
+ case TextEditOp.MoveGraphicalLineEnd: MoveGraphicalLineEnd (); break;
+ case TextEditOp.SelectLeft: SelectLeft (); break;
+ case TextEditOp.SelectRight: SelectRight (); break;
+ case TextEditOp.SelectUp: SelectUp (); break;
+ case TextEditOp.SelectDown: SelectDown (); break;
+ case TextEditOp.SelectWordRight: SelectWordRight (); break;
+ case TextEditOp.SelectWordLeft: SelectWordLeft (); break;
+ case TextEditOp.SelectToEndOfPreviousWord: SelectToEndOfPreviousWord (); break;
+ case TextEditOp.SelectToStartOfNextWord: SelectToStartOfNextWord (); break;
+
+ case TextEditOp.SelectTextStart: SelectTextStart (); break;
+ case TextEditOp.SelectTextEnd: SelectTextEnd (); break;
+ case TextEditOp.ExpandSelectGraphicalLineStart: ExpandSelectGraphicalLineStart (); break;
+ case TextEditOp.ExpandSelectGraphicalLineEnd: ExpandSelectGraphicalLineEnd (); break;
+ case TextEditOp.SelectParagraphForward: SelectParagraphForward (); break;
+ case TextEditOp.SelectParagraphBackward: SelectParagraphBackward (); break;
+ case TextEditOp.SelectGraphicalLineStart: SelectGraphicalLineStart (); break;
+ case TextEditOp.SelectGraphicalLineEnd: SelectGraphicalLineEnd (); break;
+// case TextEditOp.SelectPageUp: return SelectPageUp (); break;
+// case TextEditOp.SelectPageDown: return SelectPageDown (); break;
+ case TextEditOp.Delete: return Delete ();
+ case TextEditOp.Backspace: return Backspace ();
+ case TextEditOp.Cut: return Cut ();
+ case TextEditOp.Copy: Copy (); break;
+ case TextEditOp.Paste: return Paste ();
+ case TextEditOp.SelectAll: SelectAll (); break;
+ case TextEditOp.SelectNone: SelectNone (); break;
+// case TextEditOp.ScrollStart: return ScrollStart (); break;
+// case TextEditOp.ScrollEnd: return ScrollEnd (); break;
+// case TextEditOp.ScrollPageUp: return ScrollPageUp (); break;
+// case TextEditOp.ScrollPageDown: return ScrollPageDown (); break;
+ case TextEditOp.DeleteWordBack: return DeleteWordBack(); // break; // The uncoditional return makes the "break;" issue a warning about unreachable code
+ case TextEditOp.DeleteLineBack: return DeleteLineBack();
+ case TextEditOp.DeleteWordForward: return DeleteWordForward(); // break; // The uncoditional return makes the "break;" issue a warning about unreachable code
+ default:
+ Debug.Log ("Unimplemented: " + operation);
+ break;
+ }
+ return false;
+ }
+
+ enum TextEditOp {
+ MoveLeft, MoveRight, MoveUp, MoveDown, MoveLineStart, MoveLineEnd, MoveTextStart, MoveTextEnd, MovePageUp, MovePageDown,
+ MoveGraphicalLineStart, MoveGraphicalLineEnd, MoveWordLeft, MoveWordRight,
+ MoveParagraphForward, MoveParagraphBackward, MoveToStartOfNextWord, MoveToEndOfPreviousWord,
+ SelectLeft, SelectRight, SelectUp, SelectDown, SelectTextStart, SelectTextEnd, SelectPageUp, SelectPageDown,
+ ExpandSelectGraphicalLineStart, ExpandSelectGraphicalLineEnd, SelectGraphicalLineStart, SelectGraphicalLineEnd,
+ SelectWordLeft, SelectWordRight, SelectToEndOfPreviousWord, SelectToStartOfNextWord,
+ SelectParagraphBackward, SelectParagraphForward,
+ Delete, Backspace, DeleteWordBack, DeleteWordForward, DeleteLineBack,
+ Cut, Copy, Paste, SelectAll, SelectNone,
+ ScrollStart, ScrollEnd, ScrollPageUp, ScrollPageDown
+ };
+
+ string oldText;
+ int oldPos, oldSelectPos;
+
+ public void SaveBackup () {
+ oldText = content.text;
+ oldPos = pos;
+ oldSelectPos = selectPos;
+ }
+
+ public void Undo () {
+ content.text = oldText;
+ pos = oldPos;
+ selectPos = oldSelectPos;
+ }
+
+ public bool Cut () {
+ //Debug.Log ("Cut");
+ if (isPasswordField)
+ return false;
+ Copy ();
+ return DeleteSelection ();
+ }
+
+ public void Copy () {
+ //Debug.Log ("Copy");
+ if (selectPos == pos)
+ return;
+
+ if (isPasswordField)
+ return;
+
+ string copyStr;
+ if (pos < selectPos)
+ copyStr = content.text.Substring (pos, selectPos - pos);
+ else
+ copyStr = content.text.Substring (selectPos, pos - selectPos);
+
+ GUIUtility.systemCopyBuffer = copyStr;
+ }
+
+ public bool Paste () {
+ //Debug.Log ("Paste");
+ string pasteval = GUIUtility.systemCopyBuffer;
+ if (pasteval != "") {
+ ReplaceSelection (pasteval);
+ return true;
+ }
+ return false;
+ }
+
+ static void MapKey (string key, TextEditOp action) {
+ s_Keyactions [Event.KeyboardEvent (key)] = action;
+ }
+ static Dictionary<Event, TextEditOp> s_Keyactions;
+ /// Set up a platform independant keyboard->Edit action map. This varies depending on whether we are on mac or windows.
+ void InitKeyActions () {
+ if (s_Keyactions != null)
+ return;
+ s_Keyactions = new Dictionary<Event, TextEditOp> ();
+
+ // key mappings shared by the platforms
+ MapKey ("left", TextEditOp.MoveLeft);
+ MapKey ("right", TextEditOp.MoveRight);
+ MapKey ("up", TextEditOp.MoveUp);
+ MapKey ("down", TextEditOp.MoveDown);
+
+ MapKey ("#left", TextEditOp.SelectLeft);
+ MapKey ("#right", TextEditOp.SelectRight);
+ MapKey ("#up", TextEditOp.SelectUp);
+ MapKey ("#down", TextEditOp.SelectDown);
+
+ MapKey ("delete", TextEditOp.Delete);
+ MapKey ("backspace", TextEditOp.Backspace);
+ MapKey ("#backspace", TextEditOp.Backspace);
+
+ // OSX is the special case for input shortcuts
+ if (Application.platform == RuntimePlatform.OSXPlayer ||
+ Application.platform == RuntimePlatform.OSXWebPlayer ||
+ Application.platform == RuntimePlatform.OSXDashboardPlayer ||
+ Application.platform == RuntimePlatform.OSXEditor) {
+ // Keyboard mappings for mac
+// TODO MapKey ("home", TextEditOp.ScrollStart);
+// TODO MapKey ("end", TextEditOp.ScrollEnd);
+// TODO MapKey ("page up", TextEditOp.ScrollPageUp);
+// TODO MapKey ("page down", TextEditOp.ScrollPageDown);
+
+ MapKey ("^left", TextEditOp.MoveGraphicalLineStart);
+ MapKey ("^right", TextEditOp.MoveGraphicalLineEnd);
+// TODO MapKey ("^up", TextEditOp.ScrollPageUp);
+// TODO MapKey ("^down", TextEditOp.ScrollPageDown);
+
+ MapKey ("&left", TextEditOp.MoveWordLeft);
+ MapKey ("&right", TextEditOp.MoveWordRight);
+ MapKey ("&up", TextEditOp.MoveParagraphBackward);
+ MapKey ("&down", TextEditOp.MoveParagraphForward);
+
+ MapKey ("%left", TextEditOp.MoveGraphicalLineStart);
+ MapKey ("%right", TextEditOp.MoveGraphicalLineEnd);
+ MapKey ("%up", TextEditOp.MoveTextStart);
+ MapKey ("%down", TextEditOp.MoveTextEnd);
+
+ MapKey ("#home", TextEditOp.SelectTextStart);
+ MapKey ("#end", TextEditOp.SelectTextEnd);
+// TODO MapKey ("#page up", TextEditOp.SelectPageUp);
+// TODO MapKey ("#page down", TextEditOp.SelectPageDown);
+
+ MapKey ("#^left", TextEditOp.ExpandSelectGraphicalLineStart);
+ MapKey ("#^right", TextEditOp.ExpandSelectGraphicalLineEnd);
+ MapKey ("#^up", TextEditOp.SelectParagraphBackward);
+ MapKey ("#^down", TextEditOp.SelectParagraphForward);
+
+ MapKey ("#&left", TextEditOp.SelectWordLeft);
+ MapKey ("#&right", TextEditOp.SelectWordRight);
+ MapKey ("#&up", TextEditOp.SelectParagraphBackward);
+ MapKey ("#&down", TextEditOp.SelectParagraphForward);
+
+ MapKey ("#%left", TextEditOp.ExpandSelectGraphicalLineStart);
+ MapKey ("#%right", TextEditOp.ExpandSelectGraphicalLineEnd);
+ MapKey ("#%up", TextEditOp.SelectTextStart);
+ MapKey ("#%down", TextEditOp.SelectTextEnd);
+
+ MapKey ("%a", TextEditOp.SelectAll);
+ MapKey ("%x", TextEditOp.Cut);
+ MapKey ("%c", TextEditOp.Copy);
+ MapKey ("%v", TextEditOp.Paste);
+
+ // emacs-like keybindings
+ MapKey ("^d", TextEditOp.Delete);
+ MapKey ("^h", TextEditOp.Backspace);
+ MapKey ("^b", TextEditOp.MoveLeft);
+ MapKey ("^f", TextEditOp.MoveRight);
+ MapKey ("^a", TextEditOp.MoveLineStart);
+ MapKey ("^e", TextEditOp.MoveLineEnd);
+ // Can't be bothered to do these
+ // MapKey ("^o", TextEditOp.InsertNewlineRight);
+ // MapKey ("^t", TextEditOp.TransposeCharacters);
+
+ MapKey("&delete", TextEditOp.DeleteWordForward);
+ MapKey("&backspace", TextEditOp.DeleteWordBack);
+ MapKey ("%backspace", TextEditOp.DeleteLineBack);
+ } else {
+ // Windows/Linux keymappings
+ MapKey("home", TextEditOp.MoveGraphicalLineStart);
+ MapKey ("end", TextEditOp.MoveGraphicalLineEnd);
+// TODO MapKey ("page up", TextEditOp.MovePageUp);
+// TODO MapKey ("page down", TextEditOp.MovePageDown);
+
+ MapKey ("%left", TextEditOp.MoveWordLeft);
+ MapKey ("%right", TextEditOp.MoveWordRight);
+ MapKey ("%up", TextEditOp.MoveParagraphBackward);
+ MapKey ("%down", TextEditOp.MoveParagraphForward);
+
+ MapKey ("^left", TextEditOp.MoveToEndOfPreviousWord);
+ MapKey ("^right", TextEditOp.MoveToStartOfNextWord);
+ MapKey ("^up", TextEditOp.MoveParagraphBackward);
+ MapKey ("^down", TextEditOp.MoveParagraphForward);
+
+ MapKey ("#^left", TextEditOp.SelectToEndOfPreviousWord);
+ MapKey ("#^right", TextEditOp.SelectToStartOfNextWord);
+ MapKey ("#^up", TextEditOp.SelectParagraphBackward);
+ MapKey ("#^down", TextEditOp.SelectParagraphForward);
+
+ MapKey ("#home", TextEditOp.SelectGraphicalLineStart);
+ MapKey ("#end", TextEditOp.SelectGraphicalLineEnd);
+// TODO MapKey ("#page up", TextEditOp.SelectPageUp);
+// TODO MapKey ("#page down", TextEditOp.SelectPageDown);
+
+ MapKey("^delete", TextEditOp.DeleteWordForward);
+ MapKey("^backspace", TextEditOp.DeleteWordBack);
+ MapKey ("%backspace", TextEditOp.DeleteLineBack);
+
+ MapKey ("^a", TextEditOp.SelectAll);
+ MapKey ("^x", TextEditOp.Cut);
+ MapKey ("^c", TextEditOp.Copy);
+ MapKey ("^v", TextEditOp.Paste);
+ MapKey("#delete", TextEditOp.Cut);
+ MapKey("^insert", TextEditOp.Copy);
+ MapKey("#insert", TextEditOp.Paste);
+ }
+ }
+
+ // clamp cursor & selection to the string length
+ public void ClampPos () {
+ if (m_HasFocus == true && controlID != GUIUtility.keyboardControl)
+ OnLostFocus ();
+ if (m_HasFocus == false && controlID == GUIUtility.keyboardControl)
+ OnFocus ();
+
+ if (pos < 0) pos = 0;
+ else if (pos > content.text.Length) pos = content.text.Length;
+ if (selectPos < 0) selectPos = 0;
+ else if (selectPos > content.text.Length) selectPos = content.text.Length;
+ if (m_iAltCursorPos > content.text.Length) m_iAltCursorPos = content.text.Length;
+
+ }
+}
+
+} // namespace