BsGUIInputBox.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  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 "CmTextUtility.h"
  13. #include "CmTexture.h"
  14. #include "CmCursor.h"
  15. using namespace CamelotFramework;
  16. namespace BansheeEngine
  17. {
  18. const String& GUIInputBox::getGUITypeName()
  19. {
  20. static String name = "InputBox";
  21. return name;
  22. }
  23. GUIInputBox::GUIInputBox(GUIWidget& parent, const GUIElementStyle* style, const GUILayoutOptions& layoutOptions)
  24. :GUIElement(parent, style, layoutOptions), mNumImageRenderElements(0), mInputCursorSet(false), mDragInProgress(false),
  25. mSelectionStart(0), mSelectionEnd(0), mCaretSprite(nullptr), mCaretShown(false), mSelectionShown(false), mCaretPos(0),
  26. mIsMultiline(false)
  27. {
  28. mImageSprite = cm_new<ImageSprite, PoolAlloc>();
  29. mCaretSprite = cm_new<ImageSprite, PoolAlloc>();
  30. mTextSprite = cm_new<TextSprite, PoolAlloc>();
  31. mImageDesc.texture = mStyle->normal.texture;
  32. if(mImageDesc.texture != nullptr)
  33. {
  34. mImageDesc.width = mImageDesc.texture->getTexture()->getWidth();
  35. mImageDesc.height = mImageDesc.texture->getTexture()->getHeight();
  36. }
  37. mImageDesc.borderLeft = mStyle->border.left;
  38. mImageDesc.borderRight = mStyle->border.right;
  39. mImageDesc.borderTop = mStyle->border.top;
  40. mImageDesc.borderBottom = mStyle->border.bottom;
  41. }
  42. GUIInputBox::~GUIInputBox()
  43. {
  44. cm_delete<PoolAlloc>(mTextSprite);
  45. cm_delete<PoolAlloc>(mCaretSprite);
  46. cm_delete<PoolAlloc>(mImageSprite);
  47. for(auto& sprite : mSelectionSprites)
  48. cm_delete(sprite);
  49. }
  50. GUIInputBox* GUIInputBox::create(GUIWidget& parent, const GUIElementStyle* style)
  51. {
  52. if(style == nullptr)
  53. {
  54. const GUISkin* skin = parent.getSkin();
  55. style = skin->getStyle(getGUITypeName());
  56. }
  57. return new (cm_alloc<GUIInputBox, PoolAlloc>()) GUIInputBox(parent, style, getDefaultLayoutOptions(style));
  58. }
  59. GUIInputBox* GUIInputBox::create(GUIWidget& parent, const GUILayoutOptions& layoutOptions, const GUIElementStyle* style)
  60. {
  61. if(style == nullptr)
  62. {
  63. const GUISkin* skin = parent.getSkin();
  64. style = skin->getStyle(getGUITypeName());
  65. }
  66. return new (cm_alloc<GUIInputBox, PoolAlloc>()) GUIInputBox(parent, style, layoutOptions);
  67. }
  68. UINT32 GUIInputBox::getNumRenderElements() const
  69. {
  70. UINT32 numElements = mImageSprite->getNumRenderElements();
  71. numElements += mTextSprite->getNumRenderElements();
  72. if(mCaretShown && GUIManager::instance().getCaretBlinkState())
  73. numElements += mTextSprite->getNumRenderElements();
  74. if(mSelectionShown)
  75. {
  76. for(auto& selectionSprite : mSelectionSprites)
  77. {
  78. numElements += selectionSprite->getNumRenderElements();
  79. }
  80. }
  81. return numElements;
  82. }
  83. const HMaterial& GUIInputBox::getMaterial(UINT32 renderElementIdx) const
  84. {
  85. UINT32 localRenderElementIdx;
  86. Sprite* sprite = renderElemToSprite(renderElementIdx, localRenderElementIdx);
  87. return sprite->getMaterial(localRenderElementIdx);
  88. }
  89. UINT32 GUIInputBox::getNumQuads(UINT32 renderElementIdx) const
  90. {
  91. UINT32 localRenderElementIdx;
  92. Sprite* sprite = renderElemToSprite(renderElementIdx, localRenderElementIdx);
  93. return sprite->getNumQuads(localRenderElementIdx);
  94. }
  95. void GUIInputBox::updateRenderElementsInternal()
  96. {
  97. mImageDesc.offset = mOffset;
  98. mImageDesc.width = mWidth;
  99. mImageDesc.height = mHeight;
  100. mImageDesc.clipRect = mClipRect;
  101. mImageSprite->update(mImageDesc);
  102. mBounds = mImageSprite->getBounds();
  103. mNumImageRenderElements = mImageSprite->getNumRenderElements();
  104. TEXT_SPRITE_DESC textDesc;
  105. textDesc.text = mText;
  106. textDesc.font = mStyle->font;
  107. textDesc.fontSize = mStyle->fontSize;
  108. Rect textBounds = getTextBounds();
  109. textDesc.offset = Int2(textBounds.x, textBounds.y);
  110. textDesc.width = textBounds.width;
  111. textDesc.height = textBounds.height;
  112. textDesc.clipRect = Rect(0, 0, textDesc.width, textDesc.height);
  113. textDesc.horzAlign = mStyle->textHorzAlign;
  114. textDesc.vertAlign = mStyle->textVertAlign;
  115. mTextSprite->update(textDesc);
  116. if(mCaretShown && GUIManager::instance().getCaretBlinkState())
  117. {
  118. IMAGE_SPRITE_DESC mCaretDesc;
  119. mCaretDesc.offset = getCaretPosition();
  120. mCaretDesc.width = 1;
  121. mCaretDesc.height = getCaretHeight();
  122. mCaretDesc.clipRect = Rect(0, 0, textDesc.width, textDesc.height);
  123. mCaretDesc.texture = GUIManager::instance().getCaretTexture();
  124. mCaretSprite->update(mCaretDesc);
  125. }
  126. if(mSelectionShown)
  127. {
  128. Vector<Rect>::type selectionRects = getSelectionRects();
  129. INT32 diff = (INT32)(mSelectionSprites.size() - selectionRects.size());
  130. if(diff > 0)
  131. {
  132. for(INT32 i = 0; i < diff; i++)
  133. cm_delete(mSelectionSprites[i]);
  134. mSelectionSprites.erase(mSelectionSprites.begin() + selectionRects.size(), mSelectionSprites.end());
  135. }
  136. else if(diff < 0)
  137. {
  138. for(INT32 i = diff; i < 0; i++)
  139. {
  140. ImageSprite* newSprite = cm_new<ImageSprite>();
  141. mSelectionSprites.push_back(newSprite);
  142. }
  143. }
  144. UINT32 idx = 0;
  145. for(auto& sprite : mSelectionSprites)
  146. {
  147. IMAGE_SPRITE_DESC desc;
  148. desc.offset = Int2(selectionRects[idx].x, selectionRects[idx].y);
  149. desc.width = selectionRects[idx].width;
  150. desc.height = selectionRects[idx].height;
  151. desc.clipRect = Rect(0, 0, textDesc.width, textDesc.height);
  152. desc.texture = GUIManager::instance().getTextSelectionTexture();
  153. sprite->update(desc);
  154. idx++;
  155. }
  156. }
  157. }
  158. Sprite* GUIInputBox::renderElemToSprite(UINT32 renderElemIdx, UINT32& localRenderElemIdx) const
  159. {
  160. UINT32 oldNumElements = 0;
  161. UINT32 newNumElements = oldNumElements + mTextSprite->getNumRenderElements();
  162. if(renderElemIdx < newNumElements)
  163. {
  164. localRenderElemIdx = renderElemIdx - oldNumElements;
  165. return mTextSprite;
  166. }
  167. oldNumElements = newNumElements;
  168. newNumElements += mImageSprite->getNumRenderElements();
  169. if(renderElemIdx < newNumElements)
  170. {
  171. localRenderElemIdx = renderElemIdx - oldNumElements;
  172. return mImageSprite;
  173. }
  174. if(mCaretShown && GUIManager::instance().getCaretBlinkState())
  175. {
  176. oldNumElements = newNumElements;
  177. newNumElements += mImageSprite->getNumRenderElements();
  178. if(renderElemIdx < newNumElements)
  179. {
  180. localRenderElemIdx = renderElemIdx - oldNumElements;
  181. return mImageSprite;
  182. }
  183. }
  184. if(mSelectionShown)
  185. {
  186. for(auto& selectionSprite : mSelectionSprites)
  187. {
  188. oldNumElements = newNumElements;
  189. newNumElements += selectionSprite->getNumRenderElements();
  190. if(renderElemIdx < newNumElements)
  191. {
  192. localRenderElemIdx = renderElemIdx - oldNumElements;
  193. return selectionSprite;
  194. }
  195. }
  196. }
  197. localRenderElemIdx = renderElemIdx;
  198. return nullptr;
  199. }
  200. UINT32 GUIInputBox::_getOptimalWidth() const
  201. {
  202. if(mImageDesc.texture != nullptr)
  203. {
  204. return mImageDesc.texture->getTexture()->getWidth();
  205. }
  206. return 0;
  207. }
  208. UINT32 GUIInputBox::_getOptimalHeight() const
  209. {
  210. if(mImageDesc.texture != nullptr)
  211. {
  212. return mImageDesc.texture->getTexture()->getHeight();
  213. }
  214. return 0;
  215. }
  216. UINT32 GUIInputBox::_getRenderElementDepth(UINT32 renderElementIdx) const
  217. {
  218. UINT32 localRenderElementIdx;
  219. Sprite* sprite = renderElemToSprite(renderElementIdx, localRenderElementIdx);
  220. if(sprite == mImageSprite)
  221. return _getDepth();
  222. else if(sprite == mTextSprite || sprite == mCaretSprite)
  223. return _getDepth() + 2;
  224. else // Selection sprites
  225. return _getDepth() + 1;
  226. }
  227. void GUIInputBox::fillBuffer(UINT8* vertices, UINT8* uv, UINT32* indices, UINT32 startingQuad, UINT32 maxNumQuads,
  228. UINT32 vertexStride, UINT32 indexStride, UINT32 renderElementIdx) const
  229. {
  230. UINT32 localRenderElementIdx;
  231. Sprite* sprite = renderElemToSprite(renderElementIdx, localRenderElementIdx);
  232. sprite->fillBuffer(vertices, uv, indices, startingQuad, maxNumQuads, vertexStride, indexStride, localRenderElementIdx);
  233. }
  234. bool GUIInputBox::mouseEvent(const GUIMouseEvent& ev)
  235. {
  236. if(ev.getType() == GUIMouseEventType::MouseOver)
  237. {
  238. mImageDesc.texture = mStyle->hover.texture;
  239. markAsDirty();
  240. if(!mInputCursorSet)
  241. {
  242. Cursor::setCursor(CursorType::IBeam);
  243. mInputCursorSet = true;
  244. }
  245. return true;
  246. }
  247. else if(ev.getType() == GUIMouseEventType::MouseOut)
  248. {
  249. mImageDesc.texture = mStyle->normal.texture;
  250. markAsDirty();
  251. if(!mDragInProgress && mInputCursorSet)
  252. {
  253. Cursor::setCursor(CursorType::Arrow);
  254. mInputCursorSet = false;
  255. }
  256. return true;
  257. }
  258. else if(ev.getType() == GUIMouseEventType::MouseDown)
  259. {
  260. mImageDesc.texture = mStyle->active.texture;
  261. showCaret(getCharAtPosition(ev.getPosition()));
  262. clearSelection();
  263. markAsDirty();
  264. return true;
  265. }
  266. else if(ev.getType() == GUIMouseEventType::MouseUp)
  267. {
  268. mImageDesc.texture = mStyle->hover.texture;
  269. markAsDirty();
  270. return true;
  271. }
  272. else if(ev.getType() == GUIMouseEventType::MouseDragStart)
  273. {
  274. mDragInProgress = true;
  275. return true;
  276. }
  277. else if(ev.getType() == GUIMouseEventType::MouseDragEnd)
  278. {
  279. mDragInProgress = false;
  280. if(ev.getMouseOverElement() != this && mInputCursorSet)
  281. {
  282. Cursor::setCursor(CursorType::Arrow);
  283. mInputCursorSet = false;
  284. }
  285. return true;
  286. }
  287. else if(ev.getType() == GUIMouseEventType::MouseDrag)
  288. {
  289. // TODO - Update selection
  290. // - If mouse is over control, place selection marker there (make sure start < end)
  291. // - Else move the selection by a certain amount of pixels depending on drag amount
  292. markAsDirty();
  293. }
  294. return false;
  295. }
  296. bool GUIInputBox::buttonEvent(const GUIButtonEvent& ev)
  297. {
  298. if(ev.getType() == GUIKeyEventType::KeyDown)
  299. {
  300. if(ev.getKey() == BC_BACK)
  301. {
  302. if(mText.size() > 0)
  303. {
  304. if(mSelectionShown)
  305. {
  306. mText.erase(mSelectionStart, mSelectionEnd);
  307. mCaretPos = mSelectionStart;
  308. clearSelection();
  309. }
  310. else
  311. {
  312. mText.erase(mCaretPos);
  313. }
  314. markAsDirty();
  315. }
  316. return true;
  317. }
  318. // TODO - Handle newline if it's a multiline control
  319. // TODO - left+right arrow to move the cursor
  320. // TODO - shift + left + right arrow to select
  321. // TODO - ctrl + a to select all
  322. // TODO - ctrl + c, ctrl + v, ctrl + x to copy/cut/paste
  323. }
  324. else if(ev.getType() == GUIKeyEventType::TextInput)
  325. {
  326. if(mSelectionShown)
  327. {
  328. mText.erase(mSelectionStart, mSelectionEnd);
  329. mCaretPos = mSelectionStart;
  330. clearSelection();
  331. }
  332. mText.insert(mText.begin() + mCaretPos, ev.getInputChar());
  333. mCaretPos++;
  334. markAsDirty();
  335. return true;
  336. }
  337. return false;
  338. }
  339. bool GUIInputBox::commandEvent(const GUICommandEvent& ev)
  340. {
  341. if(ev.getType() == GUICommandEventType::Redraw)
  342. {
  343. markAsDirty();
  344. return true;
  345. }
  346. return false;
  347. }
  348. void GUIInputBox::showCaret(CM::UINT32 charIdx)
  349. {
  350. mCaretPos = charIdx;
  351. mCaretShown = true;
  352. markAsDirty();
  353. }
  354. void GUIInputBox::clearCaret()
  355. {
  356. mCaretShown = false;
  357. markAsDirty();
  358. }
  359. Int2 GUIInputBox::getCaretPosition() const
  360. {
  361. // TODO
  362. return Int2(0, 0);
  363. }
  364. UINT32 GUIInputBox::getCaretHeight() const
  365. {
  366. // TODO
  367. return 8;
  368. }
  369. void GUIInputBox::showSelection(UINT32 startChar, UINT32 endChar)
  370. {
  371. mSelectionStart = startChar;
  372. mSelectionEnd = endChar;
  373. mSelectionShown = true;
  374. markAsDirty();
  375. }
  376. void GUIInputBox::clearSelection()
  377. {
  378. for(auto& sprite : mSelectionSprites)
  379. cm_delete(sprite);
  380. mSelectionSprites.clear();
  381. mSelectionShown = false;
  382. markAsDirty();
  383. }
  384. Vector<Rect>::type GUIInputBox::getSelectionRects() const
  385. {
  386. // TODO
  387. return Vector<Rect>::type();
  388. }
  389. UINT32 GUIInputBox::getCharAtPosition(const Int2& pos) const
  390. {
  391. Rect textBounds = getTextBounds();
  392. //std::shared_ptr<TextUtility::TextData> textData =
  393. // TextUtility::getTextData(mText, mStyle->font, mStyle->fontSize,
  394. // textBounds.width, textBounds.height, mIsMultiline);
  395. //textData->getLines()
  396. // TODO
  397. return 0;
  398. }
  399. CM::Rect GUIInputBox::getTextBounds() const
  400. {
  401. Rect textBounds = mBounds;
  402. textBounds.x += mStyle->margins.left + mStyle->contentOffset.left;
  403. textBounds.y += mStyle->margins.top + mStyle->contentOffset.top;
  404. textBounds.width = (UINT32)std::max(0, (INT32)textBounds.width -
  405. (INT32)(mStyle->margins.left + mStyle->margins.right + mStyle->contentOffset.left + mStyle->contentOffset.right));
  406. textBounds.height = (UINT32)std::max(0, (INT32)textBounds.height -
  407. (INT32)(mStyle->margins.top + mStyle->margins.bottom + mStyle->contentOffset.top + mStyle->contentOffset.bottom));
  408. return textBounds;
  409. }
  410. void GUIInputBox::_setFocus(bool focus)
  411. {
  412. if(focus)
  413. {
  414. mImageDesc.texture = mStyle->focused.texture;
  415. markAsDirty();
  416. }
  417. else
  418. {
  419. mImageDesc.texture = mStyle->normal.texture;
  420. clearCaret();
  421. clearSelection();
  422. markAsDirty();
  423. }
  424. }
  425. }