diff options
Diffstat (limited to 'Runtime/Export/TextEditor.cs')
-rw-r--r-- | Runtime/Export/TextEditor.cs | 1203 |
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 |