mirror of
https://github.com/GrahamKracker/UnityExplorer.git
synced 2025-07-01 19:13:03 +08:00
1557 lines
59 KiB
C#
1557 lines
59 KiB
C#
#if CPP
|
|
// Unity C# reference source
|
|
// Copyright (c) Unity Technologies. For terms of use, see
|
|
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
namespace Explorer.Unstrip.IMGUI
|
|
{
|
|
public class TextEditorUnstrip
|
|
{
|
|
public TextEditorUnstrip() { }
|
|
// public Internal_TextEditor(IntPtr ptr) : base(ptr) { }
|
|
|
|
public TouchScreenKeyboard keyboardOnScreen = null;
|
|
public int controlID = 0;
|
|
public GUIStyle style = GUIStyle.none;
|
|
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
|
|
|
|
public GUIContent m_Content = new GUIContent();
|
|
private Rect m_Position;
|
|
private int m_CursorIndex = 0;
|
|
private int m_SelectIndex = 0;
|
|
private bool m_RevealCursor = false;
|
|
|
|
public string text
|
|
{
|
|
get { return m_Content.text; }
|
|
set
|
|
{
|
|
m_Content.text = value ?? string.Empty;
|
|
EnsureValidCodePointIndex(ref m_CursorIndex);
|
|
EnsureValidCodePointIndex(ref m_SelectIndex);
|
|
}
|
|
}
|
|
|
|
public Rect position
|
|
{
|
|
get { return m_Position; }
|
|
set
|
|
{
|
|
if (m_Position == value)
|
|
return;
|
|
|
|
m_Position = value;
|
|
|
|
UpdateScrollOffset();
|
|
}
|
|
}
|
|
|
|
internal virtual Rect localPosition
|
|
{
|
|
get { return position; }
|
|
}
|
|
|
|
public int cursorIndex
|
|
{
|
|
get { return m_CursorIndex; }
|
|
set
|
|
{
|
|
int oldCursorIndex = m_CursorIndex;
|
|
m_CursorIndex = value;
|
|
EnsureValidCodePointIndex(ref m_CursorIndex);
|
|
|
|
if (m_CursorIndex != oldCursorIndex)
|
|
{
|
|
m_RevealCursor = true;
|
|
OnCursorIndexChange();
|
|
}
|
|
}
|
|
}
|
|
|
|
public int selectIndex
|
|
{
|
|
get { return m_SelectIndex; }
|
|
set
|
|
{
|
|
int oldSelectIndex = m_SelectIndex;
|
|
m_SelectIndex = value;
|
|
EnsureValidCodePointIndex(ref m_SelectIndex);
|
|
|
|
if (m_SelectIndex != oldSelectIndex)
|
|
OnSelectIndexChange();
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
|
|
public DblClickSnapping doubleClickSnapping { get; set; } = DblClickSnapping.WORDS;
|
|
bool m_bJustSelected = false;
|
|
|
|
int m_iAltCursorPos = -1;
|
|
public int altCursorPosition { get { return m_iAltCursorPos; } set { m_iAltCursorPos = value; } }
|
|
|
|
public enum DblClickSnapping : byte { WORDS, PARAGRAPHS };
|
|
|
|
public void OnFocus()
|
|
{
|
|
if (multiline)
|
|
cursorIndex = selectIndex = 0;
|
|
else
|
|
SelectAll();
|
|
m_HasFocus = true;
|
|
}
|
|
|
|
public void OnLostFocus()
|
|
{
|
|
m_HasFocus = false;
|
|
scrollOffset = Vector2.zero;
|
|
}
|
|
|
|
void GrabGraphicalCursorPos()
|
|
{
|
|
if (!hasHorizontalCursorPos)
|
|
{
|
|
graphicalCursorPos = style.GetCursorPixelPosition(localPosition, m_Content, cursorIndex);
|
|
graphicalSelectCursorPos = style.GetCursorPixelPosition(localPosition, m_Content, selectIndex);
|
|
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;
|
|
var key = e.ToString();
|
|
if (s_Keyactions.ContainsKey(key))
|
|
{
|
|
TextEditOp op = (TextEditOp)s_Keyactions[key];
|
|
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 = cursorIndex;
|
|
int i = p;
|
|
while (i-- != 0)
|
|
if (text[i] == '\n')
|
|
{
|
|
p = i + 1;
|
|
break;
|
|
}
|
|
if (i == -1)
|
|
p = 0;
|
|
if (cursorIndex != p)
|
|
{
|
|
m_Content.text = text.Remove(p, cursorIndex - p);
|
|
selectIndex = cursorIndex = p;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Deletes the previous word
|
|
public bool DeleteWordBack()
|
|
{
|
|
if (hasSelection)
|
|
{
|
|
DeleteSelection();
|
|
return true;
|
|
}
|
|
|
|
int prevWordEnd = FindEndOfPreviousWord(cursorIndex);
|
|
if (cursorIndex != prevWordEnd)
|
|
{
|
|
m_Content.text = text.Remove(prevWordEnd, cursorIndex - prevWordEnd);
|
|
selectIndex = cursorIndex = prevWordEnd;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Deletes the following word
|
|
public bool DeleteWordForward()
|
|
{
|
|
if (hasSelection)
|
|
{
|
|
DeleteSelection();
|
|
return true;
|
|
}
|
|
|
|
int nextWordStart = FindStartOfNextWord(cursorIndex);
|
|
if (cursorIndex < text.Length)
|
|
{
|
|
m_Content.text = text.Remove(cursorIndex, nextWordStart - cursorIndex);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// perform a right-delete
|
|
public bool Delete()
|
|
{
|
|
if (hasSelection)
|
|
{
|
|
DeleteSelection();
|
|
return true;
|
|
}
|
|
else if (cursorIndex < text.Length)
|
|
{
|
|
m_Content.text = text.Remove(cursorIndex, NextCodePointIndex(cursorIndex) - cursorIndex);
|
|
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 (cursorIndex > 0)
|
|
{
|
|
var startIndex = PreviousCodePointIndex(cursorIndex);
|
|
m_Content.text = text.Remove(startIndex, cursorIndex - startIndex);
|
|
selectIndex = cursorIndex = startIndex;
|
|
ClearCursorPos();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// Select all the text
|
|
public void SelectAll()
|
|
{
|
|
cursorIndex = 0; selectIndex = text.Length;
|
|
ClearCursorPos();
|
|
}
|
|
|
|
/// Select none of the text
|
|
public void SelectNone()
|
|
{
|
|
selectIndex = cursorIndex;
|
|
ClearCursorPos();
|
|
}
|
|
|
|
/// Does this text field has a selection
|
|
public bool hasSelection { get { return cursorIndex != selectIndex; } }
|
|
|
|
/// Returns the selected text
|
|
public string SelectedText
|
|
{
|
|
get
|
|
{
|
|
if (cursorIndex == selectIndex)
|
|
return "";
|
|
if (cursorIndex < selectIndex)
|
|
return text.Substring(cursorIndex, selectIndex - cursorIndex);
|
|
else
|
|
return text.Substring(selectIndex, cursorIndex - selectIndex);
|
|
}
|
|
}
|
|
|
|
/// Delete the current selection. If there is no selection, this function does not do anything...
|
|
public bool DeleteSelection()
|
|
{
|
|
if (cursorIndex == selectIndex)
|
|
return false;
|
|
if (cursorIndex < selectIndex)
|
|
{
|
|
m_Content.text = text.Substring(0, cursorIndex) + text.Substring(selectIndex, text.Length - selectIndex);
|
|
selectIndex = cursorIndex;
|
|
}
|
|
else
|
|
{
|
|
m_Content.text = text.Substring(0, selectIndex) + text.Substring(cursorIndex, text.Length - cursorIndex);
|
|
cursorIndex = selectIndex;
|
|
}
|
|
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();
|
|
m_Content.text = text.Insert(cursorIndex, replace);
|
|
selectIndex = cursorIndex += 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;
|
|
m_Content.text = text.Insert(p, tmp);
|
|
|
|
if (p < cursorIndex)
|
|
{
|
|
cursorIndex += tmp.Length;
|
|
selectIndex += tmp.Length;
|
|
}
|
|
|
|
DeleteSelection();
|
|
|
|
selectIndex = cursorIndex = p;
|
|
ClearCursorPos();
|
|
}
|
|
|
|
/// Move the cursor one character to the right and deselect.
|
|
public void MoveRight()
|
|
{
|
|
ClearCursorPos();
|
|
if (selectIndex == cursorIndex)
|
|
{
|
|
cursorIndex = NextCodePointIndex(cursorIndex);
|
|
DetectFocusChange(); // TODO: Is this necessary?
|
|
selectIndex = cursorIndex;
|
|
}
|
|
else
|
|
{
|
|
if (selectIndex > cursorIndex)
|
|
cursorIndex = selectIndex;
|
|
else
|
|
selectIndex = cursorIndex;
|
|
}
|
|
}
|
|
|
|
/// Move the cursor one character to the left and deselect.
|
|
public void MoveLeft()
|
|
{
|
|
if (selectIndex == cursorIndex)
|
|
{
|
|
cursorIndex = PreviousCodePointIndex(cursorIndex);
|
|
selectIndex = cursorIndex;
|
|
}
|
|
else
|
|
{
|
|
if (selectIndex > cursorIndex)
|
|
selectIndex = cursorIndex;
|
|
else
|
|
cursorIndex = selectIndex;
|
|
}
|
|
ClearCursorPos();
|
|
}
|
|
|
|
/// Move the cursor up and deselects.
|
|
public void MoveUp()
|
|
{
|
|
if (selectIndex < cursorIndex)
|
|
selectIndex = cursorIndex;
|
|
else
|
|
cursorIndex = selectIndex;
|
|
GrabGraphicalCursorPos();
|
|
graphicalCursorPos.y -= 1;
|
|
cursorIndex = selectIndex = style.GetCursorStringIndex(localPosition, m_Content, graphicalCursorPos);
|
|
if (cursorIndex <= 0)
|
|
ClearCursorPos();
|
|
}
|
|
|
|
/// Move the cursor down and deselects.
|
|
public void MoveDown()
|
|
{
|
|
if (selectIndex > cursorIndex)
|
|
selectIndex = cursorIndex;
|
|
else
|
|
cursorIndex = selectIndex;
|
|
GrabGraphicalCursorPos();
|
|
graphicalCursorPos.y += style.lineHeight + 5;
|
|
cursorIndex = selectIndex = style.GetCursorStringIndex(localPosition, m_Content, graphicalCursorPos);
|
|
if (cursorIndex == 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 = selectIndex < cursorIndex ? selectIndex : cursorIndex;
|
|
// then we scan back to find the first newline
|
|
int i = p;
|
|
while (i-- != 0)
|
|
if (text[i] == '\n')
|
|
{
|
|
selectIndex = cursorIndex = i + 1;
|
|
return;
|
|
}
|
|
selectIndex = cursorIndex = 0;
|
|
}
|
|
|
|
/// Moves the selection to the end of the current line
|
|
public void MoveLineEnd()
|
|
{
|
|
// we start from the right-most selected character
|
|
int p = selectIndex > cursorIndex ? selectIndex : cursorIndex;
|
|
// then we scan forward to find the first newline
|
|
int i = p;
|
|
int strlen = text.Length;
|
|
while (i < strlen)
|
|
{
|
|
if (text[i] == '\n')
|
|
{
|
|
selectIndex = cursorIndex = i;
|
|
return;
|
|
}
|
|
i++;
|
|
}
|
|
selectIndex = cursorIndex = strlen;
|
|
}
|
|
|
|
/// Move to the start of the current graphical line. This takes word-wrapping into consideration.
|
|
public void MoveGraphicalLineStart()
|
|
{
|
|
cursorIndex = selectIndex = GetGraphicalLineStart(cursorIndex < selectIndex ? cursorIndex : selectIndex);
|
|
}
|
|
|
|
/// Move to the end of the current graphical line. This takes word-wrapping into consideration.
|
|
public void MoveGraphicalLineEnd()
|
|
{
|
|
cursorIndex = selectIndex = GetGraphicalLineEnd(cursorIndex > selectIndex ? cursorIndex : selectIndex);
|
|
}
|
|
|
|
/// Moves the cursor to the beginning of the text
|
|
public void MoveTextStart()
|
|
{
|
|
selectIndex = cursorIndex = 0;
|
|
}
|
|
|
|
/// Moves the cursor to the end of the text
|
|
public void MoveTextEnd()
|
|
{
|
|
selectIndex = cursorIndex = text.Length;
|
|
}
|
|
|
|
private int IndexOfEndOfLine(int startIndex)
|
|
{
|
|
int index = text.IndexOf('\n', startIndex);
|
|
return (index != -1 ? index : text.Length);
|
|
}
|
|
|
|
/// Move to the next paragraph
|
|
public void MoveParagraphForward()
|
|
{
|
|
cursorIndex = cursorIndex > selectIndex ? cursorIndex : selectIndex;
|
|
if (cursorIndex < text.Length)
|
|
{
|
|
selectIndex = cursorIndex = IndexOfEndOfLine(cursorIndex + 1);
|
|
}
|
|
}
|
|
|
|
/// Move to the previous paragraph
|
|
public void MoveParagraphBackward()
|
|
{
|
|
cursorIndex = cursorIndex < selectIndex ? cursorIndex : selectIndex;
|
|
if (cursorIndex > 1)
|
|
{
|
|
selectIndex = cursorIndex = text.LastIndexOf('\n', cursorIndex - 2) + 1;
|
|
}
|
|
else
|
|
selectIndex = cursorIndex = 0;
|
|
}
|
|
|
|
//
|
|
|
|
// Move the cursor to a graphical position. Used for moving the cursor on MouseDown events.
|
|
public void MoveCursorToPosition(Vector2 cursorPosition)
|
|
{
|
|
MoveCursorToPosition_Internal(cursorPosition, Event.current.shift);
|
|
}
|
|
|
|
// Move the cursor to a graphical position. Used for moving the cursor on MouseDown events.
|
|
protected internal void MoveCursorToPosition_Internal(Vector2 cursorPosition, bool shift)
|
|
{
|
|
selectIndex = style.GetCursorStringIndex(localPosition, m_Content, cursorPosition + scrollOffset);
|
|
|
|
if (!shift)
|
|
{
|
|
cursorIndex = selectIndex;
|
|
}
|
|
|
|
DetectFocusChange(); // TODO: Is this necessary?
|
|
}
|
|
|
|
public void MoveAltCursorToPosition(Vector2 cursorPosition)
|
|
{
|
|
int index = style.GetCursorStringIndex(localPosition, m_Content, cursorPosition + scrollOffset);
|
|
m_iAltCursorPos = Mathf.Min(text.Length, index);
|
|
DetectFocusChange(); // TODO: Is this necessary?
|
|
}
|
|
|
|
public bool IsOverSelection(Vector2 cursorPosition)
|
|
{
|
|
int p = style.GetCursorStringIndex(localPosition, m_Content, cursorPosition + scrollOffset);
|
|
return ((p < Mathf.Max(cursorIndex, selectIndex)) && (p > Mathf.Min(cursorIndex, selectIndex)));
|
|
}
|
|
|
|
// Do a drag selection. Used to expand the selection in MouseDrag events.
|
|
public void SelectToPosition(Vector2 cursorPosition)
|
|
{
|
|
if (!m_MouseDragSelectsWholeWords)
|
|
cursorIndex = style.GetCursorStringIndex(localPosition, m_Content, cursorPosition + scrollOffset);
|
|
else // snap to words/paragraphs
|
|
{
|
|
int p = style.GetCursorStringIndex(localPosition, m_Content, cursorPosition + scrollOffset);
|
|
|
|
EnsureValidCodePointIndex(ref p);
|
|
EnsureValidCodePointIndex(ref m_DblClickInitPos);
|
|
|
|
if (doubleClickSnapping == DblClickSnapping.WORDS)
|
|
{
|
|
if (p < m_DblClickInitPos)
|
|
{
|
|
cursorIndex = FindEndOfClassification(p, Direction.Backward);
|
|
selectIndex = FindEndOfClassification(m_DblClickInitPos, Direction.Forward);
|
|
}
|
|
else
|
|
{
|
|
cursorIndex = FindEndOfClassification(p, Direction.Forward);
|
|
selectIndex = FindEndOfClassification(m_DblClickInitPos, Direction.Backward);
|
|
}
|
|
} // paragraph
|
|
else
|
|
{
|
|
if (p < m_DblClickInitPos)
|
|
{
|
|
if (p > 0)
|
|
cursorIndex = text.LastIndexOf('\n', Mathf.Max(0, p - 2)) + 1;
|
|
else
|
|
cursorIndex = 0;
|
|
|
|
selectIndex = text.LastIndexOf('\n', m_DblClickInitPos);
|
|
}
|
|
else
|
|
{
|
|
if (p < text.Length)
|
|
{
|
|
cursorIndex = IndexOfEndOfLine(p);
|
|
}
|
|
else
|
|
cursorIndex = text.Length;
|
|
|
|
selectIndex = text.LastIndexOf('\n', Mathf.Max(0, m_DblClickInitPos - 2)) + 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Expand the selection to the left
|
|
public void SelectLeft()
|
|
{
|
|
if (m_bJustSelected)
|
|
if (cursorIndex > selectIndex)
|
|
{ // swap
|
|
int tmp = cursorIndex;
|
|
cursorIndex = selectIndex;
|
|
selectIndex = tmp;
|
|
}
|
|
m_bJustSelected = false;
|
|
|
|
cursorIndex = PreviousCodePointIndex(cursorIndex);
|
|
}
|
|
|
|
public void SelectRight()
|
|
{
|
|
if (m_bJustSelected)
|
|
if (cursorIndex < selectIndex)
|
|
{ // swap
|
|
int tmp = cursorIndex;
|
|
cursorIndex = selectIndex;
|
|
selectIndex = tmp;
|
|
}
|
|
m_bJustSelected = false;
|
|
|
|
cursorIndex = NextCodePointIndex(cursorIndex);
|
|
}
|
|
|
|
public void SelectUp()
|
|
{
|
|
GrabGraphicalCursorPos();
|
|
graphicalCursorPos.y -= 1;
|
|
cursorIndex = style.GetCursorStringIndex(localPosition, m_Content, graphicalCursorPos);
|
|
}
|
|
|
|
public void SelectDown()
|
|
{
|
|
GrabGraphicalCursorPos();
|
|
graphicalCursorPos.y += style.lineHeight + 5;
|
|
cursorIndex = style.GetCursorStringIndex(localPosition, m_Content, graphicalCursorPos);
|
|
}
|
|
|
|
/// Select to the end of the text
|
|
public void SelectTextEnd()
|
|
{
|
|
// This is not quite like the 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 wrong so we won't do that
|
|
cursorIndex = text.Length;
|
|
}
|
|
|
|
/// Select to the start of the text
|
|
public void SelectTextStart()
|
|
{
|
|
// Same thing as SelectTextEnd...
|
|
cursorIndex = 0;
|
|
}
|
|
|
|
/// sets whether the text selection is done by dbl click or not
|
|
public void MouseDragSelectsWholeWords(bool on)
|
|
{
|
|
m_MouseDragSelectsWholeWords = on;
|
|
m_DblClickInitPos = cursorIndex;
|
|
}
|
|
|
|
public void DblClickSnap(DblClickSnapping snapping)
|
|
{
|
|
doubleClickSnapping = snapping;
|
|
}
|
|
|
|
int GetGraphicalLineStart(int p)
|
|
{
|
|
Vector2 point = style.GetCursorPixelPosition(localPosition, m_Content, p);
|
|
point.x = 0;
|
|
return style.GetCursorStringIndex(localPosition, m_Content, point);
|
|
}
|
|
|
|
int GetGraphicalLineEnd(int p)
|
|
{
|
|
Vector2 point = style.GetCursorPixelPosition(localPosition, m_Content, p);
|
|
point.x += 5000;
|
|
return style.GetCursorStringIndex(localPosition, m_Content, point);
|
|
}
|
|
|
|
int FindNextSeperator(int startPos)
|
|
{
|
|
int textLen = text.Length;
|
|
while (startPos < textLen && ClassifyChar(startPos) != CharacterType.LetterLike)
|
|
startPos = NextCodePointIndex(startPos);
|
|
while (startPos < textLen && ClassifyChar(startPos) == CharacterType.LetterLike)
|
|
startPos = NextCodePointIndex(startPos);
|
|
return startPos;
|
|
}
|
|
|
|
int FindPrevSeperator(int startPos)
|
|
{
|
|
startPos = PreviousCodePointIndex(startPos);
|
|
while (startPos > 0 && ClassifyChar(startPos) != CharacterType.LetterLike)
|
|
startPos = PreviousCodePointIndex(startPos);
|
|
|
|
if (startPos == 0)
|
|
return 0;
|
|
|
|
while (startPos > 0 && ClassifyChar(startPos) == CharacterType.LetterLike)
|
|
startPos = PreviousCodePointIndex(startPos);
|
|
|
|
if (ClassifyChar(startPos) == CharacterType.LetterLike)
|
|
return startPos;
|
|
return NextCodePointIndex(startPos);
|
|
}
|
|
|
|
/// 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()
|
|
{
|
|
cursorIndex = cursorIndex > selectIndex ? cursorIndex : selectIndex;
|
|
cursorIndex = selectIndex = FindNextSeperator(cursorIndex);
|
|
ClearCursorPos();
|
|
}
|
|
|
|
public void MoveToStartOfNextWord()
|
|
{
|
|
ClearCursorPos();
|
|
if (cursorIndex != selectIndex)
|
|
{
|
|
MoveRight();
|
|
return;
|
|
}
|
|
cursorIndex = selectIndex = FindStartOfNextWord(cursorIndex);
|
|
}
|
|
|
|
public void MoveToEndOfPreviousWord()
|
|
{
|
|
ClearCursorPos();
|
|
if (cursorIndex != selectIndex)
|
|
{
|
|
MoveLeft();
|
|
return;
|
|
}
|
|
cursorIndex = selectIndex = FindEndOfPreviousWord(cursorIndex);
|
|
}
|
|
|
|
public void SelectToStartOfNextWord()
|
|
{
|
|
ClearCursorPos();
|
|
cursorIndex = FindStartOfNextWord(cursorIndex);
|
|
}
|
|
|
|
public void SelectToEndOfPreviousWord()
|
|
{
|
|
ClearCursorPos();
|
|
cursorIndex = FindEndOfPreviousWord(cursorIndex);
|
|
}
|
|
|
|
enum CharacterType
|
|
{
|
|
LetterLike,
|
|
Symbol, Symbol2,
|
|
WhiteSpace
|
|
}
|
|
|
|
CharacterType ClassifyChar(int index)
|
|
{
|
|
if (char.IsWhiteSpace(text, index))
|
|
return CharacterType.WhiteSpace;
|
|
if (char.IsLetterOrDigit(text, index) || text[index] == '\'')
|
|
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 = text.Length;
|
|
if (p == textLen)
|
|
return p;
|
|
|
|
// Find out which char type we're at...
|
|
CharacterType t = ClassifyChar(p);
|
|
if (t != CharacterType.WhiteSpace)
|
|
{
|
|
p = NextCodePointIndex(p);
|
|
while (p < textLen && ClassifyChar(p) == t)
|
|
p = NextCodePointIndex(p);
|
|
}
|
|
else
|
|
{
|
|
if (text[p] == '\t' || text[p] == '\n')
|
|
return NextCodePointIndex(p);
|
|
}
|
|
|
|
if (p == textLen)
|
|
return p;
|
|
|
|
// Skip spaces
|
|
if (text[p] == ' ') // If we're at a space, skip over any number of spaces
|
|
{
|
|
while (p < textLen && ClassifyChar(p) == CharacterType.WhiteSpace)
|
|
p = NextCodePointIndex(p);
|
|
}
|
|
else if (text[p] == '\t' || text[p] == '\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 = PreviousCodePointIndex(p);
|
|
|
|
// Skip spaces
|
|
while (p > 0 && text[p] == ' ')
|
|
p = PreviousCodePointIndex(p);
|
|
|
|
CharacterType t = ClassifyChar(p);
|
|
if (t != CharacterType.WhiteSpace)
|
|
{
|
|
while (p > 0 && ClassifyChar(PreviousCodePointIndex(p)) == t)
|
|
p = PreviousCodePointIndex(p);
|
|
}
|
|
return p;
|
|
}
|
|
|
|
public void MoveWordLeft()
|
|
{
|
|
cursorIndex = cursorIndex < selectIndex ? cursorIndex : selectIndex;
|
|
cursorIndex = FindPrevSeperator(cursorIndex);
|
|
selectIndex = cursorIndex;
|
|
}
|
|
|
|
public void SelectWordRight()
|
|
{
|
|
ClearCursorPos();
|
|
int cachedPos = selectIndex;
|
|
if (cursorIndex < selectIndex)
|
|
{
|
|
selectIndex = cursorIndex;
|
|
MoveWordRight();
|
|
selectIndex = cachedPos;
|
|
cursorIndex = cursorIndex < selectIndex ? cursorIndex : selectIndex;
|
|
return;
|
|
}
|
|
selectIndex = cursorIndex;
|
|
MoveWordRight();
|
|
selectIndex = cachedPos;
|
|
}
|
|
|
|
public void SelectWordLeft()
|
|
{
|
|
ClearCursorPos();
|
|
int cachedPos = selectIndex;
|
|
if (cursorIndex > selectIndex)
|
|
{
|
|
selectIndex = cursorIndex;
|
|
MoveWordLeft();
|
|
selectIndex = cachedPos;
|
|
cursorIndex = cursorIndex > selectIndex ? cursorIndex : selectIndex;
|
|
return;
|
|
}
|
|
selectIndex = cursorIndex;
|
|
MoveWordLeft();
|
|
selectIndex = cachedPos;
|
|
}
|
|
|
|
/// Expand the selection to the start of the line
|
|
/// Used on a mac for CMD-SHIFT-LEFT
|
|
public void ExpandSelectGraphicalLineStart()
|
|
{
|
|
ClearCursorPos();
|
|
if (cursorIndex < selectIndex)
|
|
cursorIndex = GetGraphicalLineStart(cursorIndex);
|
|
else
|
|
{
|
|
int temp = cursorIndex;
|
|
cursorIndex = GetGraphicalLineStart(selectIndex);
|
|
selectIndex = temp;
|
|
}
|
|
}
|
|
|
|
/// Expand the selection to the end of the line
|
|
/// Used on a mac for CMD-SHIFT-RIGHT
|
|
public void ExpandSelectGraphicalLineEnd()
|
|
{
|
|
ClearCursorPos();
|
|
if (cursorIndex > selectIndex)
|
|
cursorIndex = GetGraphicalLineEnd(cursorIndex);
|
|
else
|
|
{
|
|
int temp = cursorIndex;
|
|
cursorIndex = GetGraphicalLineEnd(selectIndex);
|
|
selectIndex = temp;
|
|
}
|
|
}
|
|
|
|
/// Move the selection point to the start of the line
|
|
/// Used on a Windows for SHIFT-Home
|
|
public void SelectGraphicalLineStart()
|
|
{
|
|
ClearCursorPos();
|
|
cursorIndex = GetGraphicalLineStart(cursorIndex);
|
|
}
|
|
|
|
/// Expand the selection to the end of the line
|
|
/// Used on a mac for SHIFT-End
|
|
public void SelectGraphicalLineEnd()
|
|
{
|
|
ClearCursorPos();
|
|
cursorIndex = GetGraphicalLineEnd(cursorIndex);
|
|
}
|
|
|
|
public void SelectParagraphForward()
|
|
{
|
|
ClearCursorPos();
|
|
bool wasBehind = cursorIndex < selectIndex;
|
|
if (cursorIndex < text.Length)
|
|
{
|
|
cursorIndex = IndexOfEndOfLine(cursorIndex + 1);
|
|
if (wasBehind && cursorIndex > selectIndex)
|
|
cursorIndex = selectIndex;
|
|
}
|
|
}
|
|
|
|
public void SelectParagraphBackward()
|
|
{
|
|
ClearCursorPos();
|
|
bool wasInFront = cursorIndex > selectIndex;
|
|
if (cursorIndex > 1)
|
|
{
|
|
cursorIndex = text.LastIndexOf('\n', cursorIndex - 2) + 1;
|
|
if (wasInFront && cursorIndex < selectIndex)
|
|
cursorIndex = selectIndex;
|
|
}
|
|
else
|
|
selectIndex = cursorIndex = 0;
|
|
}
|
|
|
|
/// Select the word under the cursor
|
|
public void SelectCurrentWord()
|
|
{
|
|
var index = cursorIndex;
|
|
if (cursorIndex < selectIndex)
|
|
{
|
|
cursorIndex = FindEndOfClassification(index, Direction.Backward);
|
|
selectIndex = FindEndOfClassification(index, Direction.Forward);
|
|
}
|
|
else
|
|
{
|
|
cursorIndex = FindEndOfClassification(index, Direction.Forward);
|
|
selectIndex = FindEndOfClassification(index, Direction.Backward);
|
|
}
|
|
|
|
ClearCursorPos();
|
|
m_bJustSelected = true;
|
|
}
|
|
|
|
enum Direction
|
|
{
|
|
Forward,
|
|
Backward,
|
|
}
|
|
|
|
int FindEndOfClassification(int p, Direction dir)
|
|
{
|
|
if (text.Length == 0)
|
|
return 0;
|
|
|
|
if (p == text.Length)
|
|
p = PreviousCodePointIndex(p);
|
|
|
|
var t = ClassifyChar(p);
|
|
do
|
|
{
|
|
switch (dir)
|
|
{
|
|
case Direction.Backward:
|
|
p = PreviousCodePointIndex(p);
|
|
if (p == 0)
|
|
return ClassifyChar(0) == t ? 0 : NextCodePointIndex(0);
|
|
break;
|
|
|
|
case Direction.Forward:
|
|
p = NextCodePointIndex(p);
|
|
if (p == text.Length)
|
|
return text.Length;
|
|
break;
|
|
}
|
|
}
|
|
while (ClassifyChar(p) == t);
|
|
if (dir == Direction.Forward)
|
|
return p;
|
|
return NextCodePointIndex(p);
|
|
}
|
|
|
|
// Select the entire paragraph the cursor is on (separated by \n)
|
|
public void SelectCurrentParagraph()
|
|
{
|
|
ClearCursorPos();
|
|
int textLen = text.Length;
|
|
|
|
if (cursorIndex < textLen)
|
|
{
|
|
cursorIndex = IndexOfEndOfLine(cursorIndex) + 1;
|
|
}
|
|
if (selectIndex != 0)
|
|
selectIndex = text.LastIndexOf('\n', selectIndex - 1) + 1;
|
|
}
|
|
|
|
public void UpdateScrollOffsetIfNeeded(Event evt)
|
|
{
|
|
if (evt.type != EventType.Repaint && evt.type != EventType.Layout)
|
|
{
|
|
UpdateScrollOffset();
|
|
}
|
|
}
|
|
|
|
internal void UpdateScrollOffset()
|
|
{
|
|
int cursorPos = cursorIndex;
|
|
graphicalCursorPos = style.GetCursorPixelPosition(new Rect(0, 0, position.width, position.height), m_Content, cursorPos);
|
|
|
|
Rect r = style.padding.Remove(position);
|
|
|
|
Vector2 contentSize = new Vector2(style.CalcSize(m_Content).x, style.CalcHeight(m_Content, position.width));
|
|
|
|
// If there is plenty of room, simply show entire string
|
|
if (contentSize.x < position.width)
|
|
{
|
|
scrollOffset.x = 0;
|
|
}
|
|
else if (m_RevealCursor)
|
|
{
|
|
//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
|
|
if (contentSize.y < r.height)
|
|
{
|
|
scrollOffset.y = 0;
|
|
}
|
|
else if (m_RevealCursor)
|
|
{
|
|
//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 + style.padding.top + style.padding.bottom)
|
|
scrollOffset.y = contentSize.y - r.height - style.padding.top - style.padding.bottom;
|
|
|
|
scrollOffset.y = scrollOffset.y < 0 ? 0 : scrollOffset.y;
|
|
|
|
m_RevealCursor = false;
|
|
}
|
|
|
|
// TODO: get the height from the font
|
|
|
|
public void DrawCursor(string newText)
|
|
{
|
|
string realText = text;
|
|
int cursorPos = cursorIndex;
|
|
if (InputManager.compositionString.Length > 0)
|
|
{
|
|
m_Content.text = newText.Substring(0, cursorIndex) + InputManager.compositionString + newText.Substring(selectIndex);
|
|
cursorPos += InputManager.compositionString.Length;
|
|
}
|
|
else
|
|
m_Content.text = newText;
|
|
|
|
graphicalCursorPos = style.GetCursorPixelPosition(new Rect(0, 0, position.width, position.height), m_Content, cursorPos);
|
|
|
|
//Debug.Log("Cursor pos: " + graphicalCursorPos);
|
|
|
|
Vector2 originalContentOffset = style.contentOffset;
|
|
style.contentOffset -= scrollOffset;
|
|
style.Internal_clipOffset = scrollOffset;
|
|
|
|
// Debug.Log ("ScrollOffset : " + scrollOffset);
|
|
|
|
InputManager.compositionCursorPos = graphicalCursorPos + new Vector2(position.x, position.y + style.lineHeight) - scrollOffset;
|
|
|
|
if (InputManager.compositionString.Length > 0)
|
|
style.DrawWithTextSelection(position, m_Content, controlID, cursorIndex, cursorIndex + InputManager.compositionString.Length, true);
|
|
else
|
|
style.DrawWithTextSelection(position, m_Content, controlID, cursorIndex, selectIndex);
|
|
|
|
if (m_iAltCursorPos != -1)
|
|
style.DrawCursor(position, m_Content, controlID, m_iAltCursorPos);
|
|
|
|
// reset
|
|
style.contentOffset = originalContentOffset;
|
|
style.Internal_clipOffset = Vector2.zero;
|
|
|
|
m_Content.text = realText;
|
|
}
|
|
|
|
bool PerformOperation(TextEditOp operation)
|
|
{
|
|
m_RevealCursor = true;
|
|
|
|
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 = text;
|
|
oldPos = cursorIndex;
|
|
oldSelectPos = selectIndex;
|
|
}
|
|
|
|
public void Undo()
|
|
{
|
|
m_Content.text = oldText;
|
|
cursorIndex = oldPos;
|
|
selectIndex = oldSelectPos;
|
|
}
|
|
|
|
public bool Cut()
|
|
{
|
|
//Debug.Log ("Cut");
|
|
if (isPasswordField)
|
|
return false;
|
|
Copy();
|
|
return DeleteSelection();
|
|
}
|
|
|
|
public void Copy()
|
|
{
|
|
//Debug.Log ("Copy");
|
|
if (selectIndex == cursorIndex)
|
|
return;
|
|
|
|
if (isPasswordField)
|
|
return;
|
|
|
|
string copyStr;
|
|
if (cursorIndex < selectIndex)
|
|
copyStr = text.Substring(cursorIndex, selectIndex - cursorIndex);
|
|
else
|
|
copyStr = text.Substring(selectIndex, cursorIndex - selectIndex);
|
|
|
|
GUIUtility.systemCopyBuffer = copyStr;
|
|
}
|
|
|
|
static string ReplaceNewlinesWithSpaces(string value)
|
|
{
|
|
// First get rid of Windows style new lines and then *nix so we don't leave '\r' around.
|
|
value = value.Replace("\r\n", " ");
|
|
value = value.Replace('\n', ' ');
|
|
// This probably won't happen, but just in case...
|
|
value = value.Replace('\r', ' ');
|
|
return value;
|
|
}
|
|
|
|
public bool Paste()
|
|
{
|
|
//Debug.Log ("Paste");
|
|
string pasteval = GUIUtility.systemCopyBuffer;
|
|
if (pasteval != "")
|
|
{
|
|
if (!multiline)
|
|
pasteval = ReplaceNewlinesWithSpaces(pasteval);
|
|
ReplaceSelection(pasteval);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void MapKey(string key, TextEditOp action)
|
|
{
|
|
s_Keyactions[KeyboardEvent(key).ToString()] = action;
|
|
}
|
|
|
|
private static Event KeyboardEvent(string key)
|
|
{
|
|
Event evt = new Event(0) { type = EventType.KeyDown };
|
|
if (string.IsNullOrEmpty(key))
|
|
return evt;
|
|
int startIdx = 0;
|
|
bool found = false;
|
|
do
|
|
{
|
|
found = true;
|
|
if (startIdx >= key.Length)
|
|
{
|
|
found = false; break;
|
|
}
|
|
switch (key[startIdx])
|
|
{
|
|
case '&': // Alt
|
|
evt.modifiers |= EventModifiers.Alt; startIdx++;
|
|
break;
|
|
case '^': // Ctrl
|
|
evt.modifiers |= EventModifiers.Control; startIdx++;
|
|
break;
|
|
case '%':
|
|
evt.modifiers |= EventModifiers.Command; startIdx++;
|
|
break;
|
|
case '#':
|
|
evt.modifiers |= EventModifiers.Shift; startIdx++;
|
|
break;
|
|
default:
|
|
found = false;
|
|
break;
|
|
}
|
|
}
|
|
while (found);
|
|
string subStr = key.Substring(startIdx, key.Length - startIdx).ToLowerInvariant();
|
|
switch (subStr)
|
|
{
|
|
case "[0]": evt.character = '0'; evt.keyCode = KeyCode.Keypad0; break;
|
|
case "[1]": evt.character = '1'; evt.keyCode = KeyCode.Keypad1; break;
|
|
case "[2]": evt.character = '2'; evt.keyCode = KeyCode.Keypad2; break;
|
|
case "[3]": evt.character = '3'; evt.keyCode = KeyCode.Keypad3; break;
|
|
case "[4]": evt.character = '4'; evt.keyCode = KeyCode.Keypad4; break;
|
|
case "[5]": evt.character = '5'; evt.keyCode = KeyCode.Keypad5; break;
|
|
case "[6]": evt.character = '6'; evt.keyCode = KeyCode.Keypad6; break;
|
|
case "[7]": evt.character = '7'; evt.keyCode = KeyCode.Keypad7; break;
|
|
case "[8]": evt.character = '8'; evt.keyCode = KeyCode.Keypad8; break;
|
|
case "[9]": evt.character = '9'; evt.keyCode = KeyCode.Keypad9; break;
|
|
case "[.]": evt.character = '.'; evt.keyCode = KeyCode.KeypadPeriod; break;
|
|
case "[/]": evt.character = '/'; evt.keyCode = KeyCode.KeypadDivide; break;
|
|
case "[-]": evt.character = '-'; evt.keyCode = KeyCode.KeypadMinus; break;
|
|
case "[+]": evt.character = '+'; evt.keyCode = KeyCode.KeypadPlus; break;
|
|
case "[=]": evt.character = '='; evt.keyCode = KeyCode.KeypadEquals; break;
|
|
case "[equals]": evt.character = '='; evt.keyCode = KeyCode.KeypadEquals; break;
|
|
case "[enter]": evt.character = '\n'; evt.keyCode = KeyCode.KeypadEnter; break;
|
|
case "up": evt.keyCode = KeyCode.UpArrow; evt.modifiers |= EventModifiers.FunctionKey; break;
|
|
case "down": evt.keyCode = KeyCode.DownArrow; evt.modifiers |= EventModifiers.FunctionKey; break;
|
|
case "left": evt.keyCode = KeyCode.LeftArrow; evt.modifiers |= EventModifiers.FunctionKey; break;
|
|
case "right": evt.keyCode = KeyCode.RightArrow; evt.modifiers |= EventModifiers.FunctionKey; break;
|
|
case "insert": evt.keyCode = KeyCode.Insert; evt.modifiers |= EventModifiers.FunctionKey; break;
|
|
case "home": evt.keyCode = KeyCode.Home; evt.modifiers |= EventModifiers.FunctionKey; break;
|
|
case "end": evt.keyCode = KeyCode.End; evt.modifiers |= EventModifiers.FunctionKey; break;
|
|
case "pgup": evt.keyCode = KeyCode.PageDown; evt.modifiers |= EventModifiers.FunctionKey; break;
|
|
case "page up": evt.keyCode = KeyCode.PageUp; evt.modifiers |= EventModifiers.FunctionKey; break;
|
|
case "pgdown": evt.keyCode = KeyCode.PageUp; evt.modifiers |= EventModifiers.FunctionKey; break;
|
|
case "page down": evt.keyCode = KeyCode.PageDown; evt.modifiers |= EventModifiers.FunctionKey; break;
|
|
case "backspace": evt.keyCode = KeyCode.Backspace; evt.modifiers |= EventModifiers.FunctionKey; break;
|
|
case "delete": evt.keyCode = KeyCode.Delete; evt.modifiers |= EventModifiers.FunctionKey; break;
|
|
case "tab": evt.keyCode = KeyCode.Tab; break;
|
|
case "f1": evt.keyCode = KeyCode.F1; evt.modifiers |= EventModifiers.FunctionKey; break;
|
|
case "f2": evt.keyCode = KeyCode.F2; evt.modifiers |= EventModifiers.FunctionKey; break;
|
|
case "f3": evt.keyCode = KeyCode.F3; evt.modifiers |= EventModifiers.FunctionKey; break;
|
|
case "f4": evt.keyCode = KeyCode.F4; evt.modifiers |= EventModifiers.FunctionKey; break;
|
|
case "f5": evt.keyCode = KeyCode.F5; evt.modifiers |= EventModifiers.FunctionKey; break;
|
|
case "f6": evt.keyCode = KeyCode.F6; evt.modifiers |= EventModifiers.FunctionKey; break;
|
|
case "f7": evt.keyCode = KeyCode.F7; evt.modifiers |= EventModifiers.FunctionKey; break;
|
|
case "f8": evt.keyCode = KeyCode.F8; evt.modifiers |= EventModifiers.FunctionKey; break;
|
|
case "f9": evt.keyCode = KeyCode.F9; evt.modifiers |= EventModifiers.FunctionKey; break;
|
|
case "f10": evt.keyCode = KeyCode.F10; evt.modifiers |= EventModifiers.FunctionKey; break;
|
|
case "f11": evt.keyCode = KeyCode.F11; evt.modifiers |= EventModifiers.FunctionKey; break;
|
|
case "f12": evt.keyCode = KeyCode.F12; evt.modifiers |= EventModifiers.FunctionKey; break;
|
|
case "f13": evt.keyCode = KeyCode.F13; evt.modifiers |= EventModifiers.FunctionKey; break;
|
|
case "f14": evt.keyCode = KeyCode.F14; evt.modifiers |= EventModifiers.FunctionKey; break;
|
|
case "f15": evt.keyCode = KeyCode.F15; evt.modifiers |= EventModifiers.FunctionKey; break;
|
|
case "[esc]": evt.keyCode = KeyCode.Escape; break;
|
|
case "return": evt.character = '\n'; evt.keyCode = KeyCode.Return; evt.modifiers &= ~EventModifiers.FunctionKey; break;
|
|
case "space": evt.keyCode = KeyCode.Space; evt.character = ' '; evt.modifiers &= ~EventModifiers.FunctionKey; break;
|
|
default:
|
|
if (subStr.Length != 1)
|
|
{
|
|
try
|
|
{
|
|
evt.keyCode = (KeyCode)Enum.Parse(typeof(KeyCode), subStr, true);
|
|
}
|
|
catch (ArgumentException)
|
|
{
|
|
ExplorerCore.LogError(string.Format("Unable to find key name that matches '{0}'", subStr));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
evt.character = subStr.ToLower()[0];
|
|
evt.keyCode = (KeyCode)evt.character;
|
|
if (evt.modifiers != 0)
|
|
evt.character = (char)0;
|
|
}
|
|
break;
|
|
}
|
|
return evt;
|
|
}
|
|
|
|
static Dictionary<string, 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<string, 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 (SystemInfo.operatingSystemFamily == OperatingSystemFamily.MacOSX)
|
|
{
|
|
// 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);
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
public void DetectFocusChange()
|
|
{
|
|
OnDetectFocusChange();
|
|
}
|
|
|
|
internal virtual void OnDetectFocusChange()
|
|
{
|
|
if (m_HasFocus == true && controlID != GUIUtility.keyboardControl)
|
|
OnLostFocus();
|
|
if (m_HasFocus == false && controlID == GUIUtility.keyboardControl)
|
|
OnFocus();
|
|
}
|
|
|
|
internal virtual void OnCursorIndexChange()
|
|
{
|
|
}
|
|
|
|
internal virtual void OnSelectIndexChange()
|
|
{
|
|
}
|
|
|
|
private void ClampTextIndex(ref int index)
|
|
{
|
|
index = Mathf.Clamp(index, 0, text.Length);
|
|
}
|
|
|
|
void EnsureValidCodePointIndex(ref int index)
|
|
{
|
|
ClampTextIndex(ref index);
|
|
if (!IsValidCodePointIndex(index))
|
|
index = NextCodePointIndex(index);
|
|
}
|
|
|
|
bool IsValidCodePointIndex(int index)
|
|
{
|
|
if (index < 0 || index > text.Length)
|
|
return false;
|
|
if (index == 0 || index == text.Length)
|
|
return true;
|
|
return !char.IsLowSurrogate(text[index]);
|
|
}
|
|
|
|
int PreviousCodePointIndex(int index)
|
|
{
|
|
if (index > 0)
|
|
index--;
|
|
while (index > 0 && char.IsLowSurrogate(text[index]))
|
|
index--;
|
|
return index;
|
|
}
|
|
|
|
int NextCodePointIndex(int index)
|
|
{
|
|
if (index < text.Length)
|
|
index++;
|
|
while (index < text.Length && char.IsLowSurrogate(text[index]))
|
|
index++;
|
|
return index;
|
|
}
|
|
}
|
|
} // namespace
|
|
|
|
#endif |