BsGUIScrollArea.cpp 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. #include "BsGUIScrollArea.h"
  2. #include "BsGUIElementStyle.h"
  3. #include "BsGUISkin.h"
  4. #include "BsGUIWidget.h"
  5. #include "BsGUILayoutOptions.h"
  6. #include "BsGUILayout.h"
  7. #include "BsGUISkin.h"
  8. #include "BsGUIScrollBarVert.h"
  9. #include "BsGUIScrollBarHorz.h"
  10. #include "BsGUIMouseEvent.h"
  11. #include "CmException.h"
  12. using namespace CamelotFramework;
  13. namespace BansheeEngine
  14. {
  15. const UINT32 GUIScrollArea::ScrollBarWidth = 8;
  16. const UINT32 GUIScrollArea::MinHandleSize = 4;
  17. const UINT32 GUIScrollArea::WheelScrollAmount = 50;
  18. GUIScrollArea::GUIScrollArea(GUIWidget& parent, const GUIElementStyle* style, const GUILayoutOptions& layoutOptions)
  19. :GUIElement(parent, style, layoutOptions, false), mVertScroll(nullptr), mHorzScroll(nullptr), mVertOffset(0), mHorzOffset(0),
  20. mContentWidth(0), mContentHeight(0), mClippedContentWidth(0), mClippedContentHeight(0)
  21. {
  22. mContentLayout = &addLayoutYInternal(this);
  23. }
  24. GUIScrollArea::~GUIScrollArea()
  25. {
  26. }
  27. UINT32 GUIScrollArea::getNumRenderElements() const
  28. {
  29. return 0;
  30. }
  31. const HMaterial& GUIScrollArea::getMaterial(UINT32 renderElementIdx) const
  32. {
  33. CM_EXCEPT(InternalErrorException, "Trying to retrieve a material from an element with no render elements.");
  34. }
  35. UINT32 GUIScrollArea::getNumQuads(UINT32 renderElementIdx) const
  36. {
  37. return 0;
  38. }
  39. void GUIScrollArea::updateRenderElementsInternal()
  40. {
  41. GUIElement::updateRenderElementsInternal();
  42. }
  43. void GUIScrollArea::updateBounds()
  44. {
  45. mBounds = Rect(0, 0, 0, 0); // We don't want any mouse input for this element. This is just a container.
  46. }
  47. UINT32 GUIScrollArea::_getOptimalWidth() const
  48. {
  49. return mContentLayout->_getOptimalWidth();
  50. }
  51. UINT32 GUIScrollArea::_getOptimalHeight() const
  52. {
  53. return mContentLayout->_getOptimalHeight();
  54. }
  55. void GUIScrollArea::fillBuffer(UINT8* vertices, UINT8* uv, UINT32* indices, UINT32 startingQuad, UINT32 maxNumQuads,
  56. UINT32 vertexStride, UINT32 indexStride, UINT32 renderElementIdx) const
  57. { }
  58. void GUIScrollArea::_updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height,
  59. Rect clipRect, UINT8 widgetDepth, UINT16 areaDepth)
  60. {
  61. mContentLayout->_updateLayoutInternal(x, y, width, height, clipRect, widgetDepth, areaDepth);
  62. UINT32 contentWidth = mContentLayout->_getActualWidth();
  63. UINT32 contentHeight = mContentLayout->_getActualHeight();
  64. mClippedContentWidth = width;
  65. mClippedContentHeight = height;
  66. bool hasScrollbars = false;
  67. Rect layoutClipRect = clipRect;
  68. bool hasHorzScrollbar = false;
  69. if(contentWidth > mWidth)
  70. {
  71. // Make room for scrollbar
  72. mClippedContentHeight = (UINT32)std::max(0, (INT32)height - (INT32)ScrollBarWidth);
  73. layoutClipRect.height = mClippedContentHeight;
  74. hasHorzScrollbar = true;
  75. }
  76. if(contentHeight > mClippedContentHeight)
  77. {
  78. // Make room for scrollbar
  79. mClippedContentWidth = (UINT32)std::max(0, (INT32)width - (INT32)ScrollBarWidth);
  80. layoutClipRect.width = mClippedContentWidth;
  81. if(!hasHorzScrollbar && contentWidth > mClippedContentWidth) // Since width has been reduced, we need to check if we require the horizontal scrollbar
  82. {
  83. // Make room for scrollbar
  84. mClippedContentHeight = (UINT32)std::max(0, (INT32)height - (INT32)ScrollBarWidth);
  85. layoutClipRect.height = mClippedContentHeight;
  86. }
  87. }
  88. // Add/remove/update vertical scrollbar as needed
  89. if(contentHeight > mClippedContentHeight)
  90. {
  91. if(mVertScroll == nullptr)
  92. {
  93. mVertScroll = GUIScrollBarVert::create(_getParentWidget());
  94. mVertScroll->onScrollPositionChanged.connect(boost::bind(&GUIScrollArea::vertScrollUpdate, this, _1));
  95. mVertScroll->_setAcceptsKeyboardFocus(false);
  96. }
  97. Int2 offset(x + mClippedContentWidth, y);
  98. mVertScroll->_setOffset(offset);
  99. mVertScroll->_setWidth(ScrollBarWidth);
  100. mVertScroll->_setHeight(mClippedContentHeight);
  101. mVertScroll->_setAreaDepth(areaDepth);
  102. mVertScroll->_setWidgetDepth(widgetDepth);
  103. UINT32 clippedScrollbarWidth = std::min(width, ScrollBarWidth);
  104. Rect elemClipRect(0, 0, clippedScrollbarWidth, clipRect.height);
  105. mVertScroll->_setClipRect(elemClipRect);
  106. // This element is not a child of any layout so we treat it as a root element
  107. Rect scrollBarLayoutClipRect(clipRect.x + mClippedContentWidth, clipRect.y, clippedScrollbarWidth, clipRect.height);
  108. mVertScroll->_updateLayout(offset.x, offset.y, ScrollBarWidth, mClippedContentHeight, scrollBarLayoutClipRect, widgetDepth, areaDepth);
  109. // Set new handle size and update position to match the new size
  110. UINT32 newHandleSize = (UINT32)Math::FloorToInt(mVertScroll->getMaxHandleSize() * (mClippedContentHeight / (float)contentHeight));
  111. newHandleSize = std::max(newHandleSize, MinHandleSize);
  112. UINT32 scrollableHeight = (UINT32)std::max(0, INT32(contentHeight) - INT32(mClippedContentHeight));
  113. float newScrollPct = mVertOffset / scrollableHeight;
  114. mVertScroll->setHandleSize(newHandleSize);
  115. mVertScroll->setScrollPos(newScrollPct);
  116. hasScrollbars = true;
  117. }
  118. else
  119. {
  120. if(mVertScroll != nullptr)
  121. {
  122. GUIElement::destroy(mVertScroll);
  123. mVertScroll = nullptr;
  124. }
  125. mVertOffset = 0.0f;
  126. }
  127. // Add/remove/update horizontal scrollbar as needed
  128. if(contentWidth > mClippedContentWidth)
  129. {
  130. if(mHorzScroll == nullptr)
  131. {
  132. mHorzScroll = GUIScrollBarHorz::create(_getParentWidget());
  133. mHorzScroll->onScrollPositionChanged.connect(boost::bind(&GUIScrollArea::horzScrollUpdate, this, _1));
  134. mHorzScroll->_setAcceptsKeyboardFocus(false);
  135. }
  136. Int2 offset(x, y + mClippedContentHeight);
  137. mHorzScroll->_setOffset(offset);
  138. mHorzScroll->_setWidth(mClippedContentWidth);
  139. mHorzScroll->_setHeight(ScrollBarWidth);
  140. mHorzScroll->_setAreaDepth(areaDepth);
  141. mHorzScroll->_setWidgetDepth(widgetDepth);
  142. UINT32 clippedScrollbarHeight = std::min(height, ScrollBarWidth);
  143. Rect elemClipRect(0, 0, clipRect.width, clippedScrollbarHeight);
  144. mHorzScroll->_setClipRect(elemClipRect);
  145. // This element is not a child of any layout so we treat it as a root element
  146. Rect scrollBarLayoutClipRect(clipRect.x, clipRect.y + mClippedContentHeight, clipRect.width, clippedScrollbarHeight);
  147. mHorzScroll->_updateLayout(offset.x, offset.y, mClippedContentWidth, ScrollBarWidth, scrollBarLayoutClipRect, widgetDepth, areaDepth);
  148. // Set new handle size and update position to match the new size
  149. UINT32 newHandleSize = (UINT32)Math::FloorToInt(mHorzScroll->getMaxHandleSize() * (mClippedContentWidth / (float)contentWidth));
  150. newHandleSize = std::max(newHandleSize, MinHandleSize);
  151. UINT32 scrollableWidth = (UINT32)std::max(0, INT32(contentWidth) - INT32(mClippedContentWidth));
  152. float newScrollPct = mHorzOffset / scrollableWidth;
  153. mHorzScroll->setHandleSize(newHandleSize);
  154. mHorzScroll->setScrollPos(newScrollPct);
  155. hasScrollbars = true;
  156. }
  157. else
  158. {
  159. if(mHorzScroll != nullptr)
  160. {
  161. GUIElement::destroy(mHorzScroll);
  162. mHorzScroll = nullptr;
  163. }
  164. mHorzOffset = 0.0f;
  165. }
  166. if(hasScrollbars)
  167. {
  168. mContentLayout->_updateLayoutInternal(x - Math::FloorToInt(mHorzOffset), y - Math::FloorToInt(mVertOffset),
  169. mClippedContentWidth + Math::FloorToInt(mHorzOffset), mClippedContentHeight + Math::FloorToInt(mVertOffset),
  170. layoutClipRect, widgetDepth, areaDepth);
  171. }
  172. mContentWidth = contentWidth;
  173. mContentHeight = contentHeight;
  174. }
  175. void GUIScrollArea::vertScrollUpdate(float scrollPos)
  176. {
  177. UINT32 scrollableHeight = (UINT32)std::max(0, INT32(mContentHeight) - INT32(mClippedContentHeight));
  178. mVertOffset = scrollableHeight * scrollPos;
  179. markContentAsDirty();
  180. }
  181. void GUIScrollArea::horzScrollUpdate(float scrollPos)
  182. {
  183. UINT32 scrollableWidth = (UINT32)std::max(0, INT32(mContentWidth) - INT32(mClippedContentWidth));
  184. mHorzOffset = scrollableWidth * scrollPos;
  185. markContentAsDirty();
  186. }
  187. bool GUIScrollArea::mouseEvent(const GUIMouseEvent& ev)
  188. {
  189. if(ev.getType() == GUIMouseEventType::MouseWheelScroll)
  190. {
  191. // Mouse wheel only scrolls on the Y axis
  192. if(mVertScroll != nullptr)
  193. {
  194. UINT32 scrollableHeight = (UINT32)std::max(0, INT32(mContentWidth) - INT32(mClippedContentHeight));
  195. float additionalScroll = (float)WheelScrollAmount / scrollableHeight;
  196. mVertScroll->scroll(additionalScroll * ev.getWheelScrollAmount());
  197. return true;
  198. }
  199. }
  200. return false;
  201. }
  202. void GUIScrollArea::_changeParentWidget(GUIWidget* widget)
  203. {
  204. GUIElement::_changeParentWidget(widget);
  205. // These two elements are not part of a layout so I need to make sure to
  206. // update them manually
  207. if(mVertScroll != nullptr)
  208. mVertScroll->_changeParentWidget(widget);
  209. if(mHorzScroll != nullptr)
  210. mHorzScroll->_changeParentWidget(widget);
  211. }
  212. GUIScrollArea* GUIScrollArea::create(GUIWidget& parent, const GUIElementStyle* style)
  213. {
  214. if(style == nullptr)
  215. {
  216. const GUISkin* skin = parent.getSkin();
  217. style = skin->getStyle(getGUITypeName());
  218. }
  219. return new (cm_alloc<GUIScrollArea, PoolAlloc>()) GUIScrollArea(parent, style, getDefaultLayoutOptions(style));
  220. }
  221. GUIScrollArea* GUIScrollArea::create(GUIWidget& parent, const GUILayoutOptions& layoutOptions, const GUIElementStyle* style)
  222. {
  223. if(style == nullptr)
  224. {
  225. const GUISkin* skin = parent.getSkin();
  226. style = skin->getStyle(getGUITypeName());
  227. }
  228. return new (cm_alloc<GUIScrollArea, PoolAlloc>()) GUIScrollArea(parent, style, layoutOptions);
  229. }
  230. const String& GUIScrollArea::getGUITypeName()
  231. {
  232. static String typeName = "ScrollArea";
  233. return typeName;
  234. }
  235. }