BsGUIScrollArea.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  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 "BsGUILayoutUtility.h"
  12. #include "BsException.h"
  13. using namespace std::placeholders;
  14. namespace BansheeEngine
  15. {
  16. const UINT32 GUIScrollArea::ScrollBarWidth = 8;
  17. const UINT32 GUIScrollArea::MinHandleSize = 4;
  18. const UINT32 GUIScrollArea::WheelScrollAmount = 50;
  19. GUIScrollArea::GUIScrollArea(ScrollBarType vertBarType, ScrollBarType horzBarType,
  20. const String& scrollBarStyle, const String& scrollAreaStyle, const GUILayoutOptions& layoutOptions)
  21. :GUIElementContainer(layoutOptions), mVertScroll(nullptr), mHorzScroll(nullptr), mVertOffset(0), mHorzOffset(0),
  22. mVertBarType(vertBarType), mHorzBarType(horzBarType), mScrollBarStyle(scrollBarStyle)
  23. {
  24. mContentLayout = &addLayoutYInternal(this);
  25. mHorzScroll = GUIScrollBarHorz::create(mScrollBarStyle);
  26. mVertScroll = GUIScrollBarVert::create(mScrollBarStyle);
  27. _registerChildElement(mHorzScroll);
  28. _registerChildElement(mVertScroll);
  29. mHorzScroll->onScrollPositionChanged.connect(std::bind(&GUIScrollArea::horzScrollUpdate, this, _1));
  30. mVertScroll->onScrollPositionChanged.connect(std::bind(&GUIScrollArea::vertScrollUpdate, this, _1));
  31. }
  32. GUIScrollArea::~GUIScrollArea()
  33. {
  34. }
  35. void GUIScrollArea::updateClippedBounds()
  36. {
  37. Rect2I bounds(0, 0, mWidth, mHeight);
  38. bounds.clip(mClipRect);
  39. bounds.x += mOffset.x;
  40. bounds.y += mOffset.y;
  41. mClippedBounds = bounds;
  42. }
  43. void GUIScrollArea::_getElementAreas(INT32 x, INT32 y, UINT32 width, UINT32 height, Rect2I* elementAreas, UINT32 numElements, const Vector<LayoutSizeRange>& sizeRanges) const
  44. {
  45. Vector2I visibleSize, contentSize;
  46. _getElementAreas(x, y, width, height, elementAreas, numElements, sizeRanges, visibleSize, contentSize);
  47. }
  48. void GUIScrollArea::_getElementAreas(INT32 x, INT32 y, UINT32 width, UINT32 height, Rect2I* elementAreas, UINT32 numElements,
  49. const Vector<LayoutSizeRange>& sizeRanges, Vector2I& visibleSize, Vector2I& contentSize) const
  50. {
  51. assert(mChildren.size() == numElements && numElements == 3);
  52. UINT32 layoutIdx = 0;
  53. UINT32 horzScrollIdx = 0;
  54. UINT32 vertScrollIdx = 0;
  55. UINT32 idx = 0;
  56. for (auto& child : mChildren)
  57. {
  58. if (child == mContentLayout)
  59. layoutIdx = idx;
  60. if (child == mHorzScroll)
  61. horzScrollIdx = idx;
  62. if (child == mVertScroll)
  63. vertScrollIdx = idx;
  64. idx++;
  65. }
  66. // Calculate content layout bounds
  67. //// We want elements to use their optimal height, since scroll area
  68. //// technically provides "infinite" space
  69. UINT32 optimalContentWidth = width;
  70. if (mHorzBarType != ScrollBarType::NeverShow)
  71. optimalContentWidth = sizeRanges[layoutIdx].optimal.x;
  72. UINT32 optimalContentHeight = height;
  73. if (mVertBarType != ScrollBarType::NeverShow)
  74. optimalContentHeight = sizeRanges[layoutIdx].optimal.y;
  75. UINT32 layoutWidth = std::max(optimalContentWidth, width);
  76. UINT32 layoutHeight = std::max(optimalContentHeight, height);
  77. contentSize = GUILayoutUtility::calcActualSize(layoutWidth, layoutHeight, mContentLayout);
  78. visibleSize = Vector2I(width, height);
  79. bool addHorzScrollbar = (mHorzBarType == ScrollBarType::ShowIfDoesntFit && contentSize.x > visibleSize.x) ||
  80. mHorzBarType == ScrollBarType::AlwaysShow && mHorzBarType != ScrollBarType::NeverShow;
  81. bool hasHorzScrollbar = false;
  82. bool hasVertScrollbar = false;
  83. if (addHorzScrollbar)
  84. {
  85. // Make room for scrollbar
  86. visibleSize.y = (UINT32)std::max(0, (INT32)height - (INT32)ScrollBarWidth);
  87. if (mVertBarType == ScrollBarType::NeverShow)
  88. layoutHeight = (UINT32)visibleSize.y;
  89. else
  90. layoutHeight = std::max(optimalContentHeight, (UINT32)visibleSize.y); // Never go below optimal size
  91. contentSize = GUILayoutUtility::calcActualSize(layoutWidth, layoutHeight, mContentLayout);
  92. hasHorzScrollbar = true;
  93. }
  94. bool addVertScrollbar = (mVertBarType == ScrollBarType::ShowIfDoesntFit && contentSize.y > visibleSize.y) ||
  95. mVertBarType == ScrollBarType::AlwaysShow && mVertBarType != ScrollBarType::NeverShow;
  96. if (addVertScrollbar)
  97. {
  98. // Make room for scrollbar
  99. visibleSize.x = (UINT32)std::max(0, (INT32)width - (INT32)ScrollBarWidth);
  100. if (mHorzBarType == ScrollBarType::NeverShow)
  101. layoutWidth = (UINT32)visibleSize.x;
  102. else
  103. layoutWidth = std::max(optimalContentWidth, (UINT32)visibleSize.x); // Never go below optimal size
  104. contentSize = GUILayoutUtility::calcActualSize(layoutWidth, layoutHeight, mContentLayout);
  105. hasVertScrollbar = true;
  106. if (!hasHorzScrollbar) // Since width has been reduced, we need to check if we require the horizontal scrollbar
  107. {
  108. addHorzScrollbar = (mHorzBarType == ScrollBarType::ShowIfDoesntFit && contentSize.x > visibleSize.x) && mHorzBarType != ScrollBarType::NeverShow;
  109. if (addHorzScrollbar)
  110. {
  111. // Make room for scrollbar
  112. visibleSize.y = (UINT32)std::max(0, (INT32)height - (INT32)ScrollBarWidth);
  113. if (mVertBarType == ScrollBarType::NeverShow)
  114. layoutHeight = (UINT32)visibleSize.y;
  115. else
  116. layoutHeight = std::max(optimalContentHeight, (UINT32)visibleSize.y); // Never go below optimal size
  117. contentSize = GUILayoutUtility::calcActualSize(layoutWidth, layoutHeight, mContentLayout);
  118. hasHorzScrollbar = true;
  119. }
  120. }
  121. }
  122. elementAreas[layoutIdx] = Rect2I(x - Math::floorToInt(mHorzOffset), y - Math::floorToInt(mVertOffset), layoutWidth, layoutHeight);
  123. // Calculate vertical scrollbar bounds
  124. if (hasVertScrollbar)
  125. {
  126. INT32 scrollBarOffset = (UINT32)std::max(0, (INT32)width - (INT32)ScrollBarWidth);
  127. UINT32 scrollBarHeight = height;
  128. if (hasHorzScrollbar)
  129. scrollBarHeight = (UINT32)std::max(0, (INT32)scrollBarHeight - (INT32)ScrollBarWidth);
  130. elementAreas[vertScrollIdx] = Rect2I(x + scrollBarOffset, y, ScrollBarWidth, scrollBarHeight);
  131. }
  132. else
  133. {
  134. elementAreas[vertScrollIdx] = Rect2I(x + layoutWidth, y, 0, 0);
  135. }
  136. // Calculate horizontal scrollbar bounds
  137. if (hasHorzScrollbar)
  138. {
  139. INT32 scrollBarOffset = (UINT32)std::max(0, (INT32)height - (INT32)ScrollBarWidth);
  140. UINT32 scrollBarWidth = width;
  141. if (hasVertScrollbar)
  142. scrollBarWidth = (UINT32)std::max(0, (INT32)scrollBarWidth - (INT32)ScrollBarWidth);
  143. elementAreas[horzScrollIdx] = Rect2I(x, y + scrollBarOffset, scrollBarWidth, ScrollBarWidth);
  144. }
  145. else
  146. {
  147. elementAreas[horzScrollIdx] = Rect2I(x, y + layoutHeight, 0, 0);
  148. }
  149. }
  150. void GUIScrollArea::_updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height,
  151. Rect2I clipRect, UINT8 widgetDepth, UINT16 areaDepth)
  152. {
  153. UINT32 numElements = (UINT32)mChildren.size();
  154. Rect2I* elementAreas = nullptr;
  155. if (numElements > 0)
  156. elementAreas = stackConstructN<Rect2I>(numElements);
  157. Vector<LayoutSizeRange> sizeRanges;
  158. UINT32 layoutIdx = 0;
  159. UINT32 horzScrollIdx = 0;
  160. UINT32 vertScrollIdx = 0;
  161. for (UINT32 i = 0; i < numElements; i++)
  162. {
  163. GUIElementBase* child = _getChild(i);
  164. sizeRanges.push_back(child->_calculateLayoutSizeRange());
  165. if (child == mContentLayout)
  166. layoutIdx = i;
  167. if (child == mHorzScroll)
  168. horzScrollIdx = i;
  169. if (child == mVertScroll)
  170. vertScrollIdx = i;
  171. }
  172. _getElementAreas(x, y, width, height, elementAreas, numElements, sizeRanges, mVisibleSize, mContentSize);
  173. Rect2I& layoutBounds = elementAreas[layoutIdx];
  174. Rect2I& horzScrollBounds = elementAreas[horzScrollIdx];
  175. Rect2I& vertScrollBounds = elementAreas[vertScrollIdx];
  176. // Layout
  177. Rect2I layoutClipRect = clipRect;
  178. layoutClipRect.width = (UINT32)mVisibleSize.x;
  179. layoutClipRect.height = (UINT32)mVisibleSize.y;
  180. mContentLayout->_updateLayoutInternal(layoutBounds.x, layoutBounds.y,
  181. layoutBounds.width, layoutBounds.height, layoutClipRect, widgetDepth, areaDepth);
  182. // Vertical scrollbar
  183. {
  184. mVertScroll->setOffset(Vector2I(vertScrollBounds.x, vertScrollBounds.y));
  185. mVertScroll->setWidth(vertScrollBounds.width);
  186. mVertScroll->setHeight(vertScrollBounds.height);
  187. mVertScroll->_setAreaDepth(areaDepth);
  188. mVertScroll->_setWidgetDepth(widgetDepth);
  189. UINT32 clippedScrollbarWidth = std::min(width, ScrollBarWidth);
  190. Rect2I elemClipRect(0, 0, clippedScrollbarWidth, clipRect.height);
  191. mVertScroll->_setClipRect(elemClipRect);
  192. // This element is not a child of any layout so we treat it as a root element
  193. Rect2I scrollBarLayoutClipRect(clipRect.x + (vertScrollBounds.x - x), clipRect.y + (vertScrollBounds.y - y), clippedScrollbarWidth, clipRect.height);
  194. mVertScroll->_updateLayout(vertScrollBounds.x, vertScrollBounds.y, vertScrollBounds.width, vertScrollBounds.height, scrollBarLayoutClipRect, widgetDepth, areaDepth);
  195. // Set new handle size and update position to match the new size
  196. UINT32 newHandleSize = (UINT32)Math::floorToInt(mVertScroll->getMaxHandleSize() * (vertScrollBounds.height / (float)mContentSize.y));
  197. newHandleSize = std::max(newHandleSize, MinHandleSize);
  198. UINT32 scrollableHeight = (UINT32)std::max(0, INT32(mContentSize.y) - INT32(vertScrollBounds.height));
  199. float newScrollPct = 0.0f;
  200. if (scrollableHeight > 0)
  201. newScrollPct = mVertOffset / scrollableHeight;
  202. mVertScroll->setHandleSize(newHandleSize);
  203. mVertScroll->setScrollPos(newScrollPct);
  204. }
  205. // Horizontal scrollbar
  206. {
  207. mHorzScroll->setOffset(Vector2I(horzScrollBounds.x, horzScrollBounds.y));
  208. mHorzScroll->setWidth(horzScrollBounds.width);
  209. mHorzScroll->setHeight(horzScrollBounds.height);
  210. mHorzScroll->_setAreaDepth(areaDepth);
  211. mHorzScroll->_setWidgetDepth(widgetDepth);
  212. UINT32 clippedScrollbarHeight = std::min(height, ScrollBarWidth);
  213. Rect2I elemClipRect(0, 0, clipRect.width, clippedScrollbarHeight);
  214. mHorzScroll->_setClipRect(elemClipRect);
  215. // This element is not a child of any layout so we treat it as a root element
  216. Rect2I scrollBarLayoutClipRect(clipRect.x + (horzScrollBounds.x - x), clipRect.y + (horzScrollBounds.y - y), clipRect.width, clippedScrollbarHeight);
  217. mHorzScroll->_updateLayout(horzScrollBounds.x, horzScrollBounds.y, horzScrollBounds.width, horzScrollBounds.height, scrollBarLayoutClipRect, widgetDepth, areaDepth);
  218. // Set new handle size and update position to match the new size
  219. UINT32 newHandleSize = (UINT32)Math::floorToInt(mHorzScroll->getMaxHandleSize() * (horzScrollBounds.width / (float)mContentSize.x));
  220. newHandleSize = std::max(newHandleSize, MinHandleSize);
  221. UINT32 scrollableWidth = (UINT32)std::max(0, INT32(mContentSize.x) - INT32(horzScrollBounds.width));
  222. float newScrollPct = 0.0f;
  223. if (scrollableWidth > 0)
  224. newScrollPct = mHorzOffset / scrollableWidth;
  225. mHorzScroll->setHandleSize(newHandleSize);
  226. mHorzScroll->setScrollPos(newScrollPct);
  227. }
  228. if (elementAreas != nullptr)
  229. stackDeallocLast(elementAreas);
  230. }
  231. void GUIScrollArea::vertScrollUpdate(float scrollPos)
  232. {
  233. scrollToVertical(scrollPos);
  234. }
  235. void GUIScrollArea::horzScrollUpdate(float scrollPos)
  236. {
  237. scrollToHorizontal(scrollPos);
  238. }
  239. void GUIScrollArea::scrollToVertical(float pct)
  240. {
  241. UINT32 scrollableHeight = (UINT32)std::max(0, INT32(mContentSize.y) - INT32(mVisibleSize.y));
  242. mVertOffset = scrollableHeight * Math::clamp01(pct);
  243. markContentAsDirty();
  244. }
  245. void GUIScrollArea::scrollToHorizontal(float pct)
  246. {
  247. UINT32 scrollableWidth = (UINT32)std::max(0, INT32(mContentSize.x) - INT32(mVisibleSize.x));
  248. mHorzOffset = scrollableWidth * Math::clamp01(pct);
  249. markContentAsDirty();
  250. }
  251. void GUIScrollArea::scrollUpPx(UINT32 pixels)
  252. {
  253. if(mVertScroll != nullptr)
  254. {
  255. UINT32 scrollableSize = (UINT32)std::max(0, INT32(mContentSize.y) - INT32(mVisibleSize.y));
  256. float offset = 0.0f;
  257. if(scrollableSize > 0)
  258. offset = pixels / (float)scrollableSize;
  259. mVertScroll->scroll(offset);
  260. }
  261. }
  262. void GUIScrollArea::scrollDownPx(UINT32 pixels)
  263. {
  264. if(mVertScroll != nullptr)
  265. {
  266. UINT32 scrollableSize = (UINT32)std::max(0, INT32(mContentSize.y) - INT32(mVisibleSize.y));
  267. float offset = 0.0f;
  268. if(scrollableSize > 0)
  269. offset = pixels / (float)scrollableSize;
  270. mVertScroll->scroll(-offset);
  271. }
  272. }
  273. void GUIScrollArea::scrollLeftPx(UINT32 pixels)
  274. {
  275. if(mHorzScroll != nullptr)
  276. {
  277. UINT32 scrollableSize = (UINT32)std::max(0, INT32(mContentSize.x) - INT32(mVisibleSize.x));
  278. float offset = 0.0f;
  279. if(scrollableSize > 0)
  280. offset = pixels / (float)scrollableSize;
  281. mHorzScroll->scroll(offset);
  282. }
  283. }
  284. void GUIScrollArea::scrollRightPx(UINT32 pixels)
  285. {
  286. if(mHorzScroll != nullptr)
  287. {
  288. UINT32 scrollableSize = (UINT32)std::max(0, INT32(mContentSize.x) - INT32(mVisibleSize.x));
  289. float offset = 0.0f;
  290. if(scrollableSize > 0)
  291. offset = pixels / (float)scrollableSize;
  292. mHorzScroll->scroll(-offset);
  293. }
  294. }
  295. void GUIScrollArea::scrollUpPct(float percent)
  296. {
  297. if(mVertScroll != nullptr)
  298. mVertScroll->scroll(percent);
  299. }
  300. void GUIScrollArea::scrollDownPct(float percent)
  301. {
  302. if(mVertScroll != nullptr)
  303. mVertScroll->scroll(-percent);
  304. }
  305. void GUIScrollArea::scrollLeftPct(float percent)
  306. {
  307. if(mHorzScroll != nullptr)
  308. mHorzScroll->scroll(percent);
  309. }
  310. void GUIScrollArea::scrollRightPct(float percent)
  311. {
  312. if(mHorzScroll != nullptr)
  313. mHorzScroll->scroll(-percent);
  314. }
  315. bool GUIScrollArea::_mouseEvent(const GUIMouseEvent& ev)
  316. {
  317. if(ev.getType() == GUIMouseEventType::MouseWheelScroll)
  318. {
  319. // Mouse wheel only scrolls on the Y axis
  320. if(mVertScroll != nullptr)
  321. {
  322. UINT32 scrollableHeight = (UINT32)std::max(0, INT32(mContentSize.y) - INT32(mVisibleSize.y));
  323. float additionalScroll = (float)WheelScrollAmount / scrollableHeight;
  324. mVertScroll->scroll(additionalScroll * ev.getWheelScrollAmount());
  325. return true;
  326. }
  327. }
  328. return false;
  329. }
  330. GUIScrollArea* GUIScrollArea::create(ScrollBarType vertBarType, ScrollBarType horzBarType,
  331. const String& scrollBarStyle, const String& scrollAreaStyle)
  332. {
  333. return new (bs_alloc<GUIScrollArea, PoolAlloc>()) GUIScrollArea(vertBarType, horzBarType, scrollBarStyle,
  334. getStyleName<GUIScrollArea>(scrollAreaStyle), GUILayoutOptions::create());
  335. }
  336. GUIScrollArea* GUIScrollArea::create(const GUIOptions& layoutOptions, const String& scrollBarStyle,
  337. const String& scrollAreaStyle)
  338. {
  339. return new (bs_alloc<GUIScrollArea, PoolAlloc>()) GUIScrollArea(ScrollBarType::ShowIfDoesntFit,
  340. ScrollBarType::ShowIfDoesntFit, scrollBarStyle, getStyleName<GUIScrollArea>(scrollAreaStyle), GUILayoutOptions::create(layoutOptions));
  341. }
  342. GUIScrollArea* GUIScrollArea::create(const String& scrollBarStyle, const String& scrollAreaStyle)
  343. {
  344. return new (bs_alloc<GUIScrollArea, PoolAlloc>()) GUIScrollArea(ScrollBarType::ShowIfDoesntFit, ScrollBarType::ShowIfDoesntFit, scrollBarStyle,
  345. getStyleName<GUIScrollArea>(scrollAreaStyle), GUILayoutOptions::create());
  346. }
  347. GUIScrollArea* GUIScrollArea::create(ScrollBarType vertBarType,
  348. ScrollBarType horzBarType, const GUIOptions& layoutOptions, const String& scrollBarStyle,
  349. const String& scrollAreaStyle)
  350. {
  351. return new (bs_alloc<GUIScrollArea, PoolAlloc>()) GUIScrollArea(vertBarType, horzBarType, scrollBarStyle,
  352. getStyleName<GUIScrollArea>(scrollAreaStyle), GUILayoutOptions::create(layoutOptions));
  353. }
  354. const String& GUIScrollArea::getGUITypeName()
  355. {
  356. static String typeName = "ScrollArea";
  357. return typeName;
  358. }
  359. }