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 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 (); // 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