LineEdit.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656
  1. //
  2. // Copyright (c) 2008-2014 the Urho3D project.
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to deal
  6. // in the Software without restriction, including without limitation the rights
  7. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. // copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. // THE SOFTWARE.
  21. //
  22. #include "Precompiled.h"
  23. #include "Context.h"
  24. #include "Input.h"
  25. #include "LineEdit.h"
  26. #include "Text.h"
  27. #include "UI.h"
  28. #include "UIEvents.h"
  29. #include "DebugNew.h"
  30. namespace Urho3D
  31. {
  32. StringHash VAR_DRAGDROPCONTENT("DragDropContent");
  33. extern const char* UI_CATEGORY;
  34. LineEdit::LineEdit(Context* context) :
  35. BorderImage(context),
  36. lastFont_(0),
  37. lastFontSize_(0),
  38. cursorPosition_(0),
  39. dragBeginCursor_(M_MAX_UNSIGNED),
  40. cursorBlinkRate_(1.0f),
  41. cursorBlinkTimer_(0.0f),
  42. maxLength_(0),
  43. echoCharacter_(0),
  44. cursorMovable_(true),
  45. textSelectable_(true),
  46. textCopyable_(true)
  47. {
  48. clipChildren_ = true;
  49. enabled_ = true;
  50. focusMode_ = FM_FOCUSABLE_DEFOCUSABLE;
  51. text_ = CreateChild<Text>("LE_Text");
  52. text_->SetInternal(true);
  53. cursor_ = CreateChild<BorderImage>("LE_Cursor");
  54. cursor_->SetInternal(true);
  55. cursor_->SetPriority(1); // Show over text
  56. SubscribeToEvent(this, E_FOCUSED, HANDLER(LineEdit, HandleFocused));
  57. SubscribeToEvent(this, E_DEFOCUSED, HANDLER(LineEdit, HandleDefocused));
  58. SubscribeToEvent(this, E_LAYOUTUPDATED, HANDLER(LineEdit, HandleLayoutUpdated));
  59. }
  60. LineEdit::~LineEdit()
  61. {
  62. }
  63. void LineEdit::RegisterObject(Context* context)
  64. {
  65. context->RegisterFactory<LineEdit>(UI_CATEGORY);
  66. COPY_BASE_ATTRIBUTES(LineEdit, BorderImage);
  67. UPDATE_ATTRIBUTE_DEFAULT_VALUE(LineEdit, "Clip Children", true);
  68. UPDATE_ATTRIBUTE_DEFAULT_VALUE(LineEdit, "Is Enabled", true);
  69. UPDATE_ATTRIBUTE_DEFAULT_VALUE(LineEdit, "Focus Mode", FM_FOCUSABLE_DEFOCUSABLE);
  70. ACCESSOR_ATTRIBUTE(LineEdit, VAR_INT, "Max Length", GetMaxLength, SetMaxLength, unsigned, 0, AM_FILE);
  71. ACCESSOR_ATTRIBUTE(LineEdit, VAR_BOOL, "Is Cursor Movable", IsCursorMovable, SetCursorMovable, bool, true, AM_FILE);
  72. ACCESSOR_ATTRIBUTE(LineEdit, VAR_BOOL, "Is Text Selectable", IsTextSelectable, SetTextSelectable, bool, true, AM_FILE);
  73. ACCESSOR_ATTRIBUTE(LineEdit, VAR_BOOL, "Is Text Copyable", IsTextCopyable, SetTextCopyable, bool, true, AM_FILE);
  74. ACCESSOR_ATTRIBUTE(LineEdit, VAR_FLOAT, "Cursor Blink Rate", GetCursorBlinkRate, SetCursorBlinkRate, float, 1.0f, AM_FILE);
  75. ATTRIBUTE(LineEdit, VAR_INT, "Echo Character", echoCharacter_, 0, AM_FILE);
  76. }
  77. void LineEdit::ApplyAttributes()
  78. {
  79. BorderImage::ApplyAttributes();
  80. // Set the text's position to match clipping and indent width, so that text left edge is not left partially hidden
  81. text_->SetPosition(GetIndentWidth() + clipBorder_.left_, clipBorder_.top_);
  82. // Sync the text line
  83. line_ = text_->GetText();
  84. }
  85. void LineEdit::Update(float timeStep)
  86. {
  87. if (cursorBlinkRate_ > 0.0f)
  88. cursorBlinkTimer_ = fmodf(cursorBlinkTimer_ + cursorBlinkRate_ * timeStep, 1.0f);
  89. // Update cursor position if font has changed
  90. if (text_->GetFont() != lastFont_ || text_->GetFontSize() != lastFontSize_)
  91. {
  92. lastFont_ = text_->GetFont();
  93. lastFontSize_ = text_->GetFontSize();
  94. UpdateCursor();
  95. }
  96. bool cursorVisible = HasFocus() ? cursorBlinkTimer_ < 0.5f : false;
  97. cursor_->SetVisible(cursorVisible);
  98. }
  99. void LineEdit::OnClickBegin(const IntVector2& position, const IntVector2& screenPosition, int button, int buttons, int qualifiers, Cursor* cursor)
  100. {
  101. if (button == MOUSEB_LEFT && cursorMovable_)
  102. {
  103. unsigned pos = GetCharIndex(position);
  104. if (pos != M_MAX_UNSIGNED)
  105. {
  106. SetCursorPosition(pos);
  107. text_->ClearSelection();
  108. }
  109. }
  110. }
  111. void LineEdit::OnDoubleClick(const IntVector2& position, const IntVector2& screenPosition, int button, int buttons, int qualifiers, Cursor* cursor)
  112. {
  113. if (button == MOUSEB_LEFT)
  114. text_->SetSelection(0);
  115. }
  116. void LineEdit::OnDragBegin(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor)
  117. {
  118. dragBeginCursor_ = GetCharIndex(position);
  119. }
  120. void LineEdit::OnDragMove(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor)
  121. {
  122. if (cursorMovable_ && textSelectable_)
  123. {
  124. unsigned start = dragBeginCursor_;
  125. unsigned current = GetCharIndex(position);
  126. if (start != M_MAX_UNSIGNED && current != M_MAX_UNSIGNED)
  127. {
  128. if (start < current)
  129. text_->SetSelection(start, current - start);
  130. else
  131. text_->SetSelection(current, start - current);
  132. SetCursorPosition(current);
  133. }
  134. }
  135. }
  136. bool LineEdit::OnDragDropTest(UIElement* source)
  137. {
  138. if (source)
  139. {
  140. StringHash sourceType = source->GetType();
  141. return sourceType == LineEdit::GetTypeStatic() || sourceType == Text::GetTypeStatic();
  142. }
  143. return false;
  144. }
  145. bool LineEdit::OnDragDropFinish(UIElement* source)
  146. {
  147. if (source && editable_)
  148. {
  149. // If the UI element in question has a drag-and-drop content string defined, use it instead of element text
  150. if (source->GetVars().Contains(VAR_DRAGDROPCONTENT))
  151. {
  152. SetText(source->GetVar(VAR_DRAGDROPCONTENT).GetString());
  153. return true;
  154. }
  155. StringHash sourceType = source->GetType();
  156. if (sourceType == LineEdit::GetTypeStatic())
  157. {
  158. LineEdit* sourceLineEdit = static_cast<LineEdit*>(source);
  159. SetText(sourceLineEdit->GetText());
  160. return true;
  161. }
  162. else if (sourceType == Text::GetTypeStatic())
  163. {
  164. Text* sourceText = static_cast<Text*>(source);
  165. SetText(sourceText->GetText());
  166. return true;
  167. }
  168. }
  169. return false;
  170. }
  171. void LineEdit::OnKey(int key, int buttons, int qualifiers)
  172. {
  173. bool changed = false;
  174. bool cursorMoved = false;
  175. switch (key)
  176. {
  177. case 'X':
  178. case 'C':
  179. if (textCopyable_ && qualifiers & QUAL_CTRL)
  180. {
  181. unsigned start = text_->GetSelectionStart();
  182. unsigned length = text_->GetSelectionLength();
  183. if (text_->GetSelectionLength())
  184. GetSubsystem<UI>()->SetClipboardText(line_.SubstringUTF8(start, length));
  185. if (key == 'X' && editable_)
  186. {
  187. if (start + length < line_.LengthUTF8())
  188. line_ = line_.SubstringUTF8(0, start) + line_.SubstringUTF8(start + length);
  189. else
  190. line_ = line_.SubstringUTF8(0, start);
  191. text_->ClearSelection();
  192. cursorPosition_ = start;
  193. changed = true;
  194. }
  195. }
  196. break;
  197. case 'V':
  198. if (editable_ && textCopyable_ && qualifiers & QUAL_CTRL)
  199. {
  200. const String& clipBoard = GetSubsystem<UI>()->GetClipboardText();
  201. if (!clipBoard.Empty())
  202. {
  203. // Remove selected text first
  204. if(text_->GetSelectionLength() > 0)
  205. {
  206. unsigned start = text_->GetSelectionStart();
  207. unsigned length = text_->GetSelectionLength();
  208. if (start + length < line_.LengthUTF8())
  209. line_ = line_.SubstringUTF8(0, start) + line_.SubstringUTF8(start + length);
  210. else
  211. line_ = line_.SubstringUTF8(0, start);
  212. text_->ClearSelection();
  213. cursorPosition_ = start;
  214. }
  215. if (cursorPosition_ < line_.LengthUTF8())
  216. line_ = line_.SubstringUTF8(0, cursorPosition_) + clipBoard + line_.SubstringUTF8(cursorPosition_);
  217. else
  218. line_ += clipBoard;
  219. cursorPosition_ += clipBoard.LengthUTF8();
  220. changed = true;
  221. }
  222. }
  223. break;
  224. case KEY_HOME:
  225. qualifiers |= QUAL_CTRL;
  226. // Fallthru
  227. case KEY_LEFT:
  228. if (cursorMovable_ && cursorPosition_ > 0)
  229. {
  230. if (textSelectable_ && qualifiers & QUAL_SHIFT && !text_->GetSelectionLength())
  231. dragBeginCursor_ = cursorPosition_;
  232. if (qualifiers & QUAL_CTRL)
  233. cursorPosition_ = 0;
  234. else if (text_->GetSelectionLength() && !(qualifiers & QUAL_SHIFT))
  235. cursorPosition_ = text_->GetSelectionStart();
  236. else
  237. --cursorPosition_;
  238. cursorMoved = true;
  239. if (textSelectable_ && qualifiers & QUAL_SHIFT)
  240. {
  241. unsigned start = dragBeginCursor_;
  242. unsigned current = cursorPosition_;
  243. if (start < current)
  244. text_->SetSelection(start, current - start);
  245. else
  246. text_->SetSelection(current, start - current);
  247. }
  248. }
  249. if (!(qualifiers & QUAL_SHIFT))
  250. text_->ClearSelection();
  251. break;
  252. case KEY_END:
  253. qualifiers |= QUAL_CTRL;
  254. // Fallthru
  255. case KEY_RIGHT:
  256. if (cursorMovable_ && cursorPosition_ < line_.LengthUTF8())
  257. {
  258. if (textSelectable_ && qualifiers & QUAL_SHIFT && !text_->GetSelectionLength())
  259. dragBeginCursor_ = cursorPosition_;
  260. if (qualifiers & QUAL_CTRL)
  261. cursorPosition_ = line_.LengthUTF8();
  262. else if (text_->GetSelectionLength() && !(qualifiers & QUAL_SHIFT))
  263. cursorPosition_ = text_->GetSelectionStart() + text_->GetSelectionLength();
  264. else
  265. ++cursorPosition_;
  266. cursorMoved = true;
  267. if (textSelectable_ && qualifiers & QUAL_SHIFT)
  268. {
  269. unsigned start = dragBeginCursor_;
  270. unsigned current = cursorPosition_;
  271. if (start < current)
  272. text_->SetSelection(start, current - start);
  273. else
  274. text_->SetSelection(current, start - current);
  275. }
  276. }
  277. if (!(qualifiers & QUAL_SHIFT))
  278. text_->ClearSelection();
  279. break;
  280. case KEY_DELETE:
  281. if (editable_)
  282. {
  283. if (!text_->GetSelectionLength())
  284. {
  285. if (cursorPosition_ < line_.LengthUTF8())
  286. {
  287. line_ = line_.SubstringUTF8(0, cursorPosition_) + line_.SubstringUTF8(cursorPosition_ + 1);
  288. changed = true;
  289. }
  290. }
  291. else
  292. {
  293. // If a selection exists, erase it
  294. unsigned start = text_->GetSelectionStart();
  295. unsigned length = text_->GetSelectionLength();
  296. if (start + length < line_.LengthUTF8())
  297. line_ = line_.SubstringUTF8(0, start) + line_.SubstringUTF8(start + length);
  298. else
  299. line_ = line_.SubstringUTF8(0, start);
  300. text_->ClearSelection();
  301. cursorPosition_ = start;
  302. changed = true;
  303. }
  304. }
  305. break;
  306. case KEY_UP:
  307. case KEY_DOWN:
  308. case KEY_PAGEUP:
  309. case KEY_PAGEDOWN:
  310. {
  311. using namespace UnhandledKey;
  312. VariantMap& eventData = GetEventDataMap();
  313. eventData[P_ELEMENT] = this;
  314. eventData[P_KEY] = key;
  315. eventData[P_BUTTONS] = buttons;
  316. eventData[P_QUALIFIERS] = qualifiers;
  317. SendEvent(E_UNHANDLEDKEY, eventData);
  318. }
  319. return;
  320. case KEY_BACKSPACE:
  321. if (editable_)
  322. {
  323. if (!text_->GetSelectionLength())
  324. {
  325. if (line_.LengthUTF8() && cursorPosition_)
  326. {
  327. if (cursorPosition_ < line_.LengthUTF8())
  328. line_ = line_.SubstringUTF8(0, cursorPosition_ - 1) + line_.SubstringUTF8(cursorPosition_);
  329. else
  330. line_ = line_.SubstringUTF8(0, cursorPosition_ - 1);
  331. --cursorPosition_;
  332. changed = true;
  333. }
  334. }
  335. else
  336. {
  337. // If a selection exists, erase it
  338. unsigned start = text_->GetSelectionStart();
  339. unsigned length = text_->GetSelectionLength();
  340. if (start + length < line_.LengthUTF8())
  341. line_ = line_.SubstringUTF8(0, start) + line_.SubstringUTF8(start + length);
  342. else
  343. line_ = line_.SubstringUTF8(0, start);
  344. text_->ClearSelection();
  345. cursorPosition_ = start;
  346. changed = true;
  347. }
  348. }
  349. break;
  350. case KEY_RETURN:
  351. case KEY_RETURN2:
  352. case KEY_KP_ENTER:
  353. {
  354. // If using the on-screen keyboard, defocus this element to hide it now
  355. if (GetSubsystem<UI>()->GetUseScreenKeyboard() && HasFocus())
  356. SetFocus(false);
  357. using namespace TextFinished;
  358. VariantMap& eventData = GetEventDataMap();
  359. eventData[P_ELEMENT] = this;
  360. eventData[P_TEXT] = line_;
  361. SendEvent(E_TEXTFINISHED, eventData);
  362. return;
  363. }
  364. break;
  365. }
  366. if (changed)
  367. {
  368. UpdateText();
  369. UpdateCursor();
  370. }
  371. else if (cursorMoved)
  372. UpdateCursor();
  373. }
  374. void LineEdit::OnTextInput(const String& text, int buttons, int qualifiers)
  375. {
  376. if (!editable_)
  377. return;
  378. bool changed = false;
  379. // If only CTRL is held down, do not edit
  380. if ((qualifiers & (QUAL_CTRL | QUAL_ALT)) == QUAL_CTRL)
  381. return;
  382. // Send char as an event to allow changing it
  383. using namespace CharEntry;
  384. VariantMap& eventData = GetEventDataMap();
  385. eventData[P_ELEMENT] = this;
  386. eventData[P_TEXT] = text;
  387. eventData[P_BUTTONS] = buttons;
  388. eventData[P_QUALIFIERS] = qualifiers;
  389. SendEvent(E_TEXTENTRY, eventData);
  390. const String newText = eventData[P_TEXT].GetString().SubstringUTF8(0);
  391. if (!newText.Empty() && (!maxLength_ || line_.LengthUTF8() + newText.LengthUTF8() <= maxLength_))
  392. {
  393. if (!text_->GetSelectionLength())
  394. {
  395. if (cursorPosition_ == line_.LengthUTF8())
  396. line_ += newText;
  397. else
  398. line_ = line_.SubstringUTF8(0, cursorPosition_) + newText + line_.SubstringUTF8(cursorPosition_);
  399. cursorPosition_ += newText.LengthUTF8();
  400. }
  401. else
  402. {
  403. // If a selection exists, erase it first
  404. unsigned start = text_->GetSelectionStart();
  405. unsigned length = text_->GetSelectionLength();
  406. if (start + length < line_.LengthUTF8())
  407. line_ = line_.SubstringUTF8(0, start) + newText + line_.SubstringUTF8(start + length);
  408. else
  409. line_ = line_.SubstringUTF8(0, start) + newText;
  410. cursorPosition_ = start + newText.LengthUTF8();
  411. }
  412. changed = true;
  413. }
  414. if (changed)
  415. {
  416. text_->ClearSelection();
  417. UpdateText();
  418. UpdateCursor();
  419. }
  420. }
  421. void LineEdit::SetText(const String& text)
  422. {
  423. if (text != line_)
  424. {
  425. line_ = text;
  426. cursorPosition_ = line_.LengthUTF8();
  427. UpdateText();
  428. UpdateCursor();
  429. }
  430. }
  431. void LineEdit::SetCursorPosition(unsigned position)
  432. {
  433. if (position > line_.LengthUTF8() || !cursorMovable_)
  434. position = line_.LengthUTF8();
  435. if (position != cursorPosition_)
  436. {
  437. cursorPosition_ = position;
  438. UpdateCursor();
  439. }
  440. }
  441. void LineEdit::SetCursorBlinkRate(float rate)
  442. {
  443. cursorBlinkRate_ = Max(rate, 0.0f);
  444. if (cursorBlinkRate_ == 0.0f)
  445. cursorBlinkTimer_ = 0.0f; // Cursor does not blink, i.e. always visible
  446. }
  447. void LineEdit::SetMaxLength(unsigned length)
  448. {
  449. maxLength_ = length;
  450. }
  451. void LineEdit::SetEchoCharacter(unsigned c)
  452. {
  453. echoCharacter_ = c;
  454. UpdateText();
  455. }
  456. void LineEdit::SetCursorMovable(bool enable)
  457. {
  458. cursorMovable_ = enable;
  459. }
  460. void LineEdit::SetTextSelectable(bool enable)
  461. {
  462. textSelectable_ = enable;
  463. }
  464. void LineEdit::SetTextCopyable(bool enable)
  465. {
  466. textCopyable_ = enable;
  467. }
  468. bool LineEdit::FilterImplicitAttributes(XMLElement& dest) const
  469. {
  470. if (!BorderImage::FilterImplicitAttributes(dest))
  471. return false;
  472. XMLElement childElem = dest.GetChild("element");
  473. if (!childElem)
  474. return false;
  475. if (!RemoveChildXML(childElem, "Name", "LE_Text"))
  476. return false;
  477. if (!RemoveChildXML(childElem, "Position"))
  478. return false;
  479. childElem = childElem.GetNext("element");
  480. if (!childElem)
  481. return false;
  482. if (!RemoveChildXML(childElem, "Name", "LE_Cursor"))
  483. return false;
  484. if (!RemoveChildXML(childElem, "Priority", "1"))
  485. return false;
  486. if (!RemoveChildXML(childElem, "Position"))
  487. return false;
  488. if (!RemoveChildXML(childElem, "Is Visible"))
  489. return false;
  490. return true;
  491. }
  492. void LineEdit::UpdateText()
  493. {
  494. unsigned utf8Length = line_.LengthUTF8();
  495. if (!echoCharacter_)
  496. text_->SetText(line_);
  497. else
  498. {
  499. String echoText;
  500. for (unsigned i = 0; i < utf8Length; ++i)
  501. echoText.AppendUTF8(echoCharacter_);
  502. text_->SetText(echoText);
  503. }
  504. if (cursorPosition_ > utf8Length)
  505. {
  506. cursorPosition_ = utf8Length;
  507. UpdateCursor();
  508. }
  509. using namespace TextChanged;
  510. VariantMap& eventData = GetEventDataMap();
  511. eventData[P_ELEMENT] = this;
  512. eventData[P_TEXT] = line_;
  513. SendEvent(E_TEXTCHANGED, eventData);
  514. }
  515. void LineEdit::UpdateCursor()
  516. {
  517. int x = text_->GetCharPosition(cursorPosition_).x_;
  518. text_->SetPosition(GetIndentWidth() + clipBorder_.left_, clipBorder_.top_);
  519. cursor_->SetPosition(text_->GetPosition() + IntVector2(x, 0));
  520. cursor_->SetSize(cursor_->GetWidth(), text_->GetRowHeight());
  521. // Scroll if necessary
  522. int sx = -GetChildOffset().x_;
  523. int left = clipBorder_.left_;
  524. int right = GetWidth() - clipBorder_.left_ - clipBorder_.right_ - cursor_->GetWidth();
  525. if (x - sx > right)
  526. sx = x - right;
  527. if (x - sx < left)
  528. sx = x - left;
  529. if (sx < 0)
  530. sx = 0;
  531. SetChildOffset(IntVector2(-sx, 0));
  532. // Restart blinking
  533. cursorBlinkTimer_ = 0.0f;
  534. }
  535. unsigned LineEdit::GetCharIndex(const IntVector2& position)
  536. {
  537. IntVector2 screenPosition = ElementToScreen(position);
  538. IntVector2 textPosition = text_->ScreenToElement(screenPosition);
  539. if (textPosition.x_ < 0)
  540. return 0;
  541. int numChars = text_->GetNumChars();
  542. for (int i = numChars; i >= 0; --i)
  543. {
  544. if (textPosition.x_ >= text_->GetCharPosition(i).x_)
  545. return i;
  546. }
  547. return M_MAX_UNSIGNED;
  548. }
  549. void LineEdit::HandleFocused(StringHash eventType, VariantMap& eventData)
  550. {
  551. if (eventData[Focused::P_BYKEY].GetBool())
  552. {
  553. cursorPosition_ = line_.LengthUTF8();
  554. text_->SetSelection(0);
  555. }
  556. UpdateCursor();
  557. if (GetSubsystem<UI>()->GetUseScreenKeyboard())
  558. GetSubsystem<Input>()->SetScreenKeyboardVisible(true);
  559. }
  560. void LineEdit::HandleDefocused(StringHash eventType, VariantMap& eventData)
  561. {
  562. text_->ClearSelection();
  563. if (GetSubsystem<UI>()->GetUseScreenKeyboard())
  564. GetSubsystem<Input>()->SetScreenKeyboardVisible(false);
  565. }
  566. void LineEdit::HandleLayoutUpdated(StringHash eventType, VariantMap& eventData)
  567. {
  568. UpdateCursor();
  569. }
  570. }