BsGUIInputBox.cpp 28 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031
  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 "BsGUITextInputEvent.h"
  10. #include "BsGUIMouseEvent.h"
  11. #include "BsGUICommandEvent.h"
  12. #include "CmFont.h"
  13. #include "CmTextData.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), mIsMouseOver(false)
  32. {
  33. mImageSprite = cm_new<ImageSprite, PoolAlloc>();
  34. mTextSprite = cm_new<TextSprite, PoolAlloc>();
  35. mActiveTexture = mStyle->normal.texture;
  36. if(mActiveTexture != nullptr && mActiveTexture.isLoaded() &&
  37. mActiveTexture->getTexture() != nullptr && mActiveTexture->getTexture().isLoaded())
  38. {
  39. mImageDesc.width = mActiveTexture->getTexture()->getWidth();
  40. mImageDesc.height = mActiveTexture->getTexture()->getHeight();
  41. }
  42. mImageDesc.borderLeft = mStyle->border.left;
  43. mImageDesc.borderRight = mStyle->border.right;
  44. mImageDesc.borderTop = mStyle->border.top;
  45. mImageDesc.borderBottom = mStyle->border.bottom;
  46. }
  47. GUIInputBox::~GUIInputBox()
  48. {
  49. cm_delete<PoolAlloc>(mTextSprite);
  50. cm_delete<PoolAlloc>(mImageSprite);
  51. }
  52. GUIInputBox* GUIInputBox::create(GUIWidget& parent, bool multiline, const GUIElementStyle* style)
  53. {
  54. if(style == nullptr)
  55. {
  56. const GUISkin& skin = parent.getSkin();
  57. style = skin.getStyle(getGUITypeName());
  58. }
  59. return new (cm_alloc<GUIInputBox, PoolAlloc>()) GUIInputBox(parent, style, GUILayoutOptions::create(style), multiline);
  60. }
  61. GUIInputBox* GUIInputBox::create(GUIWidget& parent, bool multiline, const GUIOptions& layoutOptions, const GUIElementStyle* style)
  62. {
  63. if(style == nullptr)
  64. {
  65. const GUISkin& skin = parent.getSkin();
  66. style = skin.getStyle(getGUITypeName());
  67. }
  68. return new (cm_alloc<GUIInputBox, PoolAlloc>()) GUIInputBox(parent, style, GUILayoutOptions::create(layoutOptions, style), multiline);
  69. }
  70. GUIInputBox* GUIInputBox::create(GUIWidget& parent, const GUIOptions& layoutOptions, const GUIElementStyle* style)
  71. {
  72. if(style == nullptr)
  73. {
  74. const GUISkin& skin = parent.getSkin();
  75. style = skin.getStyle(getGUITypeName());
  76. }
  77. return new (cm_alloc<GUIInputBox, PoolAlloc>()) GUIInputBox(parent, style, GUILayoutOptions::create(layoutOptions, style), false);
  78. }
  79. void GUIInputBox::setText(const CM::WString& text)
  80. {
  81. mText = text;
  82. TEXT_SPRITE_DESC textDesc = getTextDesc();
  83. Vector2I offset = getTextOffset();
  84. gGUIManager().getInputCaretTool()->updateText(this, textDesc);
  85. gGUIManager().getInputSelectionTool()->updateText(this, textDesc);
  86. if(mText.size() > 0)
  87. gGUIManager().getInputCaretTool()->moveCaretToChar((UINT32)mText.size() - 1, CARET_AFTER);
  88. else
  89. gGUIManager().getInputCaretTool()->moveCaretToChar(0, CARET_BEFORE);
  90. scrollTextToCaret();
  91. markContentAsDirty();
  92. }
  93. UINT32 GUIInputBox::getNumRenderElements() const
  94. {
  95. UINT32 numElements = mImageSprite->getNumRenderElements();
  96. numElements += mTextSprite->getNumRenderElements();
  97. if(mCaretShown && gGUIManager().getCaretBlinkState())
  98. numElements += gGUIManager().getInputCaretTool()->getSprite()->getNumRenderElements();
  99. if(mSelectionShown)
  100. {
  101. const Vector<ImageSprite*>::type& sprites = gGUIManager().getInputSelectionTool()->getSprites();
  102. for(auto& selectionSprite : sprites)
  103. {
  104. numElements += selectionSprite->getNumRenderElements();
  105. }
  106. }
  107. return numElements;
  108. }
  109. const GUIMaterialInfo& GUIInputBox::getMaterial(UINT32 renderElementIdx) const
  110. {
  111. UINT32 localRenderElementIdx;
  112. Sprite* sprite = renderElemToSprite(renderElementIdx, localRenderElementIdx);
  113. return sprite->getMaterial(localRenderElementIdx);
  114. }
  115. UINT32 GUIInputBox::getNumQuads(UINT32 renderElementIdx) const
  116. {
  117. UINT32 localRenderElementIdx;
  118. Sprite* sprite = renderElemToSprite(renderElementIdx, localRenderElementIdx);
  119. return sprite->getNumQuads(localRenderElementIdx);
  120. }
  121. void GUIInputBox::updateRenderElementsInternal()
  122. {
  123. if(mActiveTexture != nullptr && mActiveTexture.isLoaded())
  124. mImageDesc.texture = mActiveTexture.getInternalPtr();
  125. mImageDesc.width = mWidth;
  126. mImageDesc.height = mHeight;
  127. mImageSprite->update(mImageDesc);
  128. TEXT_SPRITE_DESC textDesc = getTextDesc();
  129. mTextSprite->update(textDesc);
  130. if(mCaretShown && gGUIManager().getCaretBlinkState())
  131. {
  132. gGUIManager().getInputCaretTool()->updateText(this, textDesc); // TODO - These shouldn't be here. Only call this when one of these parameters changes.
  133. gGUIManager().getInputCaretTool()->updateSprite();
  134. }
  135. if(mSelectionShown)
  136. {
  137. gGUIManager().getInputSelectionTool()->updateText(this, textDesc); // TODO - These shouldn't be here. Only call this when one of these parameters changes.
  138. gGUIManager().getInputSelectionTool()->updateSprite();
  139. }
  140. // When text bounds are reduced the scroll needs to be adjusted so that
  141. // input box isn't filled with mostly empty space.
  142. clampScrollToBounds(mTextSprite->getBounds(mOffset, RectI()));
  143. GUIElement::updateRenderElementsInternal();
  144. }
  145. void GUIInputBox::updateClippedBounds()
  146. {
  147. mClippedBounds = mImageSprite->getBounds(mOffset, mClipRect);
  148. }
  149. Sprite* GUIInputBox::renderElemToSprite(UINT32 renderElemIdx, UINT32& localRenderElemIdx) const
  150. {
  151. UINT32 oldNumElements = 0;
  152. UINT32 newNumElements = oldNumElements + mTextSprite->getNumRenderElements();
  153. if(renderElemIdx < newNumElements)
  154. {
  155. localRenderElemIdx = renderElemIdx - oldNumElements;
  156. return mTextSprite;
  157. }
  158. oldNumElements = newNumElements;
  159. newNumElements += mImageSprite->getNumRenderElements();
  160. if(renderElemIdx < newNumElements)
  161. {
  162. localRenderElemIdx = renderElemIdx - oldNumElements;
  163. return mImageSprite;
  164. }
  165. if(mCaretShown && gGUIManager().getCaretBlinkState())
  166. {
  167. oldNumElements = newNumElements;
  168. newNumElements += gGUIManager().getInputCaretTool()->getSprite()->getNumRenderElements();
  169. if(renderElemIdx < newNumElements)
  170. {
  171. localRenderElemIdx = renderElemIdx - oldNumElements;
  172. return gGUIManager().getInputCaretTool()->getSprite();
  173. }
  174. }
  175. if(mSelectionShown)
  176. {
  177. const Vector<ImageSprite*>::type& sprites = gGUIManager().getInputSelectionTool()->getSprites();
  178. for(auto& selectionSprite : sprites)
  179. {
  180. oldNumElements = newNumElements;
  181. newNumElements += selectionSprite->getNumRenderElements();
  182. if(renderElemIdx < newNumElements)
  183. {
  184. localRenderElemIdx = renderElemIdx - oldNumElements;
  185. return selectionSprite;
  186. }
  187. }
  188. }
  189. localRenderElemIdx = renderElemIdx;
  190. return nullptr;
  191. }
  192. Vector2I GUIInputBox::renderElemToOffset(UINT32 renderElemIdx) const
  193. {
  194. UINT32 oldNumElements = 0;
  195. UINT32 newNumElements = oldNumElements + mTextSprite->getNumRenderElements();
  196. if(renderElemIdx < newNumElements)
  197. return getTextOffset();
  198. oldNumElements = newNumElements;
  199. newNumElements += mImageSprite->getNumRenderElements();
  200. if(renderElemIdx < newNumElements)
  201. return mOffset;
  202. if(mCaretShown && gGUIManager().getCaretBlinkState())
  203. {
  204. oldNumElements = newNumElements;
  205. newNumElements += gGUIManager().getInputCaretTool()->getSprite()->getNumRenderElements();
  206. if(renderElemIdx < newNumElements)
  207. return gGUIManager().getInputCaretTool()->getSpriteOffset();
  208. }
  209. if(mSelectionShown)
  210. {
  211. UINT32 spriteIdx = 0;
  212. const Vector<ImageSprite*>::type& sprites = gGUIManager().getInputSelectionTool()->getSprites();
  213. for(auto& selectionSprite : sprites)
  214. {
  215. oldNumElements = newNumElements;
  216. newNumElements += selectionSprite->getNumRenderElements();
  217. if(renderElemIdx < newNumElements)
  218. return gGUIManager().getInputSelectionTool()->getSelectionSpriteOffset(spriteIdx);
  219. spriteIdx++;
  220. }
  221. }
  222. return Vector2I();
  223. }
  224. RectI GUIInputBox::renderElemToClipRect(UINT32 renderElemIdx) const
  225. {
  226. UINT32 oldNumElements = 0;
  227. UINT32 newNumElements = oldNumElements + mTextSprite->getNumRenderElements();
  228. if(renderElemIdx < newNumElements)
  229. return getTextClipRect();
  230. oldNumElements = newNumElements;
  231. newNumElements += mImageSprite->getNumRenderElements();
  232. if(renderElemIdx < newNumElements)
  233. return mClipRect;
  234. if(mCaretShown && gGUIManager().getCaretBlinkState())
  235. {
  236. oldNumElements = newNumElements;
  237. newNumElements += gGUIManager().getInputCaretTool()->getSprite()->getNumRenderElements();
  238. if(renderElemIdx < newNumElements)
  239. {
  240. return gGUIManager().getInputCaretTool()->getSpriteClipRect(getTextClipRect());
  241. }
  242. }
  243. if(mSelectionShown)
  244. {
  245. UINT32 spriteIdx = 0;
  246. const Vector<ImageSprite*>::type& sprites = gGUIManager().getInputSelectionTool()->getSprites();
  247. for(auto& selectionSprite : sprites)
  248. {
  249. oldNumElements = newNumElements;
  250. newNumElements += selectionSprite->getNumRenderElements();
  251. if(renderElemIdx < newNumElements)
  252. return gGUIManager().getInputSelectionTool()->getSelectionSpriteClipRect(spriteIdx, getTextClipRect());
  253. spriteIdx++;
  254. }
  255. }
  256. return RectI();
  257. }
  258. Vector2I GUIInputBox::_getOptimalSize() const
  259. {
  260. UINT32 imageWidth = 0;
  261. UINT32 imageHeight = 0;
  262. if(mActiveTexture != nullptr && mActiveTexture.isLoaded())
  263. {
  264. imageWidth = mActiveTexture->getTexture()->getWidth();
  265. imageHeight = mActiveTexture->getTexture()->getHeight();
  266. }
  267. Vector2I contentSize = GUIHelper::calcOptimalContentsSize(mText, *mStyle, _getLayoutOptions());
  268. UINT32 contentWidth = std::max(imageWidth, (UINT32)contentSize.x);
  269. UINT32 contentHeight = std::max(imageHeight, (UINT32)contentSize.y);
  270. return Vector2I(contentWidth, contentHeight);
  271. }
  272. CM::Vector2I GUIInputBox::_getTextInputOffset() const
  273. {
  274. return mTextOffset;
  275. }
  276. CM::RectI GUIInputBox::_getTextInputRect() const
  277. {
  278. RectI textBounds = getContentBounds();
  279. textBounds.x -= mOffset.x;
  280. textBounds.y -= mOffset.y;
  281. return textBounds;
  282. }
  283. UINT32 GUIInputBox::_getRenderElementDepth(UINT32 renderElementIdx) const
  284. {
  285. UINT32 localRenderElementIdx;
  286. Sprite* sprite = renderElemToSprite(renderElementIdx, localRenderElementIdx);
  287. if(sprite == mImageSprite)
  288. return _getDepth();
  289. else if(sprite == mTextSprite)
  290. return _getDepth() - 2;
  291. else if(sprite == gGUIManager().getInputCaretTool()->getSprite())
  292. return _getDepth() - 3;
  293. else // Selection sprites
  294. return _getDepth() - 1;
  295. }
  296. void GUIInputBox::fillBuffer(UINT8* vertices, UINT8* uv, UINT32* indices, UINT32 startingQuad, UINT32 maxNumQuads,
  297. UINT32 vertexStride, UINT32 indexStride, UINT32 renderElementIdx) const
  298. {
  299. UINT32 localRenderElementIdx;
  300. Sprite* sprite = renderElemToSprite(renderElementIdx, localRenderElementIdx);
  301. Vector2I offset = renderElemToOffset(renderElementIdx);
  302. RectI clipRect = renderElemToClipRect(renderElementIdx);
  303. sprite->fillBuffer(vertices, uv, indices, startingQuad, maxNumQuads, vertexStride, indexStride, localRenderElementIdx, offset, clipRect);
  304. }
  305. bool GUIInputBox::mouseEvent(const GUIMouseEvent& ev)
  306. {
  307. if(ev.getType() == GUIMouseEventType::MouseOver)
  308. {
  309. if(!mHasFocus)
  310. {
  311. mActiveTexture = mStyle->hover.texture;
  312. markContentAsDirty();
  313. }
  314. if(!mInputCursorSet)
  315. {
  316. Platform::setCursor(CursorType::IBeam);
  317. mInputCursorSet = true;
  318. }
  319. mIsMouseOver = true;
  320. return true;
  321. }
  322. else if(ev.getType() == GUIMouseEventType::MouseOut)
  323. {
  324. if(!mHasFocus)
  325. {
  326. mActiveTexture = mStyle->normal.texture;
  327. markContentAsDirty();
  328. }
  329. if(!mDragInProgress && mInputCursorSet)
  330. {
  331. Platform::setCursor(CursorType::Arrow);
  332. mInputCursorSet = false;
  333. }
  334. mIsMouseOver = false;
  335. return true;
  336. }
  337. else if(ev.getType() == GUIMouseEventType::MouseDoubleClick && ev.getButton() == GUIMouseButton::Left)
  338. {
  339. showSelection(0);
  340. gGUIManager().getInputSelectionTool()->selectAll();
  341. markContentAsDirty();
  342. return true;
  343. }
  344. else if(ev.getType() == GUIMouseEventType::MouseDown && ev.getButton() == GUIMouseButton::Left)
  345. {
  346. if(mHasFocus)
  347. {
  348. if(ev.isShiftDown())
  349. {
  350. if(!mSelectionShown)
  351. showSelection(gGUIManager().getInputCaretTool()->getCaretPos());
  352. }
  353. else
  354. clearSelection();
  355. if(mText.size() > 0)
  356. gGUIManager().getInputCaretTool()->moveCaretToPos(ev.getPosition());
  357. else
  358. gGUIManager().getInputCaretTool()->moveCaretToStart();
  359. if(ev.isShiftDown())
  360. gGUIManager().getInputSelectionTool()->moveSelectionToCaret(gGUIManager().getInputCaretTool()->getCaretPos());
  361. }
  362. else
  363. {
  364. clearSelection();
  365. showCaret();
  366. if(mText.size() > 0)
  367. gGUIManager().getInputCaretTool()->moveCaretToPos(ev.getPosition());
  368. else
  369. gGUIManager().getInputCaretTool()->moveCaretToStart();
  370. }
  371. scrollTextToCaret();
  372. markContentAsDirty();
  373. return true;
  374. }
  375. else if(ev.getType() == GUIMouseEventType::MouseDragStart)
  376. {
  377. if(!ev.isShiftDown())
  378. {
  379. mDragInProgress = true;
  380. UINT32 caretPos = gGUIManager().getInputCaretTool()->getCaretPos();
  381. showSelection(caretPos);
  382. gGUIManager().getInputSelectionTool()->selectionDragStart(caretPos);
  383. return true;
  384. }
  385. }
  386. else if(ev.getType() == GUIMouseEventType::MouseDragEnd)
  387. {
  388. if(!ev.isShiftDown())
  389. {
  390. mDragInProgress = false;
  391. if(!mIsMouseOver && mInputCursorSet)
  392. {
  393. Platform::setCursor(CursorType::Arrow);
  394. mInputCursorSet = false;
  395. }
  396. gGUIManager().getInputSelectionTool()->selectionDragEnd();
  397. return true;
  398. }
  399. }
  400. else if(ev.getType() == GUIMouseEventType::MouseDrag)
  401. {
  402. if(!ev.isShiftDown())
  403. {
  404. if(mText.size() > 0)
  405. gGUIManager().getInputCaretTool()->moveCaretToPos(ev.getPosition());
  406. else
  407. gGUIManager().getInputCaretTool()->moveCaretToStart();
  408. gGUIManager().getInputSelectionTool()->selectionDragUpdate(gGUIManager().getInputCaretTool()->getCaretPos());
  409. scrollTextToCaret();
  410. markContentAsDirty();
  411. return true;
  412. }
  413. }
  414. return false;
  415. }
  416. bool GUIInputBox::textInputEvent(const GUITextInputEvent& ev)
  417. {
  418. if(mSelectionShown)
  419. deleteSelectedText();
  420. UINT32 charIdx = gGUIManager().getInputCaretTool()->getCharIdxAtCaretPos();
  421. insertChar(charIdx, ev.getInputChar());
  422. gGUIManager().getInputCaretTool()->moveCaretToChar(charIdx, CARET_AFTER);
  423. scrollTextToCaret();
  424. markContentAsDirty();
  425. return true;
  426. }
  427. bool GUIInputBox::commandEvent(const GUICommandEvent& ev)
  428. {
  429. if(ev.getType() == GUICommandEventType::Redraw)
  430. {
  431. markMeshAsDirty();
  432. return true;
  433. }
  434. if(ev.getType() == GUICommandEventType::FocusGained)
  435. {
  436. mActiveTexture = mStyle->focused.texture;
  437. clearSelection();
  438. showCaret();
  439. markContentAsDirty();
  440. mHasFocus = true;
  441. return true;
  442. }
  443. if(ev.getType() == GUICommandEventType::FocusLost)
  444. {
  445. mActiveTexture = mStyle->normal.texture;
  446. hideCaret();
  447. clearSelection();
  448. markContentAsDirty();
  449. mHasFocus = false;
  450. return true;
  451. }
  452. if(ev.getType() == GUICommandEventType::Backspace)
  453. {
  454. if(mText.size() > 0)
  455. {
  456. if(mSelectionShown)
  457. {
  458. deleteSelectedText();
  459. }
  460. else
  461. {
  462. UINT32 charIdx = gGUIManager().getInputCaretTool()->getCharIdxAtCaretPos() - 1;
  463. if(charIdx < (UINT32)mText.size())
  464. {
  465. eraseChar(charIdx);
  466. if(charIdx > 0)
  467. charIdx--;
  468. gGUIManager().getInputCaretTool()->moveCaretToChar(charIdx, CARET_AFTER);
  469. scrollTextToCaret();
  470. }
  471. }
  472. markContentAsDirty();
  473. }
  474. return true;
  475. }
  476. if(ev.getType() == GUICommandEventType::Delete)
  477. {
  478. if(mText.size() > 0)
  479. {
  480. if(mSelectionShown)
  481. {
  482. deleteSelectedText();
  483. }
  484. else
  485. {
  486. UINT32 charIdx = gGUIManager().getInputCaretTool()->getCharIdxAtCaretPos();
  487. if(charIdx < (UINT32)mText.size())
  488. {
  489. eraseChar(charIdx);
  490. if(charIdx > 0)
  491. charIdx--;
  492. gGUIManager().getInputCaretTool()->moveCaretToChar(charIdx, CARET_AFTER);
  493. scrollTextToCaret();
  494. }
  495. }
  496. markContentAsDirty();
  497. }
  498. return true;
  499. }
  500. if(ev.getType() == GUICommandEventType::CursorMoveLeft)
  501. {
  502. if(mSelectionShown)
  503. {
  504. UINT32 selStart = gGUIManager().getInputSelectionTool()->getSelectionStart();
  505. clearSelection();
  506. if(selStart > 0)
  507. gGUIManager().getInputCaretTool()->moveCaretToChar(selStart - 1, CARET_AFTER);
  508. else
  509. gGUIManager().getInputCaretTool()->moveCaretToChar(0, CARET_BEFORE);
  510. }
  511. else
  512. gGUIManager().getInputCaretTool()->moveCaretLeft();
  513. scrollTextToCaret();
  514. markContentAsDirty();
  515. return true;
  516. }
  517. if(ev.getType() == GUICommandEventType::SelectLeft)
  518. {
  519. if(!mSelectionShown)
  520. showSelection(gGUIManager().getInputCaretTool()->getCaretPos());
  521. gGUIManager().getInputCaretTool()->moveCaretLeft();
  522. gGUIManager().getInputSelectionTool()->moveSelectionToCaret(gGUIManager().getInputCaretTool()->getCaretPos());
  523. scrollTextToCaret();
  524. markContentAsDirty();
  525. return true;
  526. }
  527. if(ev.getType() == GUICommandEventType::CursorMoveRight)
  528. {
  529. if(mSelectionShown)
  530. {
  531. UINT32 selEnd = gGUIManager().getInputSelectionTool()->getSelectionEnd();
  532. clearSelection();
  533. if(selEnd > 0)
  534. gGUIManager().getInputCaretTool()->moveCaretToChar(selEnd - 1, CARET_AFTER);
  535. else
  536. gGUIManager().getInputCaretTool()->moveCaretToChar(0, CARET_BEFORE);
  537. }
  538. else
  539. gGUIManager().getInputCaretTool()->moveCaretRight();
  540. scrollTextToCaret();
  541. markContentAsDirty();
  542. return true;
  543. }
  544. if(ev.getType() == GUICommandEventType::SelectRight)
  545. {
  546. if(!mSelectionShown)
  547. showSelection(gGUIManager().getInputCaretTool()->getCaretPos());
  548. gGUIManager().getInputCaretTool()->moveCaretRight();
  549. gGUIManager().getInputSelectionTool()->moveSelectionToCaret(gGUIManager().getInputCaretTool()->getCaretPos());
  550. scrollTextToCaret();
  551. markContentAsDirty();
  552. return true;
  553. }
  554. if(ev.getType() == GUICommandEventType::CursorMoveUp)
  555. {
  556. clearSelection();
  557. gGUIManager().getInputCaretTool()->moveCaretUp();
  558. scrollTextToCaret();
  559. markContentAsDirty();
  560. return true;
  561. }
  562. if(ev.getType() == GUICommandEventType::SelectUp)
  563. {
  564. if(!mSelectionShown)
  565. showSelection(gGUIManager().getInputCaretTool()->getCaretPos());;
  566. gGUIManager().getInputCaretTool()->moveCaretUp();
  567. gGUIManager().getInputSelectionTool()->moveSelectionToCaret(gGUIManager().getInputCaretTool()->getCaretPos());
  568. scrollTextToCaret();
  569. markContentAsDirty();
  570. return true;
  571. }
  572. if(ev.getType() == GUICommandEventType::CursorMoveDown)
  573. {
  574. clearSelection();
  575. gGUIManager().getInputCaretTool()->moveCaretDown();
  576. scrollTextToCaret();
  577. markContentAsDirty();
  578. return true;
  579. }
  580. if(ev.getType() == GUICommandEventType::SelectDown)
  581. {
  582. if(!mSelectionShown)
  583. showSelection(gGUIManager().getInputCaretTool()->getCaretPos());
  584. gGUIManager().getInputCaretTool()->moveCaretDown();
  585. gGUIManager().getInputSelectionTool()->moveSelectionToCaret(gGUIManager().getInputCaretTool()->getCaretPos());
  586. scrollTextToCaret();
  587. markContentAsDirty();
  588. return true;
  589. }
  590. if(ev.getType() == GUICommandEventType::Return)
  591. {
  592. if(mIsMultiline)
  593. {
  594. if(mSelectionShown)
  595. deleteSelectedText();
  596. insertChar(gGUIManager().getInputCaretTool()->getCharIdxAtCaretPos(), '\n');
  597. gGUIManager().getInputCaretTool()->moveCaretRight();
  598. scrollTextToCaret();
  599. markContentAsDirty();
  600. return true;
  601. }
  602. }
  603. if(ev.getType() == GUICommandEventType::SelectAll)
  604. {
  605. showSelection(0);
  606. gGUIManager().getInputSelectionTool()->selectAll();
  607. markContentAsDirty();
  608. return true;
  609. }
  610. if(ev.getType() == GUICommandEventType::Cut)
  611. {
  612. cutText();
  613. markContentAsDirty();
  614. return true;
  615. }
  616. if(ev.getType() == GUICommandEventType::Copy)
  617. {
  618. copyText();
  619. return true;
  620. }
  621. if(ev.getType() == GUICommandEventType::Paste)
  622. {
  623. pasteText();
  624. markContentAsDirty();
  625. return true;
  626. }
  627. return false;
  628. }
  629. void GUIInputBox::showCaret()
  630. {
  631. mCaretShown = true;
  632. TEXT_SPRITE_DESC textDesc = getTextDesc();
  633. Vector2I offset = getTextOffset();
  634. gGUIManager().getInputCaretTool()->updateText(this, textDesc);
  635. markContentAsDirty();
  636. }
  637. void GUIInputBox::hideCaret()
  638. {
  639. mCaretShown = false;
  640. markContentAsDirty();
  641. }
  642. void GUIInputBox::showSelection(CM::UINT32 anchorCaretPos)
  643. {
  644. TEXT_SPRITE_DESC textDesc = getTextDesc();
  645. Vector2I offset = getTextOffset();
  646. gGUIManager().getInputSelectionTool()->updateText(this, textDesc);
  647. gGUIManager().getInputSelectionTool()->showSelection(anchorCaretPos);
  648. mSelectionShown = true;
  649. markContentAsDirty();
  650. }
  651. void GUIInputBox::clearSelection()
  652. {
  653. gGUIManager().getInputSelectionTool()->clearSelection();
  654. mSelectionShown = false;
  655. markContentAsDirty();
  656. }
  657. void GUIInputBox::scrollTextToCaret()
  658. {
  659. TEXT_SPRITE_DESC textDesc = getTextDesc();
  660. Vector2I textOffset = getTextOffset();
  661. Vector2I caretPos = gGUIManager().getInputCaretTool()->getCaretPosition(textOffset);
  662. UINT32 caretHeight = gGUIManager().getInputCaretTool()->getCaretHeight();
  663. UINT32 caretWidth = 1;
  664. INT32 caretRight = caretPos.x + (INT32)caretWidth;
  665. INT32 caretBottom = caretPos.y + (INT32)caretHeight;
  666. INT32 left = textOffset.x - mTextOffset.x;
  667. // Include caret width here because we don't want to scroll if just the caret is outside the bounds
  668. // (Possible if the text width is exactly the maximum width)
  669. INT32 right = left + (INT32)textDesc.width + caretWidth;
  670. INT32 top = textOffset.y - mTextOffset.y;
  671. INT32 bottom = top + (INT32)textDesc.height;
  672. Vector2I offset;
  673. if(caretPos.x < left)
  674. {
  675. offset.x = left - caretPos.x;
  676. }
  677. else if(caretRight > right)
  678. {
  679. offset.x = -(caretRight - right);
  680. }
  681. if(caretPos.y < top)
  682. {
  683. offset.y = top - caretPos.y;
  684. }
  685. else if(caretBottom > bottom)
  686. {
  687. offset.y = -(caretBottom - bottom);
  688. }
  689. mTextOffset += offset;
  690. gGUIManager().getInputCaretTool()->updateText(this, textDesc);
  691. gGUIManager().getInputSelectionTool()->updateText(this, textDesc);
  692. markContentAsDirty();
  693. }
  694. void GUIInputBox::clampScrollToBounds(RectI unclippedTextBounds)
  695. {
  696. TEXT_SPRITE_DESC textDesc = getTextDesc();
  697. Vector2I newTextOffset;
  698. INT32 maxScrollableWidth = std::max(0, (INT32)unclippedTextBounds.width - (INT32)textDesc.width);
  699. INT32 maxScrollableHeight = std::max(0, (INT32)unclippedTextBounds.height - (INT32)textDesc.height);
  700. newTextOffset.x = Math::clamp(mTextOffset.x, -maxScrollableWidth, 0);
  701. newTextOffset.y = Math::clamp(mTextOffset.y, -maxScrollableHeight, 0);
  702. if(newTextOffset != mTextOffset)
  703. {
  704. mTextOffset = newTextOffset;
  705. gGUIManager().getInputCaretTool()->updateText(this, textDesc);
  706. gGUIManager().getInputSelectionTool()->updateText(this, textDesc);
  707. markContentAsDirty();
  708. }
  709. }
  710. void GUIInputBox::insertString(CM::UINT32 charIdx, const WString& string)
  711. {
  712. mText.insert(mText.begin() + charIdx, string.begin(), string.end());
  713. TEXT_SPRITE_DESC textDesc = getTextDesc();
  714. Vector2I offset = getTextOffset();
  715. gGUIManager().getInputCaretTool()->updateText(this, textDesc);
  716. gGUIManager().getInputSelectionTool()->updateText(this, textDesc);
  717. }
  718. void GUIInputBox::insertChar(CM::UINT32 charIdx, CM::UINT32 charCode)
  719. {
  720. mText.insert(mText.begin() + charIdx, charCode);
  721. TEXT_SPRITE_DESC textDesc = getTextDesc();
  722. Vector2I offset = getTextOffset();
  723. gGUIManager().getInputCaretTool()->updateText(this, textDesc);
  724. gGUIManager().getInputSelectionTool()->updateText(this, textDesc);
  725. }
  726. void GUIInputBox::eraseChar(CM::UINT32 charIdx)
  727. {
  728. mText.erase(charIdx, 1);
  729. TEXT_SPRITE_DESC textDesc = getTextDesc();
  730. Vector2I offset = getTextOffset();
  731. gGUIManager().getInputCaretTool()->updateText(this, textDesc);
  732. gGUIManager().getInputSelectionTool()->updateText(this, textDesc);
  733. }
  734. void GUIInputBox::deleteSelectedText()
  735. {
  736. UINT32 selStart = gGUIManager().getInputSelectionTool()->getSelectionStart();
  737. mText.erase(mText.begin() + selStart, mText.begin() + gGUIManager().getInputSelectionTool()->getSelectionEnd());
  738. TEXT_SPRITE_DESC textDesc = getTextDesc();
  739. Vector2I offset = getTextOffset();
  740. gGUIManager().getInputCaretTool()->updateText(this, textDesc);
  741. gGUIManager().getInputSelectionTool()->updateText(this, textDesc);
  742. if(selStart > 0)
  743. {
  744. UINT32 newCaretPos = selStart - 1;
  745. gGUIManager().getInputCaretTool()->moveCaretToChar(newCaretPos, CARET_AFTER);
  746. }
  747. else
  748. {
  749. gGUIManager().getInputCaretTool()->moveCaretToChar(0, CARET_BEFORE);
  750. }
  751. scrollTextToCaret();
  752. clearSelection();
  753. }
  754. WString GUIInputBox::getSelectedText()
  755. {
  756. UINT32 selStart = gGUIManager().getInputSelectionTool()->getSelectionStart();
  757. UINT32 selEnd = gGUIManager().getInputSelectionTool()->getSelectionEnd();
  758. return mText.substr(selStart, selEnd - selStart);
  759. mText.erase(mText.begin() + selStart, mText.begin() + gGUIManager().getInputSelectionTool()->getSelectionEnd());
  760. }
  761. CM::Vector2I GUIInputBox::getTextOffset() const
  762. {
  763. RectI textBounds = getContentBounds();
  764. return Vector2I(textBounds.x, textBounds.y) + mTextOffset;
  765. }
  766. CM::RectI GUIInputBox::getTextClipRect() const
  767. {
  768. RectI contentClipRect = getContentClipRect();
  769. return RectI(contentClipRect.x - mTextOffset.x, contentClipRect.y - mTextOffset.y, contentClipRect.width, contentClipRect.height);
  770. }
  771. TEXT_SPRITE_DESC GUIInputBox::getTextDesc() const
  772. {
  773. TEXT_SPRITE_DESC textDesc;
  774. textDesc.text = mText;
  775. textDesc.font = mStyle->font;
  776. textDesc.fontSize = mStyle->fontSize;
  777. RectI textBounds = getContentBounds();
  778. textDesc.width = textBounds.width;
  779. textDesc.height = textBounds.height;
  780. textDesc.horzAlign = mStyle->textHorzAlign;
  781. textDesc.vertAlign = mStyle->textVertAlign;
  782. textDesc.wordWrap = mIsMultiline;
  783. return textDesc;
  784. }
  785. GUIContextMenu* GUIInputBox::getContextMenu() const
  786. {
  787. static bool initialized = false;
  788. static GUIContextMenu mContextMenu;
  789. if(!initialized)
  790. {
  791. mContextMenu.addMenuItem(L"Cut", boost::bind(&GUIInputBox::cutText, const_cast<GUIInputBox*>(this)));
  792. mContextMenu.addMenuItem(L"Copy", boost::bind(&GUIInputBox::copyText, const_cast<GUIInputBox*>(this)));
  793. mContextMenu.addMenuItem(L"Paste", boost::bind(&GUIInputBox::pasteText, const_cast<GUIInputBox*>(this)));
  794. mContextMenu.setLocalizedName(L"Cut", HString(L"Cut"));
  795. mContextMenu.setLocalizedName(L"Copy", HString(L"Copy"));
  796. mContextMenu.setLocalizedName(L"Paste", HString(L"Paste"));
  797. initialized = true;
  798. }
  799. return &mContextMenu;
  800. }
  801. void GUIInputBox::cutText()
  802. {
  803. copyText();
  804. deleteSelectedText();
  805. }
  806. void GUIInputBox::copyText()
  807. {
  808. Platform::copyToClipboard(getSelectedText());
  809. }
  810. void GUIInputBox::pasteText()
  811. {
  812. WString textInClipboard = Platform::copyFromClipboard();
  813. UINT32 charIdx = gGUIManager().getInputCaretTool()->getCharIdxAtCaretPos();
  814. insertString(charIdx, textInClipboard);
  815. if(textInClipboard.size() > 0)
  816. gGUIManager().getInputCaretTool()->moveCaretToChar(charIdx + ((UINT32)textInClipboard.size() - 1), CARET_AFTER);
  817. scrollTextToCaret();
  818. markContentAsDirty();
  819. }
  820. }