BsGUIButtonBase.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "GUI/BsGUIButtonBase.h"
  4. #include "2D/BsImageSprite.h"
  5. #include "GUI/BsGUISkin.h"
  6. #include "2D/BsSpriteTexture.h"
  7. #include "2D/BsTextSprite.h"
  8. #include "GUI/BsGUIDimensions.h"
  9. #include "GUI/BsGUIMouseEvent.h"
  10. #include "GUI/BsGUIHelper.h"
  11. namespace bs
  12. {
  13. GUIButtonBase::GUIButtonBase(const String& styleName, const GUIContent& content, const GUIDimensions& dimensions)
  14. : GUIElement(styleName, dimensions), mContentImageSprite(nullptr), mActiveState(GUIElementState::Normal)
  15. , mContent(content)
  16. {
  17. mImageSprite = bs_new<ImageSprite>();
  18. mTextSprite = bs_new<TextSprite>();
  19. refreshContentSprite();
  20. }
  21. GUIButtonBase::~GUIButtonBase()
  22. {
  23. bs_delete(mTextSprite);
  24. bs_delete(mImageSprite);
  25. if(mContentImageSprite != nullptr)
  26. bs_delete(mContentImageSprite);
  27. }
  28. void GUIButtonBase::setContent(const GUIContent& content)
  29. {
  30. Vector2I origSize = mDimensions.calculateSizeRange(_getOptimalSize()).optimal;
  31. mContent = content;
  32. refreshContentSprite();
  33. Vector2I newSize = mDimensions.calculateSizeRange(_getOptimalSize()).optimal;
  34. if (origSize != newSize)
  35. _markLayoutAsDirty();
  36. else
  37. _markContentAsDirty();
  38. }
  39. void GUIButtonBase::_setOn(bool on)
  40. {
  41. if(on)
  42. _setState((GUIElementState)((INT32)mActiveState | 0x10));
  43. else
  44. _setState((GUIElementState)((INT32)mActiveState & (~0x10)));
  45. }
  46. bool GUIButtonBase::_isOn() const
  47. {
  48. return ((INT32)mActiveState & 0x10) != 0;
  49. }
  50. UINT32 GUIButtonBase::_getNumRenderElements() const
  51. {
  52. UINT32 numElements = mImageSprite->getNumRenderElements();
  53. numElements += mTextSprite->getNumRenderElements();
  54. if(mContentImageSprite != nullptr)
  55. numElements += mContentImageSprite->getNumRenderElements();
  56. return numElements;
  57. }
  58. const SpriteMaterialInfo& GUIButtonBase::_getMaterial(UINT32 renderElementIdx, SpriteMaterial** material) const
  59. {
  60. UINT32 textSpriteIdx = mImageSprite->getNumRenderElements();
  61. UINT32 contentImgSpriteIdx = textSpriteIdx + mTextSprite->getNumRenderElements();
  62. if (renderElementIdx >= contentImgSpriteIdx)
  63. {
  64. *material = mContentImageSprite->getMaterial(contentImgSpriteIdx - renderElementIdx);
  65. return mContentImageSprite->getMaterialInfo(contentImgSpriteIdx - renderElementIdx);
  66. }
  67. else if (renderElementIdx >= textSpriteIdx)
  68. {
  69. *material = mTextSprite->getMaterial(textSpriteIdx - renderElementIdx);
  70. return mTextSprite->getMaterialInfo(textSpriteIdx - renderElementIdx);
  71. }
  72. else
  73. {
  74. *material = mImageSprite->getMaterial(renderElementIdx);
  75. return mImageSprite->getMaterialInfo(renderElementIdx);
  76. }
  77. }
  78. void GUIButtonBase::_getMeshInfo(UINT32 renderElementIdx, UINT32& numVertices, UINT32& numIndices, GUIMeshType& type) const
  79. {
  80. UINT32 textSpriteIdx = mImageSprite->getNumRenderElements();
  81. UINT32 contentImgSpriteIdx = textSpriteIdx + mTextSprite->getNumRenderElements();
  82. UINT32 numQuads = 0;
  83. if(renderElementIdx >= contentImgSpriteIdx)
  84. numQuads = mContentImageSprite->getNumQuads(contentImgSpriteIdx - renderElementIdx);
  85. else if(renderElementIdx >= textSpriteIdx)
  86. numQuads = mTextSprite->getNumQuads(textSpriteIdx - renderElementIdx);
  87. else
  88. numQuads = mImageSprite->getNumQuads(renderElementIdx);
  89. numVertices = numQuads * 4;
  90. numIndices = numQuads * 6;
  91. type = GUIMeshType::Triangle;
  92. }
  93. void GUIButtonBase::updateRenderElementsInternal()
  94. {
  95. mImageDesc.width = mLayoutData.area.width;
  96. mImageDesc.height = mLayoutData.area.height;
  97. const HSpriteTexture& activeTex = getActiveTexture();
  98. if (SpriteTexture::checkIsLoaded(activeTex))
  99. mImageDesc.texture = activeTex.getInternalPtr();
  100. else
  101. mImageDesc.texture = nullptr;
  102. mImageDesc.borderLeft = _getStyle()->border.left;
  103. mImageDesc.borderRight = _getStyle()->border.right;
  104. mImageDesc.borderTop = _getStyle()->border.top;
  105. mImageDesc.borderBottom = _getStyle()->border.bottom;
  106. mImageDesc.color = getTint();
  107. mImageSprite->update(mImageDesc, (UINT64)_getParentWidget());
  108. mTextSprite->update(getTextDesc(), (UINT64)_getParentWidget());
  109. if(mContentImageSprite != nullptr)
  110. {
  111. Rect2I contentBounds = getCachedContentBounds();
  112. HSpriteTexture image = mContent.getImage(mActiveState);
  113. UINT32 contentWidth = image->getWidth();
  114. UINT32 contentHeight = image->getHeight();
  115. UINT32 contentMaxWidth = std::min((UINT32)contentBounds.width, contentWidth);
  116. UINT32 contentMaxHeight = std::min((UINT32)contentBounds.height, contentHeight);
  117. float horzRatio = contentMaxWidth / (float)contentWidth;
  118. float vertRatio = contentMaxHeight / (float)contentHeight;
  119. if (horzRatio < vertRatio)
  120. {
  121. contentWidth = Math::roundToInt(contentWidth * horzRatio);
  122. contentHeight = Math::roundToInt(contentHeight * horzRatio);
  123. }
  124. else
  125. {
  126. contentWidth = Math::roundToInt(contentWidth * vertRatio);
  127. contentHeight = Math::roundToInt(contentHeight * vertRatio);
  128. }
  129. IMAGE_SPRITE_DESC contentImgDesc;
  130. contentImgDesc.texture = image.getInternalPtr();
  131. contentImgDesc.width = contentWidth;
  132. contentImgDesc.height = contentHeight;
  133. contentImgDesc.color = getTint();
  134. mContentImageSprite->update(contentImgDesc, (UINT64)_getParentWidget());
  135. }
  136. GUIElement::updateRenderElementsInternal();
  137. }
  138. Vector2I GUIButtonBase::_getOptimalSize() const
  139. {
  140. UINT32 imageWidth = 0;
  141. UINT32 imageHeight = 0;
  142. const HSpriteTexture& activeTex = getActiveTexture();
  143. if(SpriteTexture::checkIsLoaded(activeTex))
  144. {
  145. imageWidth = activeTex->getWidth();
  146. imageHeight = activeTex->getHeight();
  147. }
  148. Vector2I contentSize = GUIHelper::calcOptimalContentsSize(mContent, *_getStyle(), _getDimensions(), mActiveState);
  149. UINT32 contentWidth = std::max(imageWidth, (UINT32)contentSize.x);
  150. UINT32 contentHeight = std::max(imageHeight, (UINT32)contentSize.y);
  151. return Vector2I(contentWidth, contentHeight);
  152. }
  153. UINT32 GUIButtonBase::_getRenderElementDepth(UINT32 renderElementIdx) const
  154. {
  155. UINT32 textSpriteIdx = mImageSprite->getNumRenderElements();
  156. UINT32 contentImgSpriteIdx = textSpriteIdx + mTextSprite->getNumRenderElements();
  157. if(renderElementIdx >= contentImgSpriteIdx)
  158. return _getDepth();
  159. else if(renderElementIdx >= textSpriteIdx)
  160. return _getDepth();
  161. else
  162. return _getDepth() + 1;
  163. }
  164. UINT32 GUIButtonBase::_getRenderElementDepthRange() const
  165. {
  166. return 2;
  167. }
  168. void GUIButtonBase::_fillBuffer(UINT8* vertices, UINT32* indices, UINT32 vertexOffset, UINT32 indexOffset,
  169. UINT32 maxNumVerts, UINT32 maxNumIndices, UINT32 renderElementIdx) const
  170. {
  171. UINT8* uvs = vertices + sizeof(Vector2);
  172. UINT32 vertexStride = sizeof(Vector2) * 2;
  173. UINT32 indexStride = sizeof(UINT32);
  174. UINT32 textSpriteIdx = mImageSprite->getNumRenderElements();
  175. UINT32 contentImgSpriteIdx = textSpriteIdx + mTextSprite->getNumRenderElements();
  176. if(renderElementIdx < textSpriteIdx)
  177. {
  178. Vector2I offset(mLayoutData.area.x, mLayoutData.area.y);
  179. mImageSprite->fillBuffer(vertices, uvs, indices, vertexOffset, indexOffset, maxNumVerts, maxNumIndices,
  180. vertexStride, indexStride, renderElementIdx, offset, mLayoutData.getLocalClipRect());
  181. return;
  182. }
  183. Rect2I contentBounds = getCachedContentBounds();
  184. Rect2I contentClipRect = getCachedContentClipRect();
  185. Rect2I textBounds = mTextSprite->getBounds(Vector2I(), Rect2I());
  186. Vector2I textOffset;
  187. Rect2I textClipRect;
  188. Vector2I imageOffset;
  189. Rect2I imageClipRect;
  190. if(mContentImageSprite != nullptr)
  191. {
  192. Rect2I imageBounds = mContentImageSprite->getBounds(Vector2I(), Rect2I());
  193. INT32 imageXOffset = 0;
  194. INT32 textImageSpacing = 0;
  195. if (textBounds.width == 0)
  196. {
  197. UINT32 freeWidth = (UINT32)std::max(0, (INT32)contentBounds.width - (INT32)textBounds.width - (INT32)imageBounds.width);
  198. imageXOffset = (INT32)(freeWidth / 2);
  199. }
  200. else
  201. textImageSpacing = GUIContent::IMAGE_TEXT_SPACING;
  202. if(_getStyle()->imagePosition == GUIImagePosition::Right)
  203. {
  204. INT32 imageReservedWidth = std::max(0, (INT32)contentBounds.width - (INT32)textBounds.width);
  205. textOffset = Vector2I(contentBounds.x, contentBounds.y);
  206. textClipRect = contentClipRect;
  207. textClipRect.width = std::min(contentBounds.width - imageReservedWidth, textClipRect.width);
  208. imageOffset = Vector2I(contentBounds.x + textBounds.width + imageXOffset + textImageSpacing, contentBounds.y);
  209. imageClipRect = contentClipRect;
  210. imageClipRect.x -= textBounds.width + imageXOffset;
  211. }
  212. else
  213. {
  214. INT32 imageReservedWidth = imageBounds.width + imageXOffset;
  215. imageOffset = Vector2I(contentBounds.x + imageXOffset, contentBounds.y);
  216. imageClipRect = contentClipRect;
  217. imageClipRect.x -= imageXOffset;
  218. imageClipRect.width = std::min(imageReservedWidth, (INT32)imageClipRect.width);
  219. textOffset = Vector2I(contentBounds.x + imageReservedWidth + textImageSpacing, contentBounds.y);
  220. textClipRect = contentClipRect;
  221. textClipRect.x -= imageReservedWidth;
  222. }
  223. INT32 imageYOffset = (contentBounds.height - imageBounds.height) / 2;
  224. imageClipRect.y -= imageYOffset;
  225. imageOffset.y += imageYOffset;
  226. }
  227. else
  228. {
  229. textOffset = Vector2I(contentBounds.x, contentBounds.y);
  230. textClipRect = contentClipRect;
  231. }
  232. if(renderElementIdx >= contentImgSpriteIdx)
  233. {
  234. mContentImageSprite->fillBuffer(vertices, uvs, indices, vertexOffset, indexOffset, maxNumVerts, maxNumIndices,
  235. vertexStride, indexStride, contentImgSpriteIdx - renderElementIdx, imageOffset, imageClipRect);
  236. }
  237. else
  238. {
  239. mTextSprite->fillBuffer(vertices, uvs, indices, vertexOffset, indexOffset, maxNumVerts, maxNumIndices,
  240. vertexStride, indexStride, textSpriteIdx - renderElementIdx, textOffset, textClipRect);
  241. }
  242. }
  243. bool GUIButtonBase::_mouseEvent(const GUIMouseEvent& ev)
  244. {
  245. if(ev.getType() == GUIMouseEventType::MouseOver)
  246. {
  247. if (!_isDisabled())
  248. {
  249. _setState(_isOn() ? GUIElementState::HoverOn : GUIElementState::Hover);
  250. onHover();
  251. }
  252. return mBlockPointerEvents;
  253. }
  254. else if(ev.getType() == GUIMouseEventType::MouseOut)
  255. {
  256. if (!_isDisabled())
  257. {
  258. _setState(_isOn() ? GUIElementState::NormalOn : GUIElementState::Normal);
  259. onOut();
  260. }
  261. return mBlockPointerEvents;
  262. }
  263. else if(ev.getType() == GUIMouseEventType::MouseDown)
  264. {
  265. if (!_isDisabled())
  266. _setState(_isOn() ? GUIElementState::ActiveOn : GUIElementState::Active);
  267. return mBlockPointerEvents;
  268. }
  269. else if(ev.getType() == GUIMouseEventType::MouseUp)
  270. {
  271. if (!_isDisabled())
  272. {
  273. _setState(_isOn() ? GUIElementState::HoverOn : GUIElementState::Hover);
  274. onClick();
  275. }
  276. return mBlockPointerEvents;
  277. }
  278. else if (ev.getType() == GUIMouseEventType::MouseDoubleClick)
  279. {
  280. if (!_isDisabled())
  281. onDoubleClick();
  282. return mBlockPointerEvents;
  283. }
  284. return false;
  285. }
  286. WString GUIButtonBase::_getTooltip() const
  287. {
  288. return mContent.getTooltip();
  289. }
  290. void GUIButtonBase::refreshContentSprite()
  291. {
  292. HSpriteTexture contentTex = mContent.getImage(mActiveState);
  293. if (SpriteTexture::checkIsLoaded(contentTex))
  294. {
  295. if (mContentImageSprite == nullptr)
  296. mContentImageSprite = bs_new<ImageSprite>();
  297. }
  298. else
  299. {
  300. if (mContentImageSprite != nullptr)
  301. {
  302. bs_delete(mContentImageSprite);
  303. mContentImageSprite = nullptr;
  304. }
  305. }
  306. }
  307. TEXT_SPRITE_DESC GUIButtonBase::getTextDesc() const
  308. {
  309. TEXT_SPRITE_DESC textDesc;
  310. textDesc.text = mContent.getText();
  311. textDesc.font = _getStyle()->font;
  312. textDesc.fontSize = _getStyle()->fontSize;
  313. textDesc.color = getTint() * getActiveTextColor();
  314. Rect2I textBounds = getCachedContentBounds();
  315. textDesc.width = textBounds.width;
  316. textDesc.height = textBounds.height;
  317. textDesc.horzAlign = _getStyle()->textHorzAlign;
  318. textDesc.vertAlign = _getStyle()->textVertAlign;
  319. return textDesc;
  320. }
  321. void GUIButtonBase::_setState(GUIElementState state)
  322. {
  323. Vector2I origSize = mDimensions.calculateSizeRange(_getOptimalSize()).optimal;
  324. mActiveState = state;
  325. refreshContentSprite();
  326. Vector2I newSize = mDimensions.calculateSizeRange(_getOptimalSize()).optimal;
  327. if (origSize != newSize)
  328. _markLayoutAsDirty();
  329. else
  330. _markContentAsDirty();
  331. }
  332. const HSpriteTexture& GUIButtonBase::getActiveTexture() const
  333. {
  334. switch(mActiveState)
  335. {
  336. case GUIElementState::Normal:
  337. return _getStyle()->normal.texture;
  338. case GUIElementState::Hover:
  339. return _getStyle()->hover.texture;
  340. case GUIElementState::Active:
  341. return _getStyle()->active.texture;
  342. case GUIElementState::Focused:
  343. return _getStyle()->focused.texture;
  344. case GUIElementState::NormalOn:
  345. return _getStyle()->normalOn.texture;
  346. case GUIElementState::HoverOn:
  347. return _getStyle()->hoverOn.texture;
  348. case GUIElementState::ActiveOn:
  349. return _getStyle()->activeOn.texture;
  350. case GUIElementState::FocusedOn:
  351. return _getStyle()->focusedOn.texture;
  352. }
  353. return _getStyle()->normal.texture;
  354. }
  355. Color GUIButtonBase::getActiveTextColor() const
  356. {
  357. switch (mActiveState)
  358. {
  359. case GUIElementState::Normal:
  360. return _getStyle()->normal.textColor;
  361. case GUIElementState::Hover:
  362. return _getStyle()->hover.textColor;
  363. case GUIElementState::Active:
  364. return _getStyle()->active.textColor;
  365. case GUIElementState::Focused:
  366. return _getStyle()->focused.textColor;
  367. case GUIElementState::NormalOn:
  368. return _getStyle()->normalOn.textColor;
  369. case GUIElementState::HoverOn:
  370. return _getStyle()->hoverOn.textColor;
  371. case GUIElementState::ActiveOn:
  372. return _getStyle()->activeOn.textColor;
  373. case GUIElementState::FocusedOn:
  374. return _getStyle()->focusedOn.textColor;
  375. }
  376. return _getStyle()->normal.textColor;
  377. }
  378. }