BsGUIScrollArea.cpp 15 KB

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