BsGUIButtonBase.cpp 13 KB

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