| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547 |
- //
- // Urho3D Engine
- // Copyright (c) 2008-2011 Lasse Öörni
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to deal
- // in the Software without restriction, including without limitation the rights
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- // copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- // THE SOFTWARE.
- //
- #include "Precompiled.h"
- #include "Input.h"
- #include "LineEdit.h"
- #include "Text.h"
- #include "UIEvents.h"
- #include "DebugNew.h"
- LineEdit::LineEdit(const std::string& name, const std::string& text) :
- BorderImage(name),
- mLastFont(0),
- mLastFontSize(0),
- mCursorPosition(0),
- mDragStartPosition(M_MAX_UNSIGNED),
- mCursorBlinkRate(1.0f),
- mCursorBlinkTimer(0.0f),
- mMaxLength(0),
- mEchoCharacter(0),
- mCursorMovable(true),
- mTextSelectable(true),
- mTextCopyable(true)
- {
- mClipChildren = true;
- mEnabled = true;
- mFocusMode = FM_FOCUSABLE_DEFOCUSABLE;
- mText = new Text();
- mCursor = new BorderImage();
- addChild(mText);
- addChild(mCursor);
-
- // Show cursor on top of text
- mCursor->setPriority(1);
- setText(text);
- }
- LineEdit::~LineEdit()
- {
- }
- void LineEdit::setStyle(const XMLElement& element, ResourceCache* cache)
- {
- BorderImage::setStyle(element, cache);
-
- if (element.hasChildElement("maxlength"))
- setMaxLength(element.getChildElement("maxlength").getInt("value"));
- if (element.hasChildElement("cursormovable"))
- setCursorMovable(element.getChildElement("cursormovable").getBool("enable"));
- if (element.hasChildElement("textselectable"))
- setTextSelectable(element.getChildElement("textselectable").getBool("enable"));
- if (element.hasChildElement("textcopyable"))
- setTextCopyable(element.getChildElement("textcopyable").getBool("enable"));
-
- XMLElement textElem = element.getChildElement("text");
- if (textElem)
- {
- if (textElem.hasAttribute("value"))
- setText(textElem.getString("value"));
- mText->setStyle(textElem, cache);
- }
-
- XMLElement cursorElem = element.getChildElement("cursor");
- if (cursorElem)
- mCursor->setStyle(cursorElem, cache);
-
- if (element.hasChildElement("cursorposition"))
- setCursorPosition(element.getChildElement("cursorposition").getInt("value"));
- if (element.hasChildElement("cursorblinkrate"))
- setCursorBlinkRate(element.getChildElement("cursorblinkrate").getFloat("value"));
- if (element.hasChildElement("echocharacter"))
- {
- std::string text = element.getChildElement("echocharacter").getString("value");
- if (text.length())
- setEchoCharacter(text[0]);
- }
- }
- void LineEdit::update(float timeStep)
- {
- if (mCursorBlinkRate > 0.0f)
- mCursorBlinkTimer = fmodf(mCursorBlinkTimer + mCursorBlinkRate * timeStep, 1.0f);
- else
- mCursorBlinkTimer = 0.0f;
-
- // Update cursor position if font has changed
- if ((mText->getFont() != mLastFont) || (mText->getFontSize() != mLastFontSize))
- {
- mLastFont = mText->getFont();
- mLastFontSize = mText->getFontSize();
- updateCursor();
- }
-
- bool cursorVisible = false;
- if (mFocus)
- cursorVisible = mCursorBlinkTimer < 0.5f;
- mCursor->setVisible(cursorVisible);
- }
- void LineEdit::onClick(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor)
- {
- if ((buttons & MOUSEB_LEFT) && (mCursorMovable))
- {
- unsigned pos = getCharIndex(position);
- if (pos != M_MAX_UNSIGNED)
- {
- setCursorPosition(pos);
- mText->clearSelection();
- }
- }
- }
- void LineEdit::onDragStart(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor)
- {
- mDragStartPosition = getCharIndex(position);
- }
- void LineEdit::onDragMove(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor)
- {
- if ((mCursorMovable) && (mTextSelectable))
- {
- unsigned start = mDragStartPosition;
- unsigned current = getCharIndex(position);
- if ((start != M_MAX_UNSIGNED) && (current != M_MAX_UNSIGNED))
- {
- if (start < current)
- mText->setSelection(start, current - start);
- else
- mText->setSelection(current, start - current);
- setCursorPosition(current);
- }
- }
- }
- bool LineEdit::onDragDropTest(UIElement* source)
- {
- return (dynamic_cast<LineEdit*>(source) != 0);
- }
- bool LineEdit::onDragDropFinish(UIElement* source)
- {
- LineEdit* sourceLineEdit = dynamic_cast<LineEdit*>(source);
- if (sourceLineEdit)
- {
- setText(sourceLineEdit->getText());
- return true;
- }
- else
- return false;
- }
- void LineEdit::onKey(int key, int buttons, int qualifiers)
- {
- bool changed = false;
- bool cursorMoved = false;
-
- switch (key)
- {
- case 'X':
- case 'C':
- if ((mTextCopyable) && (qualifiers & QUAL_CTRL))
- {
- unsigned start = mText->getSelectionStart();
- unsigned length = mText->getSelectionLength();
-
- if (mText->getSelectionLength())
- sClipBoard = mLine.substr(start, length);
-
- if (key == 'X')
- {
- if (start + length < mLine.length())
- mLine = mLine.substr(0, start) + mLine.substr(start + length);
- else
- mLine = mLine.substr(0, start);
- mText->clearSelection();
- mCursorPosition = start;
- changed = true;
- }
- }
- break;
-
- case 'V':
- if ((mTextCopyable) && (qualifiers & QUAL_CTRL))
- {
- if (sClipBoard.length())
- {
- if (mCursorPosition < mLine.length())
- mLine = mLine.substr(0, mCursorPosition) + sClipBoard + mLine.substr(mCursorPosition);
- else
- mLine += sClipBoard;
- mCursorPosition += sClipBoard.length();
- changed = true;
- }
- }
- break;
-
- case KEY_LEFT:
- if (!(qualifiers & QUAL_SHIFT))
- mText->clearSelection();
- if ((mCursorMovable) && (mCursorPosition > 0))
- {
- if ((mTextSelectable) && (qualifiers & QUAL_SHIFT) && (!mText->getSelectionLength()))
- mDragStartPosition = mCursorPosition;
-
- if (qualifiers & QUAL_CTRL)
- mCursorPosition = 0;
- else
- --mCursorPosition;
- cursorMoved = true;
-
- if ((mTextSelectable) && (qualifiers & QUAL_SHIFT))
- {
- unsigned start = mDragStartPosition;
- unsigned current = mCursorPosition;
- if (start < current)
- mText->setSelection(start, current - start);
- else
- mText->setSelection(current, start - current);
- }
- }
- break;
-
- case KEY_RIGHT:
- if (!(qualifiers & QUAL_SHIFT))
- mText->clearSelection();
- if ((mCursorMovable) && (mCursorPosition < mLine.length()))
- {
- if ((mTextSelectable) && (qualifiers & QUAL_SHIFT) && (!mText->getSelectionLength()))
- mDragStartPosition = mCursorPosition;
-
- if (qualifiers & QUAL_CTRL)
- mCursorPosition = mLine.length();
- else
- ++mCursorPosition;
- cursorMoved = true;
-
- if ((mTextSelectable) && (qualifiers & QUAL_SHIFT))
- {
- unsigned start = mDragStartPosition;
- unsigned current = mCursorPosition;
- if (start < current)
- mText->setSelection(start, current - start);
- else
- mText->setSelection(current, start - current);
- }
- }
- break;
-
- case KEY_HOME:
- if ((mCursorMovable) && (mCursorPosition > 0))
- {
- mCursorPosition = 0;
- cursorMoved = true;
- }
- break;
-
- case KEY_END:
- if ((mCursorMovable) && (mCursorPosition < mLine.length()))
- {
- mCursorPosition = mLine.length();
- cursorMoved = true;
- }
- break;
-
- case KEY_DELETE:
- if (!mText->getSelectionLength())
- {
- if (mCursorPosition < mLine.length())
- {
- mLine = mLine.substr(0, mCursorPosition) + mLine.substr(mCursorPosition + 1);
- changed = true;
- }
- }
- else
- {
- // If a selection exists, erase it
- unsigned start = mText->getSelectionStart();
- unsigned length = mText->getSelectionLength();
- if (start + length < mLine.length())
- mLine = mLine.substr(0, start) + mLine.substr(start + length);
- else
- mLine = mLine.substr(0, start);
- mText->clearSelection();
- mCursorPosition = start;
- changed = true;
- }
- break;
-
- case KEY_UP:
- case KEY_DOWN:
- case KEY_PAGEUP:
- case KEY_PAGEDOWN:
- {
- using namespace UnhandledKey;
-
- VariantMap eventData;
- eventData[P_ELEMENT] = (void*)this;
- eventData[P_KEY] = key;
- eventData[P_BUTTONS] = buttons;
- eventData[P_QUALIFIERS] = qualifiers;
- sendEvent(EVENT_UNHANDLEDKEY, eventData);
- }
- return;
- }
-
- if (changed)
- {
- updateText();
- updateCursor();
- }
- else if (cursorMoved)
- updateCursor();
- }
- void LineEdit::onChar(unsigned char c, int buttons, int qualifiers)
- {
- bool changed = false;
-
- if (qualifiers & QUAL_CTRL)
- return;
-
- if (c == '\b')
- {
- if (!mText->getSelectionLength())
- {
- if ((mLine.length()) && (mCursorPosition))
- {
- if (mCursorPosition < mLine.length())
- mLine = mLine.substr(0, mCursorPosition - 1) + mLine.substr(mCursorPosition);
- else
- mLine = mLine.substr(0, mCursorPosition - 1);
- --mCursorPosition;
- changed = true;
- }
- }
- else
- {
- // If a selection exists, erase it
- unsigned start = mText->getSelectionStart();
- unsigned length = mText->getSelectionLength();
- if (start + length < mLine.length())
- mLine = mLine.substr(0, start) + mLine.substr(start + length);
- else
- mLine = mLine.substr(0, start);
- mText->clearSelection();
- mCursorPosition = start;
- changed = true;
- }
- }
- else if (c == '\r')
- {
- using namespace TextFinished;
-
- VariantMap eventData;
- eventData[P_ELEMENT] = (void*)this;
- eventData[P_TEXT] = mLine;
- sendEvent(EVENT_TEXTFINISHED, eventData);
- return;
- }
- else if ((c >= 0x20) && ((!mMaxLength) || (mLine.length() < mMaxLength)))
- {
- static std::string charStr;
- charStr.resize(1);
- charStr[0] = c;
-
- if (!mText->getSelectionLength())
- {
- if (mCursorPosition == mLine.length())
- mLine += charStr;
- else
- mLine = mLine.substr(0, mCursorPosition) + charStr + mLine.substr(mCursorPosition);
- ++mCursorPosition;
- }
- else
- {
- // If a selection exists, erase it first
- unsigned start = mText->getSelectionStart();
- unsigned length = mText->getSelectionLength();
- if (start + length < mLine.length())
- mLine = mLine.substr(0, start) + charStr + mLine.substr(start + length);
- else
- mLine = mLine.substr(0, start) + charStr;
- mCursorPosition = start + 1;
- }
- changed = true;
- }
-
- if (changed)
- {
- mText->clearSelection();
- updateText();
- updateCursor();
- }
- }
- void LineEdit::onFocus()
- {
- updateCursor();
- }
- void LineEdit::onDefocus()
- {
- mText->clearSelection();
- }
- void LineEdit::setText(const std::string& text)
- {
- if (text != mLine)
- {
- mLine = text;
- mCursorPosition = mLine.length();
- updateText();
- updateCursor();
- }
- }
- void LineEdit::setCursorPosition(unsigned position)
- {
- if ((position > mLine.length()) || (!mCursorMovable))
- position = mLine.length();
-
- if (position != mCursorPosition)
- {
- mCursorPosition = position;
- updateCursor();
- }
- }
- void LineEdit::setCursorBlinkRate(float rate)
- {
- mCursorBlinkRate = max(rate, 0.0f);
- }
- void LineEdit::setMaxLength(unsigned length)
- {
- mMaxLength = length;
- }
- void LineEdit::setEchoCharacter(char c)
- {
- mEchoCharacter = c;
- updateText();
- }
- void LineEdit::setCursorMovable(bool enable)
- {
- mCursorMovable = enable;
- }
- void LineEdit::setTextSelectable(bool enable)
- {
- mTextSelectable = enable;
- }
- void LineEdit::setTextCopyable(bool enable)
- {
- mTextCopyable = enable;
- }
- void LineEdit::updateText()
- {
- if (!mEchoCharacter)
- mText->setText(mLine);
- else
- {
- std::string echoText;
- echoText.resize(mLine.length());
- for (unsigned i = 0; i < mLine.length(); ++i)
- echoText[i] = mEchoCharacter;
- mText->setText(echoText);
- }
- if (mCursorPosition > mLine.length())
- {
- mCursorPosition = mLine.length();
- updateCursor();
- }
-
- using namespace TextChanged;
-
- VariantMap eventData;
- eventData[P_ELEMENT] = (void*)this;
- eventData[P_TEXT] = mLine;
- sendEvent(EVENT_TEXTCHANGED, eventData);
- }
- void LineEdit::updateCursor()
- {
- int x = 0;
- const std::vector<IntVector2>& charPositions = mText->getCharPositions();
- if (charPositions.size())
- x = mCursorPosition < charPositions.size() ? charPositions[mCursorPosition].mX : charPositions.back().mX;
-
- mCursor->setPosition(mText->getPosition() + IntVector2(x, 0));
- mCursor->setSize(mCursor->getWidth(), mText->getRowHeight());
-
- // Scroll if necessary
- int sx = -getChildOffset().mX;
- int left = mClipBorder.mLeft;
- int right = getWidth() - mClipBorder.mLeft - mClipBorder.mRight - mCursor->getWidth();
- if (x - sx > right)
- sx = x - right;
- if (x - sx < left)
- sx = x - left;
- setChildOffset(IntVector2(-sx, 0));
-
- // Restart blinking
- mCursorBlinkTimer = 0.0f;
- }
- unsigned LineEdit::getCharIndex(const IntVector2& position)
- {
- IntVector2 screenPosition = elementToScreen(position);
- IntVector2 textPosition = mText->screenToElement(screenPosition);
- const std::vector<IntVector2>& charPositions = mText->getCharPositions();
-
- if (textPosition.mX < 0)
- return 0;
-
- for (unsigned i = charPositions.size() - 1; i < charPositions.size(); --i)
- {
- if (textPosition.mX >= charPositions[i].mX)
- return i;
- }
-
- return M_MAX_UNSIGNED;
- }
|