| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665 |
- //
- // Copyright (c) 2008-2015 the Urho3D project.
- //
- // 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 "../Core/Context.h"
- #include "../Input/Input.h"
- #include "../UI/LineEdit.h"
- #include "../UI/Text.h"
- #include "../UI/UI.h"
- #include "../UI/UIEvents.h"
- #include "../DebugNew.h"
- namespace Urho3D
- {
- StringHash VAR_DRAGDROPCONTENT("DragDropContent");
- extern const char* UI_CATEGORY;
- LineEdit::LineEdit(Context* context) :
- BorderImage(context),
- lastFont_(0),
- lastFontSize_(0),
- cursorPosition_(0),
- dragBeginCursor_(M_MAX_UNSIGNED),
- cursorBlinkRate_(1.0f),
- cursorBlinkTimer_(0.0f),
- maxLength_(0),
- echoCharacter_(0),
- cursorMovable_(true),
- textSelectable_(true),
- textCopyable_(true)
- {
- clipChildren_ = true;
- SetEnabled(true);
- focusMode_ = FM_FOCUSABLE_DEFOCUSABLE;
- text_ = CreateChild<Text>("LE_Text");
- text_->SetInternal(true);
- cursor_ = CreateChild<BorderImage>("LE_Cursor");
- cursor_->SetInternal(true);
- cursor_->SetPriority(1); // Show over text
- SubscribeToEvent(this, E_FOCUSED, URHO3D_HANDLER(LineEdit, HandleFocused));
- SubscribeToEvent(this, E_DEFOCUSED, URHO3D_HANDLER(LineEdit, HandleDefocused));
- SubscribeToEvent(this, E_LAYOUTUPDATED, URHO3D_HANDLER(LineEdit, HandleLayoutUpdated));
- }
- LineEdit::~LineEdit()
- {
- }
- void LineEdit::RegisterObject(Context* context)
- {
- context->RegisterFactory<LineEdit>(UI_CATEGORY);
- COPY_BASE_ATTRIBUTES(BorderImage);
- UPDATE_ATTRIBUTE_DEFAULT_VALUE("Clip Children", true);
- UPDATE_ATTRIBUTE_DEFAULT_VALUE("Is Enabled", true);
- UPDATE_ATTRIBUTE_DEFAULT_VALUE("Focus Mode", FM_FOCUSABLE_DEFOCUSABLE);
- ACCESSOR_ATTRIBUTE("Max Length", GetMaxLength, SetMaxLength, unsigned, 0, AM_FILE);
- ACCESSOR_ATTRIBUTE("Is Cursor Movable", IsCursorMovable, SetCursorMovable, bool, true, AM_FILE);
- ACCESSOR_ATTRIBUTE("Is Text Selectable", IsTextSelectable, SetTextSelectable, bool, true, AM_FILE);
- ACCESSOR_ATTRIBUTE("Is Text Copyable", IsTextCopyable, SetTextCopyable, bool, true, AM_FILE);
- ACCESSOR_ATTRIBUTE("Cursor Blink Rate", GetCursorBlinkRate, SetCursorBlinkRate, float, 1.0f, AM_FILE);
- ATTRIBUTE("Echo Character", int, echoCharacter_, 0, AM_FILE);
- }
- void LineEdit::ApplyAttributes()
- {
- BorderImage::ApplyAttributes();
- // Set the text's position to match clipping and indent width, so that text left edge is not left partially hidden
- text_->SetPosition(GetIndentWidth() + clipBorder_.left_, clipBorder_.top_);
- // Sync the text line
- line_ = text_->GetText();
- }
- void LineEdit::Update(float timeStep)
- {
- if (cursorBlinkRate_ > 0.0f)
- cursorBlinkTimer_ = fmodf(cursorBlinkTimer_ + cursorBlinkRate_ * timeStep, 1.0f);
- // Update cursor position if font has changed
- if (text_->GetFont() != lastFont_ || text_->GetFontSize() != lastFontSize_)
- {
- lastFont_ = text_->GetFont();
- lastFontSize_ = text_->GetFontSize();
- UpdateCursor();
- }
- bool cursorVisible = HasFocus() ? cursorBlinkTimer_ < 0.5f : false;
- cursor_->SetVisible(cursorVisible);
- }
- void LineEdit::OnClickBegin(const IntVector2& position, const IntVector2& screenPosition, int button, int buttons, int qualifiers,
- Cursor* cursor)
- {
- if (button == MOUSEB_LEFT && cursorMovable_)
- {
- unsigned pos = GetCharIndex(position);
- if (pos != M_MAX_UNSIGNED)
- {
- SetCursorPosition(pos);
- text_->ClearSelection();
- }
- }
- }
- void LineEdit::OnDoubleClick(const IntVector2& position, const IntVector2& screenPosition, int button, int buttons, int qualifiers,
- Cursor* cursor)
- {
- if (button == MOUSEB_LEFT)
- text_->SetSelection(0);
- }
- void LineEdit::OnDragBegin(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers,
- Cursor* cursor)
- {
- UIElement::OnDragBegin(position, screenPosition, buttons, qualifiers, cursor);
- dragBeginCursor_ = GetCharIndex(position);
- }
- void LineEdit::OnDragMove(const IntVector2& position, const IntVector2& screenPosition, const IntVector2& deltaPos, int buttons,
- int qualifiers, Cursor* cursor)
- {
- if (cursorMovable_ && textSelectable_)
- {
- unsigned start = dragBeginCursor_;
- unsigned current = GetCharIndex(position);
- if (start != M_MAX_UNSIGNED && current != M_MAX_UNSIGNED)
- {
- if (start < current)
- text_->SetSelection(start, current - start);
- else
- text_->SetSelection(current, start - current);
- SetCursorPosition(current);
- }
- }
- }
- bool LineEdit::OnDragDropTest(UIElement* source)
- {
- if (source && editable_)
- {
- if (source->GetVars().Contains(VAR_DRAGDROPCONTENT))
- return true;
- StringHash sourceType = source->GetType();
- return sourceType == LineEdit::GetTypeStatic() || sourceType == Text::GetTypeStatic();
- }
- return false;
- }
- bool LineEdit::OnDragDropFinish(UIElement* source)
- {
- if (source && editable_)
- {
- // If the UI element in question has a drag-and-drop content string defined, use it instead of element text
- if (source->GetVars().Contains(VAR_DRAGDROPCONTENT))
- {
- SetText(source->GetVar(VAR_DRAGDROPCONTENT).GetString());
- return true;
- }
- StringHash sourceType = source->GetType();
- if (sourceType == LineEdit::GetTypeStatic())
- {
- LineEdit* sourceLineEdit = static_cast<LineEdit*>(source);
- SetText(sourceLineEdit->GetText());
- return true;
- }
- else if (sourceType == Text::GetTypeStatic())
- {
- Text* sourceText = static_cast<Text*>(source);
- SetText(sourceText->GetText());
- return true;
- }
- }
- return false;
- }
- void LineEdit::OnKey(int key, int buttons, int qualifiers)
- {
- bool changed = false;
- bool cursorMoved = false;
- switch (key)
- {
- case 'X':
- case 'C':
- if (textCopyable_ && qualifiers & QUAL_CTRL)
- {
- unsigned start = text_->GetSelectionStart();
- unsigned length = text_->GetSelectionLength();
- if (text_->GetSelectionLength())
- GetSubsystem<UI>()->SetClipboardText(line_.SubstringUTF8(start, length));
- if (key == 'X' && editable_)
- {
- if (start + length < line_.LengthUTF8())
- line_ = line_.SubstringUTF8(0, start) + line_.SubstringUTF8(start + length);
- else
- line_ = line_.SubstringUTF8(0, start);
- text_->ClearSelection();
- cursorPosition_ = start;
- changed = true;
- }
- }
- break;
- case 'V':
- if (editable_ && textCopyable_ && qualifiers & QUAL_CTRL)
- {
- const String& clipBoard = GetSubsystem<UI>()->GetClipboardText();
- if (!clipBoard.Empty())
- {
- // Remove selected text first
- if (text_->GetSelectionLength() > 0)
- {
- unsigned start = text_->GetSelectionStart();
- unsigned length = text_->GetSelectionLength();
- if (start + length < line_.LengthUTF8())
- line_ = line_.SubstringUTF8(0, start) + line_.SubstringUTF8(start + length);
- else
- line_ = line_.SubstringUTF8(0, start);
- text_->ClearSelection();
- cursorPosition_ = start;
- }
- if (cursorPosition_ < line_.LengthUTF8())
- line_ = line_.SubstringUTF8(0, cursorPosition_) + clipBoard + line_.SubstringUTF8(cursorPosition_);
- else
- line_ += clipBoard;
- cursorPosition_ += clipBoard.LengthUTF8();
- changed = true;
- }
- }
- break;
- case KEY_HOME:
- qualifiers |= QUAL_CTRL;
- // Fallthru
- case KEY_LEFT:
- if (cursorMovable_ && cursorPosition_ > 0)
- {
- if (textSelectable_ && qualifiers & QUAL_SHIFT && !text_->GetSelectionLength())
- dragBeginCursor_ = cursorPosition_;
- if (qualifiers & QUAL_CTRL)
- cursorPosition_ = 0;
- else if (text_->GetSelectionLength() && !(qualifiers & QUAL_SHIFT))
- cursorPosition_ = text_->GetSelectionStart();
- else
- --cursorPosition_;
- cursorMoved = true;
- if (textSelectable_ && qualifiers & QUAL_SHIFT)
- {
- unsigned start = dragBeginCursor_;
- unsigned current = cursorPosition_;
- if (start < current)
- text_->SetSelection(start, current - start);
- else
- text_->SetSelection(current, start - current);
- }
- }
- if (!(qualifiers & QUAL_SHIFT))
- text_->ClearSelection();
- break;
- case KEY_END:
- qualifiers |= QUAL_CTRL;
- // Fallthru
- case KEY_RIGHT:
- if (cursorMovable_ && cursorPosition_ < line_.LengthUTF8())
- {
- if (textSelectable_ && qualifiers & QUAL_SHIFT && !text_->GetSelectionLength())
- dragBeginCursor_ = cursorPosition_;
- if (qualifiers & QUAL_CTRL)
- cursorPosition_ = line_.LengthUTF8();
- else if (text_->GetSelectionLength() && !(qualifiers & QUAL_SHIFT))
- cursorPosition_ = text_->GetSelectionStart() + text_->GetSelectionLength();
- else
- ++cursorPosition_;
- cursorMoved = true;
- if (textSelectable_ && qualifiers & QUAL_SHIFT)
- {
- unsigned start = dragBeginCursor_;
- unsigned current = cursorPosition_;
- if (start < current)
- text_->SetSelection(start, current - start);
- else
- text_->SetSelection(current, start - current);
- }
- }
- if (!(qualifiers & QUAL_SHIFT))
- text_->ClearSelection();
- break;
- case KEY_DELETE:
- if (editable_)
- {
- if (!text_->GetSelectionLength())
- {
- if (cursorPosition_ < line_.LengthUTF8())
- {
- line_ = line_.SubstringUTF8(0, cursorPosition_) + line_.SubstringUTF8(cursorPosition_ + 1);
- changed = true;
- }
- }
- else
- {
- // If a selection exists, erase it
- unsigned start = text_->GetSelectionStart();
- unsigned length = text_->GetSelectionLength();
- if (start + length < line_.LengthUTF8())
- line_ = line_.SubstringUTF8(0, start) + line_.SubstringUTF8(start + length);
- else
- line_ = line_.SubstringUTF8(0, start);
- text_->ClearSelection();
- cursorPosition_ = start;
- changed = true;
- }
- }
- break;
- case KEY_UP:
- case KEY_DOWN:
- case KEY_PAGEUP:
- case KEY_PAGEDOWN:
- {
- using namespace UnhandledKey;
- VariantMap& eventData = GetEventDataMap();
- eventData[P_ELEMENT] = this;
- eventData[P_KEY] = key;
- eventData[P_BUTTONS] = buttons;
- eventData[P_QUALIFIERS] = qualifiers;
- SendEvent(E_UNHANDLEDKEY, eventData);
- }
- return;
- case KEY_BACKSPACE:
- if (editable_)
- {
- if (!text_->GetSelectionLength())
- {
- if (line_.LengthUTF8() && cursorPosition_)
- {
- if (cursorPosition_ < line_.LengthUTF8())
- line_ = line_.SubstringUTF8(0, cursorPosition_ - 1) + line_.SubstringUTF8(cursorPosition_);
- else
- line_ = line_.SubstringUTF8(0, cursorPosition_ - 1);
- --cursorPosition_;
- changed = true;
- }
- }
- else
- {
- // If a selection exists, erase it
- unsigned start = text_->GetSelectionStart();
- unsigned length = text_->GetSelectionLength();
- if (start + length < line_.LengthUTF8())
- line_ = line_.SubstringUTF8(0, start) + line_.SubstringUTF8(start + length);
- else
- line_ = line_.SubstringUTF8(0, start);
- text_->ClearSelection();
- cursorPosition_ = start;
- changed = true;
- }
- }
- break;
- case KEY_RETURN:
- case KEY_RETURN2:
- case KEY_KP_ENTER:
- {
- // If using the on-screen keyboard, defocus this element to hide it now
- if (GetSubsystem<UI>()->GetUseScreenKeyboard() && HasFocus())
- SetFocus(false);
- using namespace TextFinished;
- VariantMap& eventData = GetEventDataMap();
- eventData[P_ELEMENT] = this;
- eventData[P_TEXT] = line_;
- SendEvent(E_TEXTFINISHED, eventData);
- return;
- }
- default: break;
- }
- if (changed)
- {
- UpdateText();
- UpdateCursor();
- }
- else if (cursorMoved)
- UpdateCursor();
- }
- void LineEdit::OnTextInput(const String& text, int buttons, int qualifiers)
- {
- if (!editable_)
- return;
- bool changed = false;
- // If only CTRL is held down, do not edit
- if ((qualifiers & (QUAL_CTRL | QUAL_ALT)) == QUAL_CTRL)
- return;
- // Send char as an event to allow changing it
- using namespace CharEntry;
- VariantMap& eventData = GetEventDataMap();
- eventData[P_ELEMENT] = this;
- eventData[P_TEXT] = text;
- eventData[P_BUTTONS] = buttons;
- eventData[P_QUALIFIERS] = qualifiers;
- SendEvent(E_TEXTENTRY, eventData);
- const String newText = eventData[P_TEXT].GetString().SubstringUTF8(0);
- if (!newText.Empty() && (!maxLength_ || line_.LengthUTF8() + newText.LengthUTF8() <= maxLength_))
- {
- if (!text_->GetSelectionLength())
- {
- if (cursorPosition_ == line_.LengthUTF8())
- line_ += newText;
- else
- line_ = line_.SubstringUTF8(0, cursorPosition_) + newText + line_.SubstringUTF8(cursorPosition_);
- cursorPosition_ += newText.LengthUTF8();
- }
- else
- {
- // If a selection exists, erase it first
- unsigned start = text_->GetSelectionStart();
- unsigned length = text_->GetSelectionLength();
- if (start + length < line_.LengthUTF8())
- line_ = line_.SubstringUTF8(0, start) + newText + line_.SubstringUTF8(start + length);
- else
- line_ = line_.SubstringUTF8(0, start) + newText;
- cursorPosition_ = start + newText.LengthUTF8();
- }
- changed = true;
- }
- if (changed)
- {
- text_->ClearSelection();
- UpdateText();
- UpdateCursor();
- }
- }
- void LineEdit::SetText(const String& text)
- {
- if (text != line_)
- {
- line_ = text;
- cursorPosition_ = line_.LengthUTF8();
- UpdateText();
- UpdateCursor();
- }
- }
- void LineEdit::SetCursorPosition(unsigned position)
- {
- if (position > line_.LengthUTF8() || !cursorMovable_)
- position = line_.LengthUTF8();
- if (position != cursorPosition_)
- {
- cursorPosition_ = position;
- UpdateCursor();
- }
- }
- void LineEdit::SetCursorBlinkRate(float rate)
- {
- cursorBlinkRate_ = Max(rate, 0.0f);
- if (cursorBlinkRate_ == 0.0f)
- cursorBlinkTimer_ = 0.0f; // Cursor does not blink, i.e. always visible
- }
- void LineEdit::SetMaxLength(unsigned length)
- {
- maxLength_ = length;
- }
- void LineEdit::SetEchoCharacter(unsigned c)
- {
- echoCharacter_ = c;
- UpdateText();
- }
- void LineEdit::SetCursorMovable(bool enable)
- {
- cursorMovable_ = enable;
- }
- void LineEdit::SetTextSelectable(bool enable)
- {
- textSelectable_ = enable;
- }
- void LineEdit::SetTextCopyable(bool enable)
- {
- textCopyable_ = enable;
- }
- bool LineEdit::FilterImplicitAttributes(XMLElement& dest) const
- {
- if (!BorderImage::FilterImplicitAttributes(dest))
- return false;
- XMLElement childElem = dest.GetChild("element");
- if (!childElem)
- return false;
- if (!RemoveChildXML(childElem, "Name", "LE_Text"))
- return false;
- if (!RemoveChildXML(childElem, "Position"))
- return false;
- childElem = childElem.GetNext("element");
- if (!childElem)
- return false;
- if (!RemoveChildXML(childElem, "Name", "LE_Cursor"))
- return false;
- if (!RemoveChildXML(childElem, "Priority", "1"))
- return false;
- if (!RemoveChildXML(childElem, "Position"))
- return false;
- if (!RemoveChildXML(childElem, "Is Visible"))
- return false;
- return true;
- }
- void LineEdit::UpdateText()
- {
- unsigned utf8Length = line_.LengthUTF8();
- if (!echoCharacter_)
- text_->SetText(line_);
- else
- {
- String echoText;
- for (unsigned i = 0; i < utf8Length; ++i)
- echoText.AppendUTF8(echoCharacter_);
- text_->SetText(echoText);
- }
- if (cursorPosition_ > utf8Length)
- {
- cursorPosition_ = utf8Length;
- UpdateCursor();
- }
- using namespace TextChanged;
- VariantMap& eventData = GetEventDataMap();
- eventData[P_ELEMENT] = this;
- eventData[P_TEXT] = line_;
- SendEvent(E_TEXTCHANGED, eventData);
- }
- void LineEdit::UpdateCursor()
- {
- int x = text_->GetCharPosition(cursorPosition_).x_;
- text_->SetPosition(GetIndentWidth() + clipBorder_.left_, clipBorder_.top_);
- cursor_->SetPosition(text_->GetPosition() + IntVector2(x, 0));
- cursor_->SetSize(cursor_->GetWidth(), text_->GetRowHeight());
- // Scroll if necessary
- int sx = -GetChildOffset().x_;
- int left = clipBorder_.left_;
- int right = GetWidth() - clipBorder_.left_ - clipBorder_.right_ - cursor_->GetWidth();
- if (x - sx > right)
- sx = x - right;
- if (x - sx < left)
- sx = x - left;
- if (sx < 0)
- sx = 0;
- SetChildOffset(IntVector2(-sx, 0));
- // Restart blinking
- cursorBlinkTimer_ = 0.0f;
- }
- unsigned LineEdit::GetCharIndex(const IntVector2& position)
- {
- IntVector2 screenPosition = ElementToScreen(position);
- IntVector2 textPosition = text_->ScreenToElement(screenPosition);
- if (textPosition.x_ < 0)
- return 0;
- for (int i = text_->GetNumChars(); i >= 0; --i)
- {
- if (textPosition.x_ >= text_->GetCharPosition((unsigned)i).x_)
- return (unsigned)i;
- }
- return M_MAX_UNSIGNED;
- }
- void LineEdit::HandleFocused(StringHash eventType, VariantMap& eventData)
- {
- if (eventData[Focused::P_BYKEY].GetBool())
- {
- cursorPosition_ = line_.LengthUTF8();
- text_->SetSelection(0);
- }
- UpdateCursor();
- if (GetSubsystem<UI>()->GetUseScreenKeyboard())
- GetSubsystem<Input>()->SetScreenKeyboardVisible(true);
- }
- void LineEdit::HandleDefocused(StringHash eventType, VariantMap& eventData)
- {
- text_->ClearSelection();
- if (GetSubsystem<UI>()->GetUseScreenKeyboard())
- GetSubsystem<Input>()->SetScreenKeyboardVisible(false);
- }
- void LineEdit::HandleLayoutUpdated(StringHash eventType, VariantMap& eventData)
- {
- UpdateCursor();
- }
- }
|