BsGUIInputBox.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878
  1. #include "BsGUIInputBox.h"
  2. #include "BsGUIManager.h"
  3. #include "BsImageSprite.h"
  4. #include "BsGUIWidget.h"
  5. #include "BsGUISkin.h"
  6. #include "BsSpriteTexture.h"
  7. #include "BsTextSprite.h"
  8. #include "BsGUILayoutOptions.h"
  9. #include "BsGUIButtonEvent.h"
  10. #include "BsGUIMouseEvent.h"
  11. #include "BsGUICommandEvent.h"
  12. #include "CmFont.h"
  13. #include "CmTextUtility.h"
  14. #include "CmTexture.h"
  15. #include "CmPlatform.h"
  16. #include "BsGUIInputCaret.h"
  17. #include "BsGUIInputSelection.h"
  18. #include "BsDragAndDropManager.h"
  19. #include "BsGUIContextMenu.h"
  20. #include "BsGUIHelper.h"
  21. using namespace CamelotFramework;
  22. namespace BansheeEngine
  23. {
  24. const String& GUIInputBox::getGUITypeName()
  25. {
  26. static String name = "InputBox";
  27. return name;
  28. }
  29. GUIInputBox::GUIInputBox(GUIWidget& parent, const GUIElementStyle* style, const GUILayoutOptions& layoutOptions, bool multiline)
  30. :GUIElement(parent, style, layoutOptions), mInputCursorSet(false), mDragInProgress(false),
  31. mCaretShown(false), mSelectionShown(false), mIsMultiline(multiline), mHasFocus(false)
  32. {
  33. mImageSprite = cm_new<ImageSprite, PoolAlloc>();
  34. mTextSprite = cm_new<TextSprite, PoolAlloc>();
  35. mImageDesc.texture = mStyle->normal.texture;
  36. if(mImageDesc.texture != nullptr)
  37. {
  38. mImageDesc.width = mImageDesc.texture->getTexture()->getWidth();
  39. mImageDesc.height = mImageDesc.texture->getTexture()->getHeight();
  40. }
  41. mImageDesc.borderLeft = mStyle->border.left;
  42. mImageDesc.borderRight = mStyle->border.right;
  43. mImageDesc.borderTop = mStyle->border.top;
  44. mImageDesc.borderBottom = mStyle->border.bottom;
  45. }
  46. GUIInputBox::~GUIInputBox()
  47. {
  48. cm_delete<PoolAlloc>(mTextSprite);
  49. cm_delete<PoolAlloc>(mImageSprite);
  50. }
  51. GUIInputBox* GUIInputBox::create(GUIWidget& parent, bool multiline, const GUIElementStyle* style)
  52. {
  53. if(style == nullptr)
  54. {
  55. const GUISkin& skin = parent.getSkin();
  56. style = skin.getStyle(getGUITypeName());
  57. }
  58. return new (cm_alloc<GUIInputBox, PoolAlloc>()) GUIInputBox(parent, style, getDefaultLayoutOptions(style), multiline);
  59. }
  60. GUIInputBox* GUIInputBox::create(GUIWidget& parent, const GUILayoutOptions& layoutOptions, bool multiline, const GUIElementStyle* style)
  61. {
  62. if(style == nullptr)
  63. {
  64. const GUISkin& skin = parent.getSkin();
  65. style = skin.getStyle(getGUITypeName());
  66. }
  67. return new (cm_alloc<GUIInputBox, PoolAlloc>()) GUIInputBox(parent, style, layoutOptions, multiline);
  68. }
  69. UINT32 GUIInputBox::getNumRenderElements() const
  70. {
  71. UINT32 numElements = mImageSprite->getNumRenderElements();
  72. numElements += mTextSprite->getNumRenderElements();
  73. if(mCaretShown && gGUIManager().getCaretBlinkState())
  74. numElements += gGUIManager().getInputCaretTool()->getSprite()->getNumRenderElements();
  75. if(mSelectionShown)
  76. {
  77. const Vector<ImageSprite*>::type& sprites = gGUIManager().getInputSelectionTool()->getSprites();
  78. for(auto& selectionSprite : sprites)
  79. {
  80. numElements += selectionSprite->getNumRenderElements();
  81. }
  82. }
  83. return numElements;
  84. }
  85. const HMaterial& GUIInputBox::getMaterial(UINT32 renderElementIdx) const
  86. {
  87. UINT32 localRenderElementIdx;
  88. Sprite* sprite = renderElemToSprite(renderElementIdx, localRenderElementIdx);
  89. return sprite->getMaterial(localRenderElementIdx);
  90. }
  91. UINT32 GUIInputBox::getNumQuads(UINT32 renderElementIdx) const
  92. {
  93. UINT32 localRenderElementIdx;
  94. Sprite* sprite = renderElemToSprite(renderElementIdx, localRenderElementIdx);
  95. return sprite->getNumQuads(localRenderElementIdx);
  96. }
  97. void GUIInputBox::updateRenderElementsInternal()
  98. {
  99. mImageDesc.width = mWidth;
  100. mImageDesc.height = mHeight;
  101. mImageSprite->update(mImageDesc);
  102. TEXT_SPRITE_DESC textDesc = getTextDesc();
  103. mTextSprite->update(textDesc);
  104. if(mCaretShown && gGUIManager().getCaretBlinkState())
  105. {
  106. gGUIManager().getInputCaretTool()->updateText(this, textDesc); // TODO - These shouldn't be here. Only call this when one of these parameters changes.
  107. gGUIManager().getInputCaretTool()->updateSprite();
  108. }
  109. if(mSelectionShown)
  110. {
  111. gGUIManager().getInputSelectionTool()->updateText(this, textDesc); // TODO - These shouldn't be here. Only call this when one of these parameters changes.
  112. gGUIManager().getInputSelectionTool()->updateSprite();
  113. }
  114. GUIElement::updateRenderElementsInternal();
  115. }
  116. void GUIInputBox::updateClippedBounds()
  117. {
  118. mClippedBounds = mImageSprite->getBounds(mOffset, mClipRect);
  119. }
  120. Sprite* GUIInputBox::renderElemToSprite(UINT32 renderElemIdx, UINT32& localRenderElemIdx) const
  121. {
  122. UINT32 oldNumElements = 0;
  123. UINT32 newNumElements = oldNumElements + mTextSprite->getNumRenderElements();
  124. if(renderElemIdx < newNumElements)
  125. {
  126. localRenderElemIdx = renderElemIdx - oldNumElements;
  127. return mTextSprite;
  128. }
  129. oldNumElements = newNumElements;
  130. newNumElements += mImageSprite->getNumRenderElements();
  131. if(renderElemIdx < newNumElements)
  132. {
  133. localRenderElemIdx = renderElemIdx - oldNumElements;
  134. return mImageSprite;
  135. }
  136. if(mCaretShown && gGUIManager().getCaretBlinkState())
  137. {
  138. oldNumElements = newNumElements;
  139. newNumElements += gGUIManager().getInputCaretTool()->getSprite()->getNumRenderElements();
  140. if(renderElemIdx < newNumElements)
  141. {
  142. localRenderElemIdx = renderElemIdx - oldNumElements;
  143. return gGUIManager().getInputCaretTool()->getSprite();
  144. }
  145. }
  146. if(mSelectionShown)
  147. {
  148. const Vector<ImageSprite*>::type& sprites = gGUIManager().getInputSelectionTool()->getSprites();
  149. for(auto& selectionSprite : sprites)
  150. {
  151. oldNumElements = newNumElements;
  152. newNumElements += selectionSprite->getNumRenderElements();
  153. if(renderElemIdx < newNumElements)
  154. {
  155. localRenderElemIdx = renderElemIdx - oldNumElements;
  156. return selectionSprite;
  157. }
  158. }
  159. }
  160. localRenderElemIdx = renderElemIdx;
  161. return nullptr;
  162. }
  163. Int2 GUIInputBox::renderElemToOffset(UINT32 renderElemIdx) const
  164. {
  165. UINT32 oldNumElements = 0;
  166. UINT32 newNumElements = oldNumElements + mTextSprite->getNumRenderElements();
  167. if(renderElemIdx < newNumElements)
  168. return getTextOffset();
  169. oldNumElements = newNumElements;
  170. newNumElements += mImageSprite->getNumRenderElements();
  171. if(renderElemIdx < newNumElements)
  172. return mOffset;
  173. if(mCaretShown && gGUIManager().getCaretBlinkState())
  174. {
  175. oldNumElements = newNumElements;
  176. newNumElements += gGUIManager().getInputCaretTool()->getSprite()->getNumRenderElements();
  177. if(renderElemIdx < newNumElements)
  178. return gGUIManager().getInputCaretTool()->getSpriteOffset();
  179. }
  180. if(mSelectionShown)
  181. {
  182. UINT32 spriteIdx = 0;
  183. const Vector<ImageSprite*>::type& sprites = gGUIManager().getInputSelectionTool()->getSprites();
  184. for(auto& selectionSprite : sprites)
  185. {
  186. oldNumElements = newNumElements;
  187. newNumElements += selectionSprite->getNumRenderElements();
  188. if(renderElemIdx < newNumElements)
  189. return gGUIManager().getInputSelectionTool()->getSelectionSpriteOffset(spriteIdx);
  190. spriteIdx++;
  191. }
  192. }
  193. return Int2();
  194. }
  195. Rect GUIInputBox::renderElemToClipRect(UINT32 renderElemIdx) const
  196. {
  197. UINT32 oldNumElements = 0;
  198. UINT32 newNumElements = oldNumElements + mTextSprite->getNumRenderElements();
  199. if(renderElemIdx < newNumElements)
  200. return getTextClipRect();
  201. oldNumElements = newNumElements;
  202. newNumElements += mImageSprite->getNumRenderElements();
  203. if(renderElemIdx < newNumElements)
  204. return mClipRect;
  205. if(mCaretShown && gGUIManager().getCaretBlinkState())
  206. {
  207. oldNumElements = newNumElements;
  208. newNumElements += gGUIManager().getInputCaretTool()->getSprite()->getNumRenderElements();
  209. if(renderElemIdx < newNumElements)
  210. {
  211. return gGUIManager().getInputCaretTool()->getSpriteClipRect(getTextClipRect());
  212. }
  213. }
  214. if(mSelectionShown)
  215. {
  216. UINT32 spriteIdx = 0;
  217. const Vector<ImageSprite*>::type& sprites = gGUIManager().getInputSelectionTool()->getSprites();
  218. for(auto& selectionSprite : sprites)
  219. {
  220. oldNumElements = newNumElements;
  221. newNumElements += selectionSprite->getNumRenderElements();
  222. if(renderElemIdx < newNumElements)
  223. return gGUIManager().getInputSelectionTool()->getSelectionSpriteClipRect(spriteIdx, getTextClipRect());
  224. spriteIdx++;
  225. }
  226. }
  227. return Rect();
  228. }
  229. UINT32 GUIInputBox::_getOptimalWidth() const
  230. {
  231. UINT32 imageWidth = 0;
  232. if(mImageDesc.texture != nullptr)
  233. imageWidth = mImageDesc.texture->getTexture()->getWidth();
  234. return std::max(imageWidth, (UINT32)GUIHelper::calcOptimalContentsSize(mText, *mStyle, _getLayoutOptions()).x);
  235. }
  236. UINT32 GUIInputBox::_getOptimalHeight() const
  237. {
  238. UINT32 imageHeight = 0;
  239. if(mImageDesc.texture != nullptr)
  240. imageHeight = mImageDesc.texture->getTexture()->getHeight();
  241. return std::max(imageHeight, (UINT32)GUIHelper::calcOptimalContentsSize(mText, *mStyle, _getLayoutOptions()).y);
  242. }
  243. CM::Int2 GUIInputBox::_getTextInputOffset() const
  244. {
  245. return mTextOffset;
  246. }
  247. CM::Rect GUIInputBox::_getTextInputRect() const
  248. {
  249. Rect textBounds = getContentBounds();
  250. textBounds.x -= mOffset.x;
  251. textBounds.y -= mOffset.y;
  252. return textBounds;
  253. }
  254. UINT32 GUIInputBox::_getRenderElementDepth(UINT32 renderElementIdx) const
  255. {
  256. UINT32 localRenderElementIdx;
  257. Sprite* sprite = renderElemToSprite(renderElementIdx, localRenderElementIdx);
  258. if(sprite == mImageSprite)
  259. return _getDepth();
  260. else if(sprite == mTextSprite)
  261. return _getDepth() - 2;
  262. else if(sprite == gGUIManager().getInputCaretTool()->getSprite())
  263. return _getDepth() - 3;
  264. else // Selection sprites
  265. return _getDepth() - 1;
  266. }
  267. void GUIInputBox::fillBuffer(UINT8* vertices, UINT8* uv, UINT32* indices, UINT32 startingQuad, UINT32 maxNumQuads,
  268. UINT32 vertexStride, UINT32 indexStride, UINT32 renderElementIdx) const
  269. {
  270. UINT32 localRenderElementIdx;
  271. Sprite* sprite = renderElemToSprite(renderElementIdx, localRenderElementIdx);
  272. Int2 offset = renderElemToOffset(renderElementIdx);
  273. Rect clipRect = renderElemToClipRect(renderElementIdx);
  274. sprite->fillBuffer(vertices, uv, indices, startingQuad, maxNumQuads, vertexStride, indexStride, localRenderElementIdx, offset, clipRect);
  275. }
  276. bool GUIInputBox::mouseEvent(const GUIMouseEvent& ev)
  277. {
  278. static UINT32 dbg = 0;
  279. if(ev.getType() == GUIMouseEventType::MouseOver)
  280. {
  281. if(!mHasFocus)
  282. {
  283. mImageDesc.texture = mStyle->hover.texture;
  284. markContentAsDirty();
  285. }
  286. if(!mInputCursorSet)
  287. {
  288. Platform::setCursor(CursorType::IBeam);
  289. mInputCursorSet = true;
  290. }
  291. return true;
  292. }
  293. else if(ev.getType() == GUIMouseEventType::MouseOut)
  294. {
  295. if(!mHasFocus)
  296. {
  297. mImageDesc.texture = mStyle->normal.texture;
  298. markContentAsDirty();
  299. }
  300. if(!mDragInProgress && mInputCursorSet)
  301. {
  302. Platform::setCursor(CursorType::Arrow);
  303. mInputCursorSet = false;
  304. }
  305. return true;
  306. }
  307. else if(ev.getType() == GUIMouseEventType::MouseDown)
  308. {
  309. if(mHasFocus)
  310. {
  311. if(ev.isShiftDown())
  312. {
  313. if(!mSelectionShown)
  314. showSelection(gGUIManager().getInputCaretTool()->getCaretPos());
  315. }
  316. else
  317. clearSelection();
  318. if(mText.size() > 0)
  319. gGUIManager().getInputCaretTool()->moveCaretToPos(ev.getPosition());
  320. else
  321. gGUIManager().getInputCaretTool()->moveCaretToStart();
  322. if(ev.isShiftDown())
  323. gGUIManager().getInputSelectionTool()->moveSelectionToCaret(gGUIManager().getInputCaretTool()->getCaretPos());
  324. }
  325. else
  326. {
  327. clearSelection();
  328. showCaret();
  329. if(mText.size() > 0)
  330. gGUIManager().getInputCaretTool()->moveCaretToPos(ev.getPosition());
  331. else
  332. gGUIManager().getInputCaretTool()->moveCaretToStart();
  333. }
  334. scrollTextToCaret();
  335. markContentAsDirty();
  336. return true;
  337. }
  338. else if(ev.getType() == GUIMouseEventType::MouseDragStart)
  339. {
  340. if(!ev.isShiftDown())
  341. {
  342. mDragInProgress = true;
  343. UINT32 caretPos = gGUIManager().getInputCaretTool()->getCaretPos();
  344. showSelection(caretPos);
  345. gGUIManager().getInputSelectionTool()->selectionDragStart(caretPos);
  346. return true;
  347. }
  348. }
  349. else if(ev.getType() == GUIMouseEventType::MouseDragEnd)
  350. {
  351. if(!ev.isShiftDown())
  352. {
  353. mDragInProgress = false;
  354. if(ev.getMouseOverElement() != this && mInputCursorSet)
  355. {
  356. Platform::setCursor(CursorType::Arrow);
  357. mInputCursorSet = false;
  358. }
  359. gGUIManager().getInputSelectionTool()->selectionDragEnd();
  360. return true;
  361. }
  362. }
  363. else if(ev.getType() == GUIMouseEventType::MouseDrag)
  364. {
  365. if(!ev.isShiftDown())
  366. {
  367. if(mText.size() > 0)
  368. gGUIManager().getInputCaretTool()->moveCaretToPos(ev.getPosition());
  369. else
  370. gGUIManager().getInputCaretTool()->moveCaretToStart();
  371. gGUIManager().getInputSelectionTool()->selectionDragUpdate(gGUIManager().getInputCaretTool()->getCaretPos());
  372. scrollTextToCaret();
  373. markContentAsDirty();
  374. return true;
  375. }
  376. }
  377. return false;
  378. }
  379. bool GUIInputBox::keyEvent(const GUIKeyEvent& ev)
  380. {
  381. if(ev.getType() == GUIKeyEventType::KeyDown)
  382. {
  383. if(ev.getKey() == BC_BACK)
  384. {
  385. if(mText.size() > 0)
  386. {
  387. if(mSelectionShown)
  388. {
  389. deleteSelectedText();
  390. }
  391. else
  392. {
  393. UINT32 charIdx = gGUIManager().getInputCaretTool()->getCharIdxAtCaretPos() - 1;
  394. if(charIdx < (UINT32)mText.size())
  395. {
  396. eraseChar(charIdx);
  397. if(charIdx > 0)
  398. charIdx--;
  399. gGUIManager().getInputCaretTool()->moveCaretToChar(charIdx, CARET_AFTER);
  400. scrollTextToCaret();
  401. }
  402. }
  403. markContentAsDirty();
  404. }
  405. return true;
  406. }
  407. if(ev.getKey() == BC_DELETE)
  408. {
  409. if(mText.size() > 0)
  410. {
  411. if(mSelectionShown)
  412. {
  413. deleteSelectedText();
  414. }
  415. else
  416. {
  417. UINT32 charIdx = gGUIManager().getInputCaretTool()->getCharIdxAtCaretPos();
  418. if(charIdx < (UINT32)mText.size())
  419. {
  420. eraseChar(charIdx);
  421. if(charIdx > 0)
  422. charIdx--;
  423. gGUIManager().getInputCaretTool()->moveCaretToChar(charIdx, CARET_AFTER);
  424. scrollTextToCaret();
  425. }
  426. }
  427. markContentAsDirty();
  428. }
  429. return true;
  430. }
  431. if(ev.getKey() == BC_LEFT)
  432. {
  433. if(ev.isShiftDown())
  434. {
  435. if(!mSelectionShown)
  436. showSelection(gGUIManager().getInputCaretTool()->getCaretPos());
  437. }
  438. else
  439. clearSelection();
  440. gGUIManager().getInputCaretTool()->moveCaretLeft();
  441. if(ev.isShiftDown())
  442. gGUIManager().getInputSelectionTool()->moveSelectionToCaret(gGUIManager().getInputCaretTool()->getCaretPos());
  443. scrollTextToCaret();
  444. markContentAsDirty();
  445. return true;
  446. }
  447. if(ev.getKey() == BC_RIGHT)
  448. {
  449. if(ev.isShiftDown())
  450. {
  451. if(!mSelectionShown)
  452. showSelection(gGUIManager().getInputCaretTool()->getCaretPos());
  453. }
  454. else
  455. clearSelection();
  456. gGUIManager().getInputCaretTool()->moveCaretRight();
  457. if(ev.isShiftDown())
  458. gGUIManager().getInputSelectionTool()->moveSelectionToCaret(gGUIManager().getInputCaretTool()->getCaretPos());
  459. scrollTextToCaret();
  460. markContentAsDirty();
  461. return true;
  462. }
  463. if(ev.getKey() == BC_UP)
  464. {
  465. if(ev.isShiftDown())
  466. {
  467. if(!mSelectionShown)
  468. showSelection(gGUIManager().getInputCaretTool()->getCaretPos());
  469. }
  470. else
  471. clearSelection();
  472. gGUIManager().getInputCaretTool()->moveCaretUp();
  473. if(ev.isShiftDown())
  474. gGUIManager().getInputSelectionTool()->moveSelectionToCaret(gGUIManager().getInputCaretTool()->getCaretPos());
  475. scrollTextToCaret();
  476. markContentAsDirty();
  477. return true;
  478. }
  479. if(ev.getKey() == BC_DOWN)
  480. {
  481. if(ev.isShiftDown())
  482. {
  483. if(!mSelectionShown)
  484. showSelection(gGUIManager().getInputCaretTool()->getCaretPos());
  485. }
  486. else
  487. clearSelection();
  488. gGUIManager().getInputCaretTool()->moveCaretDown();
  489. if(ev.isShiftDown())
  490. gGUIManager().getInputSelectionTool()->moveSelectionToCaret(gGUIManager().getInputCaretTool()->getCaretPos());
  491. scrollTextToCaret();
  492. markContentAsDirty();
  493. return true;
  494. }
  495. if(ev.getKey() == BC_RETURN)
  496. {
  497. if(mIsMultiline)
  498. {
  499. if(mSelectionShown)
  500. deleteSelectedText();
  501. insertChar(gGUIManager().getInputCaretTool()->getCharIdxAtCaretPos(), '\n');
  502. gGUIManager().getInputCaretTool()->moveCaretRight();
  503. scrollTextToCaret();
  504. markContentAsDirty();
  505. return true;
  506. }
  507. }
  508. if(ev.getKey() == BC_A && ev.isCtrlDown())
  509. {
  510. showSelection(0);
  511. gGUIManager().getInputSelectionTool()->selectAll();
  512. markContentAsDirty();
  513. return true;
  514. }
  515. }
  516. else if(ev.getType() == GUIKeyEventType::TextInput)
  517. {
  518. if(mSelectionShown)
  519. deleteSelectedText();
  520. UINT32 charIdx = gGUIManager().getInputCaretTool()->getCharIdxAtCaretPos();
  521. insertChar(charIdx, ev.getInputChar());
  522. gGUIManager().getInputCaretTool()->moveCaretToChar(charIdx, CARET_AFTER);
  523. scrollTextToCaret();
  524. markContentAsDirty();
  525. return true;
  526. }
  527. return false;
  528. }
  529. bool GUIInputBox::commandEvent(const GUICommandEvent& ev)
  530. {
  531. if(ev.getType() == GUICommandEventType::Redraw)
  532. {
  533. markMeshAsDirty();
  534. return true;
  535. }
  536. return false;
  537. }
  538. void GUIInputBox::showCaret()
  539. {
  540. mCaretShown = true;
  541. TEXT_SPRITE_DESC textDesc = getTextDesc();
  542. Int2 offset = getTextOffset();
  543. gGUIManager().getInputCaretTool()->updateText(this, textDesc);
  544. markContentAsDirty();
  545. }
  546. void GUIInputBox::hideCaret()
  547. {
  548. mCaretShown = false;
  549. markContentAsDirty();
  550. }
  551. void GUIInputBox::showSelection(CM::UINT32 anchorCaretPos)
  552. {
  553. TEXT_SPRITE_DESC textDesc = getTextDesc();
  554. Int2 offset = getTextOffset();
  555. gGUIManager().getInputSelectionTool()->updateText(this, textDesc);
  556. gGUIManager().getInputSelectionTool()->showSelection(anchorCaretPos);
  557. mSelectionShown = true;
  558. markContentAsDirty();
  559. }
  560. void GUIInputBox::clearSelection()
  561. {
  562. gGUIManager().getInputSelectionTool()->clearSelection();
  563. mSelectionShown = false;
  564. markContentAsDirty();
  565. }
  566. void GUIInputBox::scrollTextToCaret()
  567. {
  568. TEXT_SPRITE_DESC textDesc = getTextDesc();
  569. Int2 textOffset = getTextOffset();
  570. Int2 caretPos = gGUIManager().getInputCaretTool()->getCaretPosition(textOffset);
  571. UINT32 caretHeight = gGUIManager().getInputCaretTool()->getCaretHeight();
  572. UINT32 caretWidth = 1;
  573. INT32 caretRight = caretPos.x + (INT32)caretWidth;
  574. INT32 caretBottom = caretPos.y + (INT32)caretHeight;
  575. INT32 left = textOffset.x - mTextOffset.x;
  576. // Include caret width here because we don't want to scroll if just the caret is outside the bounds
  577. // (Possible if the text width is exactly the maximum width)
  578. INT32 right = left + (INT32)textDesc.width + caretWidth;
  579. INT32 top = textOffset.y - mTextOffset.y;
  580. INT32 bottom = top + (INT32)textDesc.height;
  581. Int2 offset;
  582. if(caretPos.x < left)
  583. {
  584. offset.x = left - caretPos.x;
  585. }
  586. else if(caretRight > right)
  587. {
  588. offset.x = -(caretRight - right);
  589. }
  590. if(caretPos.y < top)
  591. {
  592. offset.y = top - caretPos.y;
  593. }
  594. else if(caretBottom > bottom)
  595. {
  596. offset.y = -(caretBottom - bottom);
  597. }
  598. mTextOffset += offset;
  599. Int2 newOffset = getTextOffset();
  600. gGUIManager().getInputCaretTool()->updateText(this, textDesc);
  601. gGUIManager().getInputSelectionTool()->updateText(this, textDesc);
  602. markContentAsDirty();
  603. }
  604. void GUIInputBox::insertChar(CM::UINT32 charIdx, CM::UINT32 charCode)
  605. {
  606. mText.insert(mText.begin() + charIdx, charCode);
  607. TEXT_SPRITE_DESC textDesc = getTextDesc();
  608. Int2 offset = getTextOffset();
  609. gGUIManager().getInputCaretTool()->updateText(this, textDesc);
  610. gGUIManager().getInputSelectionTool()->updateText(this, textDesc);
  611. }
  612. void GUIInputBox::eraseChar(CM::UINT32 charIdx)
  613. {
  614. mText.erase(charIdx, 1);
  615. TEXT_SPRITE_DESC textDesc = getTextDesc();
  616. Int2 offset = getTextOffset();
  617. gGUIManager().getInputCaretTool()->updateText(this, textDesc);
  618. gGUIManager().getInputSelectionTool()->updateText(this, textDesc);
  619. }
  620. void GUIInputBox::deleteSelectedText()
  621. {
  622. UINT32 selStart = gGUIManager().getInputSelectionTool()->getSelectionStart();
  623. mText.erase(mText.begin() + selStart, mText.begin() + gGUIManager().getInputSelectionTool()->getSelectionEnd());
  624. TEXT_SPRITE_DESC textDesc = getTextDesc();
  625. Int2 offset = getTextOffset();
  626. gGUIManager().getInputCaretTool()->updateText(this, textDesc);
  627. gGUIManager().getInputSelectionTool()->updateText(this, textDesc);
  628. if(selStart > 0)
  629. {
  630. UINT32 newCaretPos = selStart - 1;
  631. gGUIManager().getInputCaretTool()->moveCaretToChar(newCaretPos, CARET_AFTER);
  632. }
  633. else
  634. {
  635. gGUIManager().getInputCaretTool()->moveCaretToChar(0, CARET_BEFORE);
  636. }
  637. scrollTextToCaret();
  638. clearSelection();
  639. }
  640. CM::Int2 GUIInputBox::getTextOffset() const
  641. {
  642. Rect textBounds = getContentBounds();
  643. return Int2(textBounds.x, textBounds.y) + mTextOffset;
  644. }
  645. CM::Rect GUIInputBox::getTextClipRect() const
  646. {
  647. Rect contentClipRect = getContentClipRect();
  648. return Rect(contentClipRect.x - mTextOffset.x, contentClipRect.y - mTextOffset.y, contentClipRect.width, contentClipRect.height);
  649. }
  650. TEXT_SPRITE_DESC GUIInputBox::getTextDesc() const
  651. {
  652. TEXT_SPRITE_DESC textDesc;
  653. textDesc.text = mText;
  654. textDesc.font = mStyle->font;
  655. textDesc.fontSize = mStyle->fontSize;
  656. Rect textBounds = getContentBounds();
  657. textDesc.width = textBounds.width;
  658. textDesc.height = textBounds.height;
  659. textDesc.horzAlign = mStyle->textHorzAlign;
  660. textDesc.vertAlign = mStyle->textVertAlign;
  661. textDesc.wordWrap = mIsMultiline;
  662. return textDesc;
  663. }
  664. void GUIInputBox::_setFocus(bool focus)
  665. {
  666. if(focus)
  667. {
  668. mImageDesc.texture = mStyle->focused.texture;
  669. markContentAsDirty();
  670. }
  671. else
  672. {
  673. mImageDesc.texture = mStyle->normal.texture;
  674. hideCaret();
  675. clearSelection();
  676. markContentAsDirty();
  677. }
  678. mHasFocus = focus;
  679. }
  680. const GUIContextMenu* GUIInputBox::getContextMenu() const
  681. {
  682. static bool initialized = false;
  683. static GUIContextMenu mContextMenu;
  684. if(!initialized)
  685. {
  686. mContextMenu.addMenuItem(L"Cut", boost::bind(&GUIInputBox::cutText, const_cast<GUIInputBox*>(this)));
  687. mContextMenu.addMenuItem(L"Copy", boost::bind(&GUIInputBox::copyText, const_cast<GUIInputBox*>(this)));
  688. mContextMenu.addMenuItem(L"Paste", boost::bind(&GUIInputBox::pasteText, const_cast<GUIInputBox*>(this)));
  689. // DEBUG ONLY
  690. mContextMenu.addSeparator(L"");
  691. mContextMenu.addMenuItem(L"DebugBox/Test1", boost::bind(&GUIInputBox::pasteText, const_cast<GUIInputBox*>(this)));
  692. mContextMenu.addMenuItem(L"DebugBox/Test2", boost::bind(&GUIInputBox::pasteText, const_cast<GUIInputBox*>(this)));
  693. mContextMenu.addMenuItem(L"Zzzz/Test1", boost::bind(&GUIInputBox::pasteText, const_cast<GUIInputBox*>(this)));
  694. initialized = true;
  695. }
  696. return &mContextMenu;
  697. }
  698. void GUIInputBox::cutText()
  699. {
  700. // TODO
  701. }
  702. void GUIInputBox::copyText()
  703. {
  704. // TODO
  705. }
  706. void GUIInputBox::pasteText()
  707. {
  708. // TODO
  709. }
  710. }