BsGUIScrollArea.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  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, ScrollBarType vertBarType, ScrollBarType horzBarType,
  19. const GUIElementStyle* scrollBarStyle, const GUIElementStyle* scrollAreaStyle, const GUILayoutOptions& layoutOptions)
  20. :GUIElement(parent, scrollAreaStyle, layoutOptions, false), mVertScroll(nullptr), mHorzScroll(nullptr), mVertOffset(0), mHorzOffset(0),
  21. mContentWidth(0), mContentHeight(0), mClippedContentWidth(0), mClippedContentHeight(0), mVertBarType(vertBarType), mHorzBarType(horzBarType),
  22. mScrollBarStyle(scrollBarStyle)
  23. {
  24. mContentLayout = &addLayoutYInternal(this);
  25. }
  26. GUIScrollArea::~GUIScrollArea()
  27. {
  28. }
  29. UINT32 GUIScrollArea::getNumRenderElements() const
  30. {
  31. return 0;
  32. }
  33. const GUIMaterialInfo& GUIScrollArea::getMaterial(UINT32 renderElementIdx) const
  34. {
  35. CM_EXCEPT(InternalErrorException, "Trying to retrieve a material from an element with no render elements.");
  36. }
  37. UINT32 GUIScrollArea::getNumQuads(UINT32 renderElementIdx) const
  38. {
  39. return 0;
  40. }
  41. void GUIScrollArea::updateRenderElementsInternal()
  42. {
  43. GUIElement::updateRenderElementsInternal();
  44. }
  45. void GUIScrollArea::updateClippedBounds()
  46. {
  47. mClippedBounds = RectI(0, 0, 0, 0); // We don't want any mouse input for this element. This is just a container.
  48. }
  49. Vector2I GUIScrollArea::_getOptimalSize() const
  50. {
  51. return mContentLayout->_getOptimalSize();
  52. }
  53. void GUIScrollArea::fillBuffer(UINT8* vertices, UINT8* uv, UINT32* indices, UINT32 startingQuad, UINT32 maxNumQuads,
  54. UINT32 vertexStride, UINT32 indexStride, UINT32 renderElementIdx) const
  55. { }
  56. void GUIScrollArea::_updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height,
  57. RectI clipRect, UINT8 widgetDepth, UINT16 areaDepth)
  58. {
  59. mContentLayout->_updateLayoutInternal(x, y, width, height, clipRect, widgetDepth, areaDepth);
  60. UINT32 contentWidth = mContentLayout->_getActualWidth();
  61. UINT32 contentHeight = mContentLayout->_getActualHeight();
  62. mClippedContentWidth = width;
  63. mClippedContentHeight = height;
  64. RectI layoutClipRect = clipRect;
  65. bool addHorzScrollbar = (mHorzBarType == ScrollBarType::ShowIfDoesntFit && contentWidth > mWidth) ||
  66. mHorzBarType == ScrollBarType::AlwaysShow && mHorzBarType != ScrollBarType::NeverShow;
  67. bool hasHorzScrollbar = false;
  68. bool hasVertScrollbar = false;
  69. if(addHorzScrollbar)
  70. {
  71. // Make room for scrollbar
  72. mClippedContentHeight = (UINT32)std::max(0, (INT32)height - (INT32)ScrollBarWidth);
  73. layoutClipRect.height = mClippedContentHeight;
  74. hasHorzScrollbar = true;
  75. mContentLayout->_updateLayoutInternal(x - Math::floorToInt(mHorzOffset), y,
  76. mClippedContentWidth + Math::floorToInt(mHorzOffset), mClippedContentHeight, layoutClipRect, widgetDepth, areaDepth);
  77. contentWidth = mContentLayout->_getActualWidth();
  78. contentHeight = mContentLayout->_getActualHeight();
  79. }
  80. bool addVertScrollbar = (mVertBarType == ScrollBarType::ShowIfDoesntFit && contentHeight > mClippedContentHeight) ||
  81. mVertBarType == ScrollBarType::AlwaysShow && mVertBarType != ScrollBarType::NeverShow;
  82. if(addVertScrollbar)
  83. {
  84. // Make room for scrollbar
  85. mClippedContentWidth = (UINT32)std::max(0, (INT32)width - (INT32)ScrollBarWidth);
  86. layoutClipRect.width = mClippedContentWidth;
  87. hasVertScrollbar = true;
  88. if(hasHorzScrollbar)
  89. {
  90. mContentLayout->_updateLayoutInternal(x - Math::floorToInt(mHorzOffset), y - Math::floorToInt(mVertOffset),
  91. mClippedContentWidth + Math::floorToInt(mHorzOffset), mClippedContentHeight + Math::floorToInt(mVertOffset),
  92. layoutClipRect, widgetDepth, areaDepth);
  93. }
  94. else
  95. {
  96. mContentLayout->_updateLayoutInternal(x, y - Math::floorToInt(mVertOffset),
  97. mClippedContentWidth, mClippedContentHeight + Math::floorToInt(mVertOffset),
  98. layoutClipRect, widgetDepth, areaDepth);
  99. }
  100. contentWidth = mContentLayout->_getActualWidth();
  101. contentHeight = mContentLayout->_getActualHeight();
  102. if(!hasHorzScrollbar) // Since width has been reduced, we need to check if we require the horizontal scrollbar
  103. {
  104. addHorzScrollbar = (mHorzBarType == ScrollBarType::ShowIfDoesntFit && contentWidth > mClippedContentWidth) && mHorzBarType != ScrollBarType::NeverShow;
  105. if(addHorzScrollbar)
  106. {
  107. // Make room for scrollbar
  108. mClippedContentHeight = (UINT32)std::max(0, (INT32)height - (INT32)ScrollBarWidth);
  109. layoutClipRect.height = mClippedContentHeight;
  110. mContentLayout->_updateLayoutInternal(x - Math::floorToInt(mHorzOffset), y - Math::floorToInt(mVertOffset),
  111. mClippedContentWidth + Math::floorToInt(mHorzOffset), mClippedContentHeight + Math::floorToInt(mVertOffset),
  112. layoutClipRect, widgetDepth, areaDepth);
  113. }
  114. }
  115. }
  116. // Add/remove/update vertical scrollbar as needed
  117. if((mVertBarType == ScrollBarType::ShowIfDoesntFit && contentHeight > mClippedContentHeight) || mVertBarType == ScrollBarType::AlwaysShow &&
  118. mVertBarType != ScrollBarType::NeverShow)
  119. {
  120. if(mVertScroll == nullptr)
  121. {
  122. if(mScrollBarStyle != nullptr)
  123. mVertScroll = GUIScrollBarVert::create(_getParentWidget(), mScrollBarStyle);
  124. else
  125. mVertScroll = GUIScrollBarVert::create(_getParentWidget());
  126. mVertScroll->onScrollPositionChanged.connect(boost::bind(&GUIScrollArea::vertScrollUpdate, this, _1));
  127. mVertScroll->_setAcceptsKeyboardFocus(false);
  128. }
  129. INT32 scrollBarOffset = (UINT32)std::max(0, (INT32)width - (INT32)ScrollBarWidth);
  130. UINT32 scrollBarHeight = height;
  131. if(hasHorzScrollbar)
  132. scrollBarHeight = (UINT32)std::max(0, (INT32)scrollBarHeight - (INT32)ScrollBarWidth);
  133. Vector2I offset(x + scrollBarOffset, y);
  134. mVertScroll->_setOffset(offset);
  135. mVertScroll->_setWidth(ScrollBarWidth);
  136. mVertScroll->_setHeight(scrollBarHeight);
  137. mVertScroll->_setAreaDepth(areaDepth);
  138. mVertScroll->_setWidgetDepth(widgetDepth);
  139. UINT32 clippedScrollbarWidth = std::min(width, ScrollBarWidth);
  140. RectI elemClipRect(0, 0, clippedScrollbarWidth, clipRect.height);
  141. mVertScroll->_setClipRect(elemClipRect);
  142. // This element is not a child of any layout so we treat it as a root element
  143. RectI scrollBarLayoutClipRect(clipRect.x + scrollBarOffset, clipRect.y, clippedScrollbarWidth, clipRect.height);
  144. mVertScroll->_updateLayout(offset.x, offset.y, ScrollBarWidth, scrollBarHeight, scrollBarLayoutClipRect, widgetDepth, areaDepth);
  145. // Set new handle size and update position to match the new size
  146. UINT32 newHandleSize = (UINT32)Math::floorToInt(mVertScroll->getMaxHandleSize() * (scrollBarHeight / (float)contentHeight));
  147. newHandleSize = std::max(newHandleSize, MinHandleSize);
  148. UINT32 scrollableHeight = (UINT32)std::max(0, INT32(contentHeight) - INT32(scrollBarHeight));
  149. float newScrollPct = 0.0f;
  150. if(scrollableHeight > 0)
  151. newScrollPct = mVertOffset / scrollableHeight;
  152. mVertScroll->setHandleSize(newHandleSize);
  153. mVertScroll->setScrollPos(newScrollPct);
  154. }
  155. else
  156. {
  157. if(mVertScroll != nullptr)
  158. {
  159. GUIElement::destroy(mVertScroll);
  160. mVertScroll = nullptr;
  161. }
  162. mVertOffset = 0.0f;
  163. }
  164. // Add/remove/update horizontal scrollbar as needed
  165. if((mHorzBarType == ScrollBarType::ShowIfDoesntFit && contentWidth > mClippedContentWidth) || mHorzBarType == ScrollBarType::AlwaysShow &&
  166. mHorzBarType != ScrollBarType::NeverShow)
  167. {
  168. if(mHorzScroll == nullptr)
  169. {
  170. if(mScrollBarStyle != nullptr)
  171. mHorzScroll = GUIScrollBarHorz::create(_getParentWidget(), mScrollBarStyle);
  172. else
  173. mHorzScroll = GUIScrollBarHorz::create(_getParentWidget());
  174. mHorzScroll->onScrollPositionChanged.connect(boost::bind(&GUIScrollArea::horzScrollUpdate, this, _1));
  175. mHorzScroll->_setAcceptsKeyboardFocus(false);
  176. }
  177. INT32 scrollBarOffset = (UINT32)std::max(0, (INT32)height - (INT32)ScrollBarWidth);
  178. UINT32 scrollBarWidth = width;
  179. if(hasVertScrollbar)
  180. scrollBarWidth = (UINT32)std::max(0, (INT32)scrollBarWidth - (INT32)ScrollBarWidth);
  181. Vector2I offset(x, y + scrollBarOffset);
  182. mHorzScroll->_setOffset(offset);
  183. mHorzScroll->_setWidth(scrollBarWidth);
  184. mHorzScroll->_setHeight(ScrollBarWidth);
  185. mHorzScroll->_setAreaDepth(areaDepth);
  186. mHorzScroll->_setWidgetDepth(widgetDepth);
  187. UINT32 clippedScrollbarHeight = std::min(height, ScrollBarWidth);
  188. RectI elemClipRect(0, 0, clipRect.width, clippedScrollbarHeight);
  189. mHorzScroll->_setClipRect(elemClipRect);
  190. // This element is not a child of any layout so we treat it as a root element
  191. RectI scrollBarLayoutClipRect(clipRect.x, clipRect.y + scrollBarOffset, clipRect.width, clippedScrollbarHeight);
  192. mHorzScroll->_updateLayout(offset.x, offset.y, scrollBarWidth, ScrollBarWidth, scrollBarLayoutClipRect, widgetDepth, areaDepth);
  193. // Set new handle size and update position to match the new size
  194. UINT32 newHandleSize = (UINT32)Math::floorToInt(mHorzScroll->getMaxHandleSize() * (scrollBarWidth / (float)contentWidth));
  195. newHandleSize = std::max(newHandleSize, MinHandleSize);
  196. UINT32 scrollableWidth = (UINT32)std::max(0, INT32(contentWidth) - INT32(scrollBarWidth));
  197. float newScrollPct = 0.0f;
  198. if(scrollableWidth > 0)
  199. newScrollPct = mHorzOffset / scrollableWidth;
  200. mHorzScroll->setHandleSize(newHandleSize);
  201. mHorzScroll->setScrollPos(newScrollPct);
  202. }
  203. else
  204. {
  205. if(mHorzScroll != nullptr)
  206. {
  207. GUIElement::destroy(mHorzScroll);
  208. mHorzScroll = nullptr;
  209. }
  210. mHorzOffset = 0.0f;
  211. }
  212. mContentWidth = contentWidth;
  213. mContentHeight = contentHeight;
  214. }
  215. void GUIScrollArea::vertScrollUpdate(float scrollPos)
  216. {
  217. UINT32 scrollableHeight = (UINT32)std::max(0, INT32(mContentHeight) - INT32(mClippedContentHeight));
  218. mVertOffset = scrollableHeight * scrollPos;
  219. markContentAsDirty();
  220. }
  221. void GUIScrollArea::horzScrollUpdate(float scrollPos)
  222. {
  223. UINT32 scrollableWidth = (UINT32)std::max(0, INT32(mContentWidth) - INT32(mClippedContentWidth));
  224. mHorzOffset = scrollableWidth * scrollPos;
  225. markContentAsDirty();
  226. }
  227. bool GUIScrollArea::mouseEvent(const GUIMouseEvent& ev)
  228. {
  229. if(ev.getType() == GUIMouseEventType::MouseWheelScroll)
  230. {
  231. // Mouse wheel only scrolls on the Y axis
  232. if(mVertScroll != nullptr)
  233. {
  234. UINT32 scrollableHeight = (UINT32)std::max(0, INT32(mContentWidth) - INT32(mClippedContentHeight));
  235. float additionalScroll = (float)WheelScrollAmount / scrollableHeight;
  236. mVertScroll->scroll(additionalScroll * ev.getWheelScrollAmount());
  237. return true;
  238. }
  239. }
  240. return false;
  241. }
  242. void GUIScrollArea::_changeParentWidget(GUIWidget* widget)
  243. {
  244. GUIElement::_changeParentWidget(widget);
  245. // These two elements are not part of a layout so I need to make sure to
  246. // update them manually
  247. if(mVertScroll != nullptr)
  248. mVertScroll->_changeParentWidget(widget);
  249. if(mHorzScroll != nullptr)
  250. mHorzScroll->_changeParentWidget(widget);
  251. }
  252. GUIScrollArea* GUIScrollArea::create(GUIWidget& parent, ScrollBarType vertBarType, ScrollBarType horzBarType,
  253. const GUIElementStyle* scrollBarStyle, const GUIElementStyle* scrollAreaStyle)
  254. {
  255. if(scrollAreaStyle == nullptr)
  256. {
  257. const GUISkin& skin = parent.getSkin();
  258. scrollAreaStyle = skin.getStyle(getGUITypeName());
  259. }
  260. return new (cm_alloc<GUIScrollArea, PoolAlloc>()) GUIScrollArea(parent, vertBarType, horzBarType, scrollBarStyle,
  261. scrollAreaStyle, GUILayoutOptions::create(scrollAreaStyle));
  262. }
  263. GUIScrollArea* GUIScrollArea::create(GUIWidget& parent, const GUIOptions& layoutOptions, const GUIElementStyle* scrollBarStyle,
  264. const GUIElementStyle* scrollAreaStyle)
  265. {
  266. if(scrollAreaStyle == nullptr)
  267. {
  268. const GUISkin& skin = parent.getSkin();
  269. scrollAreaStyle = skin.getStyle(getGUITypeName());
  270. }
  271. return new (cm_alloc<GUIScrollArea, PoolAlloc>()) GUIScrollArea(parent, ScrollBarType::ShowIfDoesntFit,
  272. ScrollBarType::ShowIfDoesntFit, scrollBarStyle, scrollAreaStyle, GUILayoutOptions::create(layoutOptions, scrollAreaStyle));
  273. }
  274. GUIScrollArea* GUIScrollArea::create(GUIWidget& parent, const GUIElementStyle* scrollBarStyle, const GUIElementStyle* scrollAreaStyle)
  275. {
  276. if(scrollAreaStyle == nullptr)
  277. {
  278. const GUISkin& skin = parent.getSkin();
  279. scrollAreaStyle = skin.getStyle(getGUITypeName());
  280. }
  281. return new (cm_alloc<GUIScrollArea, PoolAlloc>()) GUIScrollArea(parent, ScrollBarType::ShowIfDoesntFit, ScrollBarType::ShowIfDoesntFit, scrollBarStyle,
  282. scrollAreaStyle, GUILayoutOptions::create(scrollAreaStyle));
  283. }
  284. GUIScrollArea* GUIScrollArea::create(GUIWidget& parent, ScrollBarType vertBarType,
  285. ScrollBarType horzBarType, const GUIOptions& layoutOptions, const GUIElementStyle* scrollBarStyle,
  286. const GUIElementStyle* scrollAreaStyle)
  287. {
  288. if(scrollAreaStyle == nullptr)
  289. {
  290. const GUISkin& skin = parent.getSkin();
  291. scrollAreaStyle = skin.getStyle(getGUITypeName());
  292. }
  293. return new (cm_alloc<GUIScrollArea, PoolAlloc>()) GUIScrollArea(parent, vertBarType, horzBarType, scrollBarStyle, scrollAreaStyle, GUILayoutOptions::create(layoutOptions, scrollAreaStyle));
  294. }
  295. const String& GUIScrollArea::getGUITypeName()
  296. {
  297. static String typeName = "ScrollArea";
  298. return typeName;
  299. }
  300. }