2
0

TextControls.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749
  1. /*
  2. * TextControls.cpp, part of VCMI engine
  3. *
  4. * Authors: listed in file AUTHORS in main folder
  5. *
  6. * License: GNU General Public License v2.0 or later
  7. * Full text of license available in license.txt file, in main folder
  8. *
  9. */
  10. #include "StdInc.h"
  11. #include "TextControls.h"
  12. #include "Slider.h"
  13. #include "Images.h"
  14. #include "../CPlayerInterface.h"
  15. #include "../gui/CGuiHandler.h"
  16. #include "../gui/Shortcut.h"
  17. #include "../windows/CMessage.h"
  18. #include "../adventureMap/CInGameConsole.h"
  19. #include "../renderSDL/SDL_Extensions.h"
  20. #include "../render/Canvas.h"
  21. #include "../render/Graphics.h"
  22. #include "../render/IFont.h"
  23. #include "../../lib/TextOperations.h"
  24. #ifdef VCMI_ANDROID
  25. #include "lib/CAndroidVMHelper.h"
  26. #endif
  27. std::list<CFocusable*> CFocusable::focusables;
  28. CFocusable * CFocusable::inputWithFocus;
  29. std::string CLabel::visibleText()
  30. {
  31. return text;
  32. }
  33. void CLabel::showAll(Canvas & to)
  34. {
  35. CIntObject::showAll(to);
  36. if(!visibleText().empty())
  37. blitLine(to, pos, visibleText());
  38. }
  39. CLabel::CLabel(int x, int y, EFonts Font, ETextAlignment Align, const ColorRGBA & Color, const std::string & Text)
  40. : CTextContainer(Align, Font, Color), text(Text)
  41. {
  42. setRedrawParent(true);
  43. autoRedraw = true;
  44. pos.x += x;
  45. pos.y += y;
  46. pos.w = pos.h = 0;
  47. if(alignment == ETextAlignment::TOPLEFT) // causes issues for MIDDLE
  48. {
  49. pos.w = (int)graphics->fonts[font]->getStringWidth(visibleText().c_str());
  50. pos.h = (int)graphics->fonts[font]->getLineHeight();
  51. }
  52. }
  53. Point CLabel::getBorderSize()
  54. {
  55. return Point(0, 0);
  56. }
  57. std::string CLabel::getText()
  58. {
  59. return text;
  60. }
  61. void CLabel::setAutoRedraw(bool value)
  62. {
  63. autoRedraw = value;
  64. }
  65. void CLabel::setText(const std::string & Txt)
  66. {
  67. text = Txt;
  68. if(autoRedraw)
  69. {
  70. if(background || !parent)
  71. redraw();
  72. else
  73. parent->redraw();
  74. }
  75. }
  76. void CLabel::setColor(const ColorRGBA & Color)
  77. {
  78. color = Color;
  79. if(autoRedraw)
  80. {
  81. if(background || !parent)
  82. redraw();
  83. else
  84. parent->redraw();
  85. }
  86. }
  87. size_t CLabel::getWidth()
  88. {
  89. return graphics->fonts[font]->getStringWidth(visibleText());;
  90. }
  91. CMultiLineLabel::CMultiLineLabel(Rect position, EFonts Font, ETextAlignment Align, const ColorRGBA & Color, const std::string & Text) :
  92. CLabel(position.x, position.y, Font, Align, Color, Text),
  93. visibleSize(0, 0, position.w, position.h)
  94. {
  95. pos.w = position.w;
  96. pos.h = position.h;
  97. splitText(Text, true);
  98. }
  99. void CMultiLineLabel::setVisibleSize(Rect visibleSize, bool redrawElement)
  100. {
  101. this->visibleSize = visibleSize;
  102. if(redrawElement)
  103. redraw();
  104. }
  105. void CMultiLineLabel::scrollTextBy(int distance)
  106. {
  107. scrollTextTo(visibleSize.y + distance);
  108. }
  109. void CMultiLineLabel::scrollTextTo(int distance, bool redrawAfterScroll)
  110. {
  111. Rect size = visibleSize;
  112. size.y = distance;
  113. setVisibleSize(size, redrawAfterScroll);
  114. }
  115. void CMultiLineLabel::setText(const std::string & Txt)
  116. {
  117. splitText(Txt, false); //setText used below can handle redraw
  118. CLabel::setText(Txt);
  119. }
  120. void CTextContainer::blitLine(Canvas & to, Rect destRect, std::string what)
  121. {
  122. const auto f = graphics->fonts[font];
  123. Point where = destRect.topLeft();
  124. const std::string delimeters = "{}";
  125. auto delimitersCount = std::count_if(what.cbegin(), what.cend(), [&delimeters](char c)
  126. {
  127. return delimeters.find(c) != std::string::npos;
  128. });
  129. //We should count delimiters length from string to correct centering later.
  130. delimitersCount *= f->getStringWidth(delimeters)/2;
  131. // input is rect in which given text should be placed
  132. // calculate proper position for top-left corner of the text
  133. if(alignment == ETextAlignment::TOPLEFT)
  134. {
  135. where.x += getBorderSize().x;
  136. where.y += getBorderSize().y;
  137. }
  138. if(alignment == ETextAlignment::CENTER)
  139. {
  140. where.x += (int(destRect.w) - int(f->getStringWidth(what) - delimitersCount)) / 2;
  141. where.y += (int(destRect.h) - int(f->getLineHeight())) / 2;
  142. }
  143. if(alignment == ETextAlignment::BOTTOMRIGHT)
  144. {
  145. where.x += getBorderSize().x + destRect.w - ((int)f->getStringWidth(what) - delimitersCount);
  146. where.y += getBorderSize().y + destRect.h - (int)f->getLineHeight();
  147. }
  148. size_t begin = 0;
  149. size_t currDelimeter = 0;
  150. do
  151. {
  152. size_t end = what.find_first_of(delimeters[currDelimeter % 2], begin);
  153. if(begin != end)
  154. {
  155. std::string toPrint = what.substr(begin, end - begin);
  156. if(currDelimeter % 2) // Enclosed in {} text - set to yellow
  157. to.drawText(where, font, Colors::YELLOW, ETextAlignment::TOPLEFT, toPrint);
  158. else // Non-enclosed text, use default color
  159. to.drawText(where, font, color, ETextAlignment::TOPLEFT, toPrint);
  160. begin = end;
  161. where.x += (int)f->getStringWidth(toPrint);
  162. }
  163. currDelimeter++;
  164. } while(begin++ != std::string::npos);
  165. }
  166. CTextContainer::CTextContainer(ETextAlignment alignment, EFonts font, ColorRGBA color) :
  167. alignment(alignment),
  168. font(font),
  169. color(color)
  170. {
  171. }
  172. void CMultiLineLabel::showAll(Canvas & to)
  173. {
  174. CIntObject::showAll(to);
  175. const auto f = graphics->fonts[font];
  176. // calculate which lines should be visible
  177. int totalLines = static_cast<int>(lines.size());
  178. int beginLine = visibleSize.y;
  179. int endLine = getTextLocation().h + visibleSize.y;
  180. if(beginLine < 0)
  181. beginLine = 0;
  182. else
  183. beginLine /= (int)f->getLineHeight();
  184. if(endLine < 0)
  185. endLine = 0;
  186. else
  187. endLine /= (int)f->getLineHeight();
  188. endLine++;
  189. // and where they should be displayed
  190. Point lineStart = getTextLocation().topLeft() - visibleSize + Point(0, beginLine * (int)f->getLineHeight());
  191. Point lineSize = Point(getTextLocation().w, (int)f->getLineHeight());
  192. CSDL_Ext::CClipRectGuard guard(to.getInternalSurface(), getTextLocation()); // to properly trim text that is too big to fit
  193. for(int i = beginLine; i < std::min(totalLines, endLine); i++)
  194. {
  195. if(!lines[i].empty()) //non-empty line
  196. blitLine(to, Rect(lineStart, lineSize), lines[i]);
  197. lineStart.y += (int)f->getLineHeight();
  198. }
  199. }
  200. void CMultiLineLabel::splitText(const std::string & Txt, bool redrawAfter)
  201. {
  202. lines.clear();
  203. const auto f = graphics->fonts[font];
  204. int lineHeight = static_cast<int>(f->getLineHeight());
  205. lines = CMessage::breakText(Txt, pos.w, font);
  206. textSize.y = lineHeight * (int)lines.size();
  207. textSize.x = 0;
  208. for(const std::string & line : lines)
  209. vstd::amax(textSize.x, f->getStringWidth(line.c_str()));
  210. if(redrawAfter)
  211. redraw();
  212. }
  213. Rect CMultiLineLabel::getTextLocation()
  214. {
  215. // this method is needed for vertical alignment alignment of text
  216. // when height of available text is smaller than height of widget
  217. // in this case - we should add proper offset to display text at required position
  218. if(pos.h <= textSize.y)
  219. return pos;
  220. Point textSize(pos.w, (int)graphics->fonts[font]->getLineHeight() * (int)lines.size());
  221. Point textOffset(pos.w - textSize.x, pos.h - textSize.y);
  222. switch(alignment)
  223. {
  224. case ETextAlignment::TOPLEFT: return Rect(pos.topLeft(), textSize);
  225. case ETextAlignment::CENTER: return Rect(pos.topLeft() + textOffset / 2, textSize);
  226. case ETextAlignment::BOTTOMRIGHT: return Rect(pos.topLeft() + textOffset, textSize);
  227. }
  228. assert(0);
  229. return Rect();
  230. }
  231. CLabelGroup::CLabelGroup(EFonts Font, ETextAlignment Align, const ColorRGBA & Color)
  232. : font(Font), align(Align), color(Color)
  233. {
  234. defActions = 255 - DISPOSE;
  235. }
  236. void CLabelGroup::add(int x, int y, const std::string & text)
  237. {
  238. OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255 - DISPOSE);
  239. labels.push_back(std::make_shared<CLabel>(x, y, font, align, color, text));
  240. }
  241. size_t CLabelGroup::currentSize() const
  242. {
  243. return labels.size();
  244. }
  245. CTextBox::CTextBox(std::string Text, const Rect & rect, int SliderStyle, EFonts Font, ETextAlignment Align, const ColorRGBA & Color) :
  246. sliderStyle(SliderStyle),
  247. slider(nullptr)
  248. {
  249. OBJECT_CONSTRUCTION_CAPTURING(255 - DISPOSE);
  250. label = std::make_shared<CMultiLineLabel>(rect, Font, Align, Color);
  251. setRedrawParent(true);
  252. pos.x += rect.x;
  253. pos.y += rect.y;
  254. pos.h = rect.h;
  255. pos.w = rect.w;
  256. assert(pos.w >= 40); //we need some space
  257. setText(Text);
  258. }
  259. void CTextBox::sliderMoved(int to)
  260. {
  261. label->scrollTextTo(to);
  262. }
  263. void CTextBox::resize(Point newSize)
  264. {
  265. pos.w = newSize.x;
  266. pos.h = newSize.y;
  267. label->pos.w = pos.w;
  268. label->pos.h = pos.h;
  269. slider.reset();
  270. setText(label->getText()); // force refresh
  271. }
  272. void CTextBox::setText(const std::string & text)
  273. {
  274. label->pos.w = pos.w; // reset to default before textSize.y check
  275. label->setText(text);
  276. if(label->textSize.y <= label->pos.h && slider)
  277. {
  278. // slider is no longer needed
  279. slider.reset();
  280. }
  281. else if(slider)
  282. {
  283. // decrease width again if slider still used
  284. label->pos.w = pos.w - 32;
  285. label->setText(text);
  286. slider->setAmount(label->textSize.y);
  287. }
  288. else if(label->textSize.y > label->pos.h)
  289. {
  290. // create slider and update widget
  291. label->pos.w = pos.w - 32;
  292. label->setText(text);
  293. OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255 - DISPOSE);
  294. slider = std::make_shared<CSlider>(Point(pos.w - 32, 0), pos.h, std::bind(&CTextBox::sliderMoved, this, _1),
  295. label->pos.h, label->textSize.y, 0, Orientation::VERTICAL, CSlider::EStyle(sliderStyle));
  296. slider->setScrollStep((int)graphics->fonts[label->font]->getLineHeight());
  297. slider->setPanningStep(1);
  298. slider->setScrollBounds(pos - slider->pos.topLeft());
  299. }
  300. }
  301. void CGStatusBar::setEnteringMode(bool on)
  302. {
  303. consoleText.clear();
  304. if (on)
  305. {
  306. //assert(enteringText == false);
  307. alignment = ETextAlignment::TOPLEFT;
  308. GH.startTextInput(pos);
  309. setText(consoleText);
  310. }
  311. else
  312. {
  313. //assert(enteringText == true);
  314. alignment = ETextAlignment::CENTER;
  315. GH.stopTextInput();
  316. setText(hoverText);
  317. }
  318. enteringText = on;
  319. }
  320. void CGStatusBar::setEnteredText(const std::string & text)
  321. {
  322. assert(enteringText == true);
  323. consoleText = text;
  324. setText(text);
  325. }
  326. void CGStatusBar::write(const std::string & Text)
  327. {
  328. hoverText = Text;
  329. if (enteringText == false)
  330. setText(hoverText);
  331. }
  332. void CGStatusBar::clearIfMatching(const std::string & Text)
  333. {
  334. if (hoverText == Text)
  335. clear();
  336. }
  337. void CGStatusBar::clear()
  338. {
  339. write({});
  340. }
  341. CGStatusBar::CGStatusBar(std::shared_ptr<CIntObject> background_, EFonts Font, ETextAlignment Align, const ColorRGBA & Color)
  342. : CLabel(background_->pos.x, background_->pos.y, Font, Align, Color, "")
  343. , enteringText(false)
  344. {
  345. addUsedEvents(LCLICK);
  346. background = background_;
  347. addChild(background.get());
  348. pos = background->pos;
  349. getBorderSize();
  350. autoRedraw = false;
  351. }
  352. CGStatusBar::CGStatusBar(int x, int y, std::string name, int maxw)
  353. : CLabel(x, y, FONT_SMALL, ETextAlignment::CENTER)
  354. , enteringText(false)
  355. {
  356. addUsedEvents(LCLICK);
  357. OBJECT_CONSTRUCTION_CAPTURING(255 - DISPOSE);
  358. auto backgroundImage = std::make_shared<CPicture>(name);
  359. background = backgroundImage;
  360. pos = background->pos;
  361. if((unsigned)maxw < (unsigned)pos.w) //(insigned)-1 > than any correct value of pos.w
  362. {
  363. //execution of this block when maxw is incorrect breaks text centralization (issue #3151)
  364. vstd::amin(pos.w, maxw);
  365. backgroundImage->srcRect = Rect(0, 0, maxw, pos.h);
  366. }
  367. autoRedraw = false;
  368. }
  369. CGStatusBar::~CGStatusBar()
  370. {
  371. assert(GH.statusbar().get() != this);
  372. }
  373. void CGStatusBar::show(Canvas & to)
  374. {
  375. showAll(to);
  376. }
  377. void CGStatusBar::clickPressed(const Point & cursorPosition)
  378. {
  379. if(LOCPLINT && LOCPLINT->cingconsole->isActive())
  380. LOCPLINT->cingconsole->startEnteringText();
  381. }
  382. void CGStatusBar::activate()
  383. {
  384. GH.setStatusbar(shared_from_this());
  385. CIntObject::activate();
  386. }
  387. void CGStatusBar::deactivate()
  388. {
  389. assert(GH.statusbar().get() == this);
  390. GH.setStatusbar(nullptr);
  391. if (enteringText)
  392. LOCPLINT->cingconsole->endEnteringText(false);
  393. CIntObject::deactivate();
  394. }
  395. Point CGStatusBar::getBorderSize()
  396. {
  397. //Width of borders where text should not be printed
  398. static const Point borderSize(5, 1);
  399. switch(alignment)
  400. {
  401. case ETextAlignment::TOPLEFT: return Point(borderSize.x, borderSize.y);
  402. case ETextAlignment::CENTER: return Point(pos.w / 2, pos.h / 2);
  403. case ETextAlignment::BOTTOMRIGHT: return Point(pos.w - borderSize.x, pos.h - borderSize.y);
  404. }
  405. assert(0);
  406. return Point();
  407. }
  408. CTextInput::CTextInput(const Rect & Pos, EFonts font, const CFunctionList<void(const std::string &)> & CB)
  409. : CLabel(Pos.x, Pos.y, font, ETextAlignment::CENTER),
  410. cb(CB),
  411. CFocusable(std::make_shared<CKeyboardFocusListener>(this))
  412. {
  413. setRedrawParent(true);
  414. pos.h = Pos.h;
  415. pos.w = Pos.w;
  416. background.reset();
  417. addUsedEvents(LCLICK | KEYBOARD | TEXTINPUT);
  418. #if !defined(VCMI_MOBILE)
  419. giveFocus();
  420. #endif
  421. }
  422. CTextInput::CTextInput(const Rect & Pos, const Point & bgOffset, const std::string & bgName, const CFunctionList<void(const std::string &)> & CB)
  423. :cb(CB), CFocusable(std::make_shared<CKeyboardFocusListener>(this))
  424. {
  425. pos += Pos.topLeft();
  426. pos.h = Pos.h;
  427. pos.w = Pos.w;
  428. OBJ_CONSTRUCTION;
  429. background = std::make_shared<CPicture>(bgName, bgOffset.x, bgOffset.y);
  430. addUsedEvents(LCLICK | KEYBOARD | TEXTINPUT);
  431. #if !defined(VCMI_MOBILE)
  432. giveFocus();
  433. #endif
  434. }
  435. CTextInput::CTextInput(const Rect & Pos, std::shared_ptr<IImage> srf)
  436. :CFocusable(std::make_shared<CKeyboardFocusListener>(this))
  437. {
  438. pos += Pos.topLeft();
  439. OBJ_CONSTRUCTION;
  440. background = std::make_shared<CPicture>(srf, Pos);
  441. pos.w = background->pos.w;
  442. pos.h = background->pos.h;
  443. background->pos = pos;
  444. addUsedEvents(LCLICK | KEYBOARD | TEXTINPUT);
  445. #if !defined(VCMI_MOBILE)
  446. giveFocus();
  447. #endif
  448. }
  449. std::atomic<int> CKeyboardFocusListener::usageIndex(0);
  450. CKeyboardFocusListener::CKeyboardFocusListener(CTextInput * textInput)
  451. :textInput(textInput)
  452. {
  453. }
  454. void CKeyboardFocusListener::focusGot()
  455. {
  456. GH.startTextInput(textInput->pos);
  457. usageIndex++;
  458. }
  459. void CKeyboardFocusListener::focusLost()
  460. {
  461. if(0 == --usageIndex)
  462. {
  463. GH.stopTextInput();
  464. }
  465. }
  466. std::string CTextInput::visibleText()
  467. {
  468. return focus ? text + newText + "_" : text;
  469. }
  470. void CTextInput::clickPressed(const Point & cursorPosition)
  471. {
  472. if(!focus)
  473. giveFocus();
  474. }
  475. void CTextInput::keyPressed(EShortcut key)
  476. {
  477. if(!focus)
  478. return;
  479. if(key == EShortcut::GLOBAL_MOVE_FOCUS)
  480. {
  481. moveFocus();
  482. return;
  483. }
  484. bool redrawNeeded = false;
  485. switch(key)
  486. {
  487. case EShortcut::GLOBAL_BACKSPACE:
  488. if(!newText.empty())
  489. {
  490. TextOperations::trimRightUnicode(newText);
  491. redrawNeeded = true;
  492. }
  493. else if(!text.empty())
  494. {
  495. TextOperations::trimRightUnicode(text);
  496. redrawNeeded = true;
  497. }
  498. break;
  499. default:
  500. break;
  501. }
  502. if(redrawNeeded)
  503. {
  504. redraw();
  505. cb(text);
  506. }
  507. }
  508. void CTextInput::setText(const std::string & nText)
  509. {
  510. setText(nText, false);
  511. }
  512. void CTextInput::setText(const std::string & nText, bool callCb)
  513. {
  514. CLabel::setText(nText);
  515. if(callCb)
  516. cb(text);
  517. }
  518. void CTextInput::textInputed(const std::string & enteredText)
  519. {
  520. if(!focus)
  521. return;
  522. std::string oldText = text;
  523. text += enteredText;
  524. filters(text, oldText);
  525. if(text != oldText)
  526. {
  527. redraw();
  528. cb(text);
  529. }
  530. newText.clear();
  531. }
  532. void CTextInput::textEdited(const std::string & enteredText)
  533. {
  534. if(!focus)
  535. return;
  536. newText = enteredText;
  537. redraw();
  538. cb(text + newText);
  539. }
  540. void CTextInput::filenameFilter(std::string & text, const std::string &)
  541. {
  542. static const std::string forbiddenChars = "<>:\"/\\|?*\r\n"; //if we are entering a filename, some special characters won't be allowed
  543. size_t pos;
  544. while((pos = text.find_first_of(forbiddenChars)) != std::string::npos)
  545. text.erase(pos, 1);
  546. }
  547. void CTextInput::numberFilter(std::string & text, const std::string & oldText, int minValue, int maxValue)
  548. {
  549. assert(minValue < maxValue);
  550. if(text.empty())
  551. text = "0";
  552. size_t pos = 0;
  553. if(text[0] == '-') //allow '-' sign as first symbol only
  554. pos++;
  555. while(pos < text.size())
  556. {
  557. if(text[pos] < '0' || text[pos] > '9')
  558. {
  559. text = oldText;
  560. return; //new text is not number.
  561. }
  562. pos++;
  563. }
  564. try
  565. {
  566. int value = boost::lexical_cast<int>(text);
  567. if(value < minValue)
  568. text = std::to_string(minValue);
  569. else if(value > maxValue)
  570. text = std::to_string(maxValue);
  571. }
  572. catch(boost::bad_lexical_cast &)
  573. {
  574. //Should never happen. Unless I missed some cases
  575. logGlobal->warn("Warning: failed to convert %s to number!", text);
  576. text = oldText;
  577. }
  578. }
  579. CFocusable::CFocusable()
  580. :CFocusable(std::make_shared<IFocusListener>())
  581. {
  582. }
  583. CFocusable::CFocusable(std::shared_ptr<IFocusListener> focusListener)
  584. : focusListener(focusListener)
  585. {
  586. focus = false;
  587. focusables.push_back(this);
  588. }
  589. CFocusable::~CFocusable()
  590. {
  591. if(hasFocus())
  592. {
  593. inputWithFocus = nullptr;
  594. focusListener->focusLost();
  595. }
  596. focusables -= this;
  597. }
  598. bool CFocusable::hasFocus() const
  599. {
  600. return inputWithFocus == this;
  601. }
  602. void CFocusable::giveFocus()
  603. {
  604. focus = true;
  605. focusListener->focusGot();
  606. redraw();
  607. if(inputWithFocus)
  608. {
  609. inputWithFocus->focus = false;
  610. inputWithFocus->focusListener->focusLost();
  611. inputWithFocus->redraw();
  612. }
  613. inputWithFocus = this;
  614. }
  615. void CFocusable::moveFocus()
  616. {
  617. auto i = vstd::find(focusables, this),
  618. ourIt = i;
  619. for(i++; i != ourIt; i++)
  620. {
  621. if(i == focusables.end())
  622. i = focusables.begin();
  623. if((*i)->isActive())
  624. {
  625. (*i)->giveFocus();
  626. break;
  627. }
  628. }
  629. }