BsGUIButtonBase.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  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 "BsGUILayoutOptions.h"
  8. #include "BsGUIMouseEvent.h"
  9. #include "BsGUIHelper.h"
  10. #include "CmTexture.h"
  11. using namespace CamelotFramework;
  12. namespace BansheeEngine
  13. {
  14. GUIButtonBase::GUIButtonBase(GUIWidget& parent, const GUIElementStyle* style, const GUIContent& content, const GUILayoutOptions& layoutOptions)
  15. :GUIElement(parent, style, layoutOptions), mContent(content), mContentImageSprite(nullptr), mActiveState(GUIButtonState::Normal)
  16. {
  17. mImageSprite = cm_new<ImageSprite, PoolAlloc>();
  18. mTextSprite = cm_new<TextSprite, PoolAlloc>();
  19. HSpriteTexture contentTex = content.getImage();
  20. if(contentTex != nullptr && contentTex.isLoaded())
  21. mContentImageSprite = cm_new<ImageSprite, PoolAlloc>();
  22. mActiveTexture = mStyle->normal.texture;
  23. if(mActiveTexture != nullptr && mActiveTexture.isLoaded() &&
  24. mActiveTexture->getTexture() != nullptr && mActiveTexture->getTexture().isLoaded())
  25. {
  26. mImageDesc.width = mActiveTexture->getTexture()->getWidth();
  27. mImageDesc.height = mActiveTexture->getTexture()->getHeight();
  28. }
  29. mImageDesc.borderLeft = mStyle->border.left;
  30. mImageDesc.borderRight = mStyle->border.right;
  31. mImageDesc.borderTop = mStyle->border.top;
  32. mImageDesc.borderBottom = mStyle->border.bottom;
  33. mLocStringUpdatedConn = mContent.getText().addOnStringModifiedCallback(boost::bind(&GUIButtonBase::markContentAsDirty, this));
  34. }
  35. GUIButtonBase::~GUIButtonBase()
  36. {
  37. mLocStringUpdatedConn.disconnect();
  38. cm_delete<PoolAlloc>(mTextSprite);
  39. cm_delete<PoolAlloc>(mImageSprite);
  40. if(mContentImageSprite != nullptr)
  41. cm_delete<PoolAlloc>(mContentImageSprite);
  42. }
  43. void GUIButtonBase::setContent(const GUIContent& content)
  44. {
  45. mLocStringUpdatedConn.disconnect();
  46. mLocStringUpdatedConn = content.getText().addOnStringModifiedCallback(boost::bind(&GUIButtonBase::markContentAsDirty, this));
  47. mContent = content;
  48. markContentAsDirty();
  49. }
  50. void GUIButtonBase::_setOn(bool on)
  51. {
  52. if(on)
  53. setState((GUIButtonState)((INT32)mActiveState | 0x10));
  54. else
  55. setState((GUIButtonState)((INT32)mActiveState & (~0x10)));
  56. }
  57. bool GUIButtonBase::_isOn() const
  58. {
  59. return ((INT32)mActiveState & 0x10) != 0;
  60. }
  61. UINT32 GUIButtonBase::getNumRenderElements() const
  62. {
  63. UINT32 numElements = mImageSprite->getNumRenderElements();
  64. numElements += mTextSprite->getNumRenderElements();
  65. if(mContentImageSprite != nullptr)
  66. numElements += mContentImageSprite->getNumRenderElements();
  67. return numElements;
  68. }
  69. const GUIMaterialInfo& GUIButtonBase::getMaterial(UINT32 renderElementIdx) const
  70. {
  71. UINT32 textSpriteIdx = mImageSprite->getNumRenderElements();
  72. UINT32 contentImgSpriteIdx = textSpriteIdx + mTextSprite->getNumRenderElements();
  73. if(renderElementIdx >= contentImgSpriteIdx)
  74. return mContentImageSprite->getMaterial(contentImgSpriteIdx - renderElementIdx);
  75. else if(renderElementIdx >= textSpriteIdx)
  76. return mTextSprite->getMaterial(textSpriteIdx - renderElementIdx);
  77. else
  78. return mImageSprite->getMaterial(renderElementIdx);
  79. }
  80. UINT32 GUIButtonBase::getNumQuads(UINT32 renderElementIdx) const
  81. {
  82. UINT32 textSpriteIdx = mImageSprite->getNumRenderElements();
  83. UINT32 contentImgSpriteIdx = textSpriteIdx + mTextSprite->getNumRenderElements();
  84. UINT32 numQuads = 0;
  85. if(renderElementIdx >= contentImgSpriteIdx)
  86. numQuads = mContentImageSprite->getNumQuads(contentImgSpriteIdx - renderElementIdx);
  87. else if(renderElementIdx >= textSpriteIdx)
  88. numQuads = mTextSprite->getNumQuads(textSpriteIdx - renderElementIdx);
  89. else
  90. numQuads = mImageSprite->getNumQuads(renderElementIdx);
  91. return numQuads;
  92. }
  93. void GUIButtonBase::updateRenderElementsInternal()
  94. {
  95. mImageDesc.width = mWidth;
  96. mImageDesc.height = mHeight;
  97. if(mActiveTexture != nullptr && mActiveTexture.isLoaded())
  98. mImageDesc.texture = mActiveTexture.getInternalPtr();
  99. mImageSprite->update(mImageDesc);
  100. mTextSprite->update(getTextDesc());
  101. if(mContentImageSprite != nullptr)
  102. {
  103. IMAGE_SPRITE_DESC contentImgDesc;
  104. contentImgDesc.texture = mContent.getImage().getInternalPtr();
  105. contentImgDesc.width = mContent.getImage()->getTexture()->getWidth();
  106. contentImgDesc.height = mContent.getImage()->getTexture()->getHeight();
  107. mContentImageSprite->update(contentImgDesc);
  108. }
  109. GUIElement::updateRenderElementsInternal();
  110. }
  111. void GUIButtonBase::updateClippedBounds()
  112. {
  113. mClippedBounds = mImageSprite->getBounds(mOffset, mClipRect);
  114. }
  115. Vector2I GUIButtonBase::_getOptimalSize() const
  116. {
  117. UINT32 imageWidth = 0;
  118. UINT32 imageHeight = 0;
  119. if(mActiveTexture != nullptr && mActiveTexture.isLoaded())
  120. {
  121. imageWidth = mActiveTexture->getTexture()->getWidth();
  122. imageHeight = mActiveTexture->getTexture()->getHeight();
  123. }
  124. Vector2I contentSize = GUIHelper::calcOptimalContentsSize(mContent, *mStyle, _getLayoutOptions());
  125. UINT32 contentWidth = std::max(imageWidth, (UINT32)contentSize.x);
  126. UINT32 contentHeight = std::max(imageHeight, (UINT32)contentSize.y);
  127. return Vector2I(contentWidth, contentHeight);
  128. }
  129. UINT32 GUIButtonBase::_getRenderElementDepth(UINT32 renderElementIdx) const
  130. {
  131. UINT32 textSpriteIdx = mImageSprite->getNumRenderElements();
  132. UINT32 contentImgSpriteIdx = textSpriteIdx + mTextSprite->getNumRenderElements();
  133. if(renderElementIdx >= contentImgSpriteIdx)
  134. return _getDepth();
  135. else if(renderElementIdx >= textSpriteIdx)
  136. return _getDepth();
  137. else
  138. return _getDepth() + 1;
  139. }
  140. void GUIButtonBase::fillBuffer(UINT8* vertices, UINT8* uv, UINT32* indices, UINT32 startingQuad, UINT32 maxNumQuads,
  141. UINT32 vertexStride, UINT32 indexStride, UINT32 renderElementIdx) const
  142. {
  143. UINT32 textSpriteIdx = mImageSprite->getNumRenderElements();
  144. UINT32 contentImgSpriteIdx = textSpriteIdx + mTextSprite->getNumRenderElements();
  145. if(renderElementIdx < textSpriteIdx)
  146. {
  147. mImageSprite->fillBuffer(vertices, uv, indices, startingQuad, maxNumQuads,
  148. vertexStride, indexStride, renderElementIdx, mOffset, mClipRect);
  149. return;
  150. }
  151. RectI contentBounds = getContentBounds();
  152. RectI contentClipRect = getContentClipRect();
  153. RectI textBounds = mTextSprite->getBounds(Vector2I(), RectI());
  154. Vector2I textOffset;
  155. RectI textClipRect;
  156. Vector2I imageOffset;
  157. RectI imageClipRect;
  158. if(mContentImageSprite != nullptr)
  159. {
  160. RectI imageBounds = mContentImageSprite->getBounds(Vector2I(), RectI());
  161. UINT32 freeWidth = (UINT32)std::max(0, contentBounds.width - textBounds.width - imageBounds.width);
  162. INT32 imageXOffset = (INT32)(freeWidth / 2);
  163. if(mStyle->imagePosition == GUIImagePosition::Right)
  164. {
  165. INT32 imageReservedWidth = std::max(0, contentBounds.width - textBounds.width);
  166. textOffset = Vector2I(contentBounds.x, contentBounds.y);
  167. textClipRect = contentClipRect;
  168. textClipRect.width = std::min(contentBounds.width - imageReservedWidth, textClipRect.width);
  169. imageOffset = Vector2I(contentBounds.x + textBounds.width + imageXOffset, contentBounds.y);
  170. imageClipRect = contentClipRect;
  171. imageClipRect.x -= textBounds.width + imageXOffset;
  172. }
  173. else
  174. {
  175. INT32 imageReservedWidth = imageBounds.width + imageXOffset;
  176. imageOffset = Vector2I(contentBounds.x + imageXOffset, contentBounds.y);
  177. imageClipRect = contentClipRect;
  178. imageClipRect.x -= imageXOffset;
  179. imageClipRect.width = std::min(imageReservedWidth, imageClipRect.width);
  180. textOffset = Vector2I(contentBounds.x + imageReservedWidth, contentBounds.y);
  181. textClipRect = contentClipRect;
  182. textClipRect.x -= imageReservedWidth;
  183. }
  184. INT32 imageYOffset = (contentBounds.height - imageBounds.height) / 2;
  185. imageClipRect.y -= imageYOffset;
  186. imageOffset.y += imageYOffset;
  187. }
  188. else
  189. {
  190. textOffset = Vector2I(contentBounds.x, contentBounds.y);
  191. textClipRect = contentClipRect;
  192. }
  193. if(renderElementIdx >= contentImgSpriteIdx)
  194. {
  195. mContentImageSprite->fillBuffer(vertices, uv, indices, startingQuad, maxNumQuads,
  196. vertexStride, indexStride, contentImgSpriteIdx - renderElementIdx, imageOffset, imageClipRect);
  197. }
  198. else
  199. {
  200. mTextSprite->fillBuffer(vertices, uv, indices, startingQuad, maxNumQuads,
  201. vertexStride, indexStride, textSpriteIdx - renderElementIdx, textOffset, textClipRect);
  202. }
  203. }
  204. bool GUIButtonBase::mouseEvent(const GUIMouseEvent& ev)
  205. {
  206. if(ev.getType() == GUIMouseEventType::MouseOver)
  207. {
  208. setState(_isOn() ? GUIButtonState::HoverOn : GUIButtonState::Hover);
  209. if(!onHover.empty())
  210. onHover();
  211. return true;
  212. }
  213. else if(ev.getType() == GUIMouseEventType::MouseOut)
  214. {
  215. setState(_isOn() ? GUIButtonState::NormalOn : GUIButtonState::Normal);
  216. if(!onOut.empty())
  217. onOut();
  218. return true;
  219. }
  220. else if(ev.getType() == GUIMouseEventType::MouseDown)
  221. {
  222. setState(_isOn() ? GUIButtonState::ActiveOn : GUIButtonState::Active);
  223. return true;
  224. }
  225. else if(ev.getType() == GUIMouseEventType::MouseUp)
  226. {
  227. setState(_isOn() ? GUIButtonState::HoverOn : GUIButtonState::Hover);
  228. if(!onClick.empty())
  229. onClick();
  230. return true;
  231. }
  232. return false;
  233. }
  234. TEXT_SPRITE_DESC GUIButtonBase::getTextDesc() const
  235. {
  236. TEXT_SPRITE_DESC textDesc;
  237. textDesc.text = mContent.getText();
  238. textDesc.font = mStyle->font;
  239. textDesc.fontSize = mStyle->fontSize;
  240. RectI textBounds = getContentBounds();
  241. textDesc.width = textBounds.width;
  242. textDesc.height = textBounds.height;
  243. textDesc.horzAlign = mStyle->textHorzAlign;
  244. textDesc.vertAlign = mStyle->textVertAlign;
  245. return textDesc;
  246. }
  247. void GUIButtonBase::setState(GUIButtonState state)
  248. {
  249. switch(state)
  250. {
  251. case GUIButtonState::Normal:
  252. mActiveTexture = mStyle->normal.texture;
  253. break;
  254. case GUIButtonState::Hover:
  255. mActiveTexture = mStyle->hover.texture;
  256. break;
  257. case GUIButtonState::Active:
  258. mActiveTexture = mStyle->active.texture;
  259. break;
  260. case GUIButtonState::Focused:
  261. mActiveTexture = mStyle->focused.texture;
  262. break;
  263. case GUIButtonState::NormalOn:
  264. mActiveTexture = mStyle->normalOn.texture;
  265. break;
  266. case GUIButtonState::HoverOn:
  267. mActiveTexture = mStyle->hoverOn.texture;
  268. break;
  269. case GUIButtonState::ActiveOn:
  270. mActiveTexture = mStyle->activeOn.texture;
  271. break;
  272. case GUIButtonState::FocusedOn:
  273. mActiveTexture = mStyle->focusedOn.texture;
  274. break;
  275. }
  276. markContentAsDirty();
  277. mActiveState = state;
  278. }
  279. }