BsGUIScrollArea.cpp 14 KB

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