tb_scroll_container.cpp 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. // ================================================================================
  2. // == This file is a part of Turbo Badger. (C) 2011-2014, Emil Segerås ==
  3. // == See tb_core.h for more information. ==
  4. // ================================================================================
  5. #include "tb_scroll_container.h"
  6. #include "tb_system.h"
  7. #include <assert.h>
  8. namespace tb {
  9. // == TBScrollBarVisibility ===================================
  10. TBScrollBarVisibility TBScrollBarVisibility::Solve(SCROLL_MODE mode, int content_w, int content_h,
  11. int available_w, int available_h,
  12. int scrollbar_x_h, int scrollbar_y_w)
  13. {
  14. TBScrollBarVisibility visibility;
  15. visibility.visible_w = available_w;
  16. visibility.visible_h = available_h;
  17. if (mode == SCROLL_MODE_X_Y)
  18. {
  19. visibility.y_on = true;
  20. visibility.x_on = true;
  21. visibility.visible_w -= scrollbar_y_w;
  22. visibility.visible_h -= scrollbar_x_h;
  23. }
  24. else if (mode == SCROLL_MODE_OFF)
  25. {
  26. }
  27. else if (mode == SCROLL_MODE_Y)
  28. {
  29. visibility.y_on = true;
  30. visibility.visible_w -= scrollbar_y_w;
  31. }
  32. else if (mode == SCROLL_MODE_Y_AUTO)
  33. {
  34. if (content_h > available_h)
  35. {
  36. visibility.y_on = true;
  37. visibility.visible_w -= scrollbar_y_w;
  38. }
  39. }
  40. else if (mode == SCROLL_MODE_X_AUTO_Y_AUTO)
  41. {
  42. if (content_w > visibility.visible_w)
  43. {
  44. visibility.x_on = true;
  45. visibility.visible_h = available_h - scrollbar_x_h;
  46. }
  47. if (content_h > visibility.visible_h)
  48. {
  49. visibility.y_on = true;
  50. visibility.visible_w = available_w - scrollbar_y_w;
  51. }
  52. if (content_w > visibility.visible_w)
  53. {
  54. visibility.x_on = true;
  55. visibility.visible_h = available_h - scrollbar_x_h;
  56. }
  57. }
  58. return visibility;
  59. }
  60. // == TBScrollContainerRoot ===================================
  61. void TBScrollContainerRoot::OnPaintChildren(const PaintProps &paint_props)
  62. {
  63. // We only want clipping in one axis (the overflowing one) so we
  64. // don't damage any expanded skins on the other axis. Add some fluff.
  65. const int fluff = 100;
  66. TBScrollContainer *sc = static_cast<TBScrollContainer *>(GetParent());
  67. TBRect clip_rect = GetPaddingRect().Expand(sc->m_scrollbar_x.CanScrollNegative() ? 0 : fluff,
  68. sc->m_scrollbar_y.CanScrollNegative() ? 0 : fluff,
  69. sc->m_scrollbar_x.CanScrollPositive() ? 0 : fluff,
  70. sc->m_scrollbar_y.CanScrollPositive() ? 0 : fluff);
  71. TBRect old_clip_rect = g_renderer->SetClipRect(clip_rect, true);
  72. TB_IF_DEBUG_SETTING(LAYOUT_CLIPPING, g_renderer->DrawRect(clip_rect, TBColor(255, 0, 0, 200)));
  73. TBWidget::OnPaintChildren(paint_props);
  74. g_renderer->SetClipRect(old_clip_rect, false);
  75. }
  76. void TBScrollContainerRoot::GetChildTranslation(int &x, int &y) const
  77. {
  78. TBScrollContainer *sc = static_cast<TBScrollContainer *>(GetParent());
  79. x = (int) -sc->m_scrollbar_x.GetValue();
  80. y = (int) -sc->m_scrollbar_y.GetValue();
  81. }
  82. // == TBScrollContainer =======================================
  83. TBScrollContainer::TBScrollContainer()
  84. : m_adapt_to_content_size(false)
  85. , m_adapt_content_size(false)
  86. , m_layout_is_invalid(false)
  87. , m_mode(SCROLL_MODE_X_Y)
  88. {
  89. AddChild(&m_scrollbar_x);
  90. AddChild(&m_scrollbar_y);
  91. AddChild(&m_root);
  92. m_scrollbar_y.SetAxis(AXIS_Y);
  93. }
  94. TBScrollContainer::~TBScrollContainer()
  95. {
  96. RemoveChild(&m_root);
  97. RemoveChild(&m_scrollbar_y);
  98. RemoveChild(&m_scrollbar_x);
  99. }
  100. void TBScrollContainer::SetAdaptToContentSize(bool adapt)
  101. {
  102. if (m_adapt_to_content_size == adapt)
  103. return;
  104. InvalidateLayout(INVALIDATE_LAYOUT_RECURSIVE);
  105. m_adapt_to_content_size = adapt;
  106. InvalidateLayout(INVALIDATE_LAYOUT_RECURSIVE);
  107. }
  108. void TBScrollContainer::SetAdaptContentSize(bool adapt)
  109. {
  110. if (m_adapt_content_size == adapt)
  111. return;
  112. m_adapt_content_size = adapt;
  113. InvalidateLayout(INVALIDATE_LAYOUT_TARGET_ONLY);
  114. }
  115. void TBScrollContainer::SetScrollMode(SCROLL_MODE mode)
  116. {
  117. if (mode == m_mode)
  118. return;
  119. m_mode = mode;
  120. InvalidateLayout(INVALIDATE_LAYOUT_TARGET_ONLY);
  121. }
  122. void TBScrollContainer::ScrollTo(int x, int y)
  123. {
  124. int old_x = m_scrollbar_x.GetValue();
  125. int old_y = m_scrollbar_y.GetValue();
  126. m_scrollbar_x.SetValue(x);
  127. m_scrollbar_y.SetValue(y);
  128. if (old_x != m_scrollbar_x.GetValue() ||
  129. old_y != m_scrollbar_y.GetValue())
  130. Invalidate();
  131. }
  132. TBWidget::ScrollInfo TBScrollContainer::GetScrollInfo()
  133. {
  134. ScrollInfo info;
  135. info.min_x = static_cast<int>(m_scrollbar_x.GetMinValue());
  136. info.min_y = static_cast<int>(m_scrollbar_y.GetMinValue());
  137. info.max_x = static_cast<int>(m_scrollbar_x.GetMaxValue());
  138. info.max_y = static_cast<int>(m_scrollbar_y.GetMaxValue());
  139. info.x = m_scrollbar_x.GetValue();
  140. info.y = m_scrollbar_y.GetValue();
  141. return info;
  142. }
  143. void TBScrollContainer::InvalidateLayout(INVALIDATE_LAYOUT il)
  144. {
  145. m_layout_is_invalid = true;
  146. // No recursion up to parents here unless we adapt to content size.
  147. if (m_adapt_to_content_size)
  148. TBWidget::InvalidateLayout(il);
  149. }
  150. TBRect TBScrollContainer::GetPaddingRect()
  151. {
  152. int visible_w = GetRect().w;
  153. int visible_h = GetRect().h;
  154. if (m_scrollbar_x.GetOpacity())
  155. visible_h -= m_scrollbar_x.GetPreferredSize().pref_h;
  156. if (m_scrollbar_y.GetOpacity())
  157. visible_w -= m_scrollbar_y.GetPreferredSize().pref_w;
  158. return TBRect(0, 0, visible_w, visible_h);
  159. }
  160. PreferredSize TBScrollContainer::OnCalculatePreferredContentSize(const SizeConstraints &constraints)
  161. {
  162. PreferredSize ps;
  163. ps.pref_w = ps.pref_h = 100;
  164. ps.min_w = ps.min_h = 50;
  165. if (m_adapt_to_content_size)
  166. {
  167. if (TBWidget *content_child = m_root.GetFirstChild())
  168. {
  169. ps = content_child->GetPreferredSize(constraints);
  170. int scrollbar_y_w = m_scrollbar_y.GetPreferredSize().pref_w;
  171. int scrollbar_x_h = m_scrollbar_x.GetPreferredSize().pref_h;
  172. ps.pref_w += scrollbar_y_w;
  173. ps.max_w += scrollbar_y_w;
  174. if (m_mode == SCROLL_MODE_X_Y ||
  175. m_mode == SCROLL_MODE_X_AUTO_Y_AUTO)
  176. {
  177. ps.pref_h += scrollbar_x_h;
  178. ps.max_h += scrollbar_x_h;
  179. }
  180. }
  181. }
  182. return ps;
  183. }
  184. bool TBScrollContainer::OnEvent(const TBWidgetEvent &ev)
  185. {
  186. if (ev.type == EVENT_TYPE_CHANGED && (ev.target == &m_scrollbar_x || ev.target == &m_scrollbar_y))
  187. {
  188. Invalidate();
  189. OnScroll(m_scrollbar_x.GetValue(), m_scrollbar_y.GetValue());
  190. return true;
  191. }
  192. else if (ev.type == EVENT_TYPE_WHEEL && ev.modifierkeys == TB_MODIFIER_NONE && !m_ignore_scroll_events)
  193. {
  194. double old_val_y = m_scrollbar_y.GetValueDouble();
  195. m_scrollbar_y.SetValueDouble(old_val_y + ev.delta_y * TBSystem::GetPixelsPerLine());
  196. double old_val_x = m_scrollbar_x.GetValueDouble();
  197. m_scrollbar_x.SetValueDouble(old_val_x + ev.delta_x * TBSystem::GetPixelsPerLine());
  198. return (m_scrollbar_x.GetValueDouble() != old_val_x || m_scrollbar_y.GetValueDouble() != old_val_y);
  199. }
  200. else if (ev.type == EVENT_TYPE_KEY_DOWN && !m_ignore_scroll_events)
  201. {
  202. if (ev.special_key == TB_KEY_LEFT && m_scrollbar_x.CanScrollNegative())
  203. ScrollBySmooth(-TBSystem::GetPixelsPerLine(), 0);
  204. else if (ev.special_key == TB_KEY_RIGHT && m_scrollbar_x.CanScrollPositive())
  205. ScrollBySmooth(TBSystem::GetPixelsPerLine(), 0);
  206. else if (ev.special_key == TB_KEY_UP && m_scrollbar_y.CanScrollNegative())
  207. ScrollBySmooth(0, -TBSystem::GetPixelsPerLine());
  208. else if (ev.special_key == TB_KEY_DOWN && m_scrollbar_y.CanScrollPositive())
  209. ScrollBySmooth(0, TBSystem::GetPixelsPerLine());
  210. else if (ev.special_key == TB_KEY_PAGE_UP && m_scrollbar_y.CanScrollNegative())
  211. ScrollBySmooth(0, -GetPaddingRect().h);
  212. else if (ev.special_key == TB_KEY_PAGE_DOWN && m_scrollbar_y.CanScrollPositive())
  213. ScrollBySmooth(0, GetPaddingRect().h);
  214. else if (ev.special_key == TB_KEY_HOME)
  215. ScrollToSmooth(m_scrollbar_x.GetValue(), 0);
  216. else if (ev.special_key == TB_KEY_END)
  217. ScrollToSmooth(m_scrollbar_x.GetValue(), (int)m_scrollbar_y.GetMaxValue());
  218. else
  219. return false;
  220. return true;
  221. }
  222. return false;
  223. }
  224. void TBScrollContainer::OnProcess()
  225. {
  226. SizeConstraints sc(GetRect().w, GetRect().h);
  227. ValidateLayout(sc);
  228. }
  229. void TBScrollContainer::ValidateLayout(const SizeConstraints &constraints)
  230. {
  231. if (!m_layout_is_invalid)
  232. return;
  233. m_layout_is_invalid = false;
  234. // Layout scrollbars (no matter if they are visible or not)
  235. int scrollbar_y_w = m_scrollbar_y.GetPreferredSize().pref_w;
  236. int scrollbar_x_h = m_scrollbar_x.GetPreferredSize().pref_h;
  237. m_scrollbar_x.SetRect(TBRect(0, GetRect().h - scrollbar_x_h, GetRect().w - scrollbar_y_w, scrollbar_x_h));
  238. m_scrollbar_y.SetRect(TBRect(GetRect().w - scrollbar_y_w, 0, scrollbar_y_w, GetRect().h));
  239. if (TBWidget *content_child = m_root.GetFirstChild())
  240. {
  241. int horizontal_padding = TBScrollBarVisibility::IsAlwaysOnY(m_mode) ? scrollbar_y_w : 0;
  242. int vertical_padding = TBScrollBarVisibility::IsAlwaysOnX(m_mode) ? scrollbar_x_h : 0;
  243. SizeConstraints inner_sc = constraints.ConstrainByPadding(horizontal_padding, vertical_padding);
  244. PreferredSize ps = content_child->GetPreferredSize(inner_sc);
  245. TBScrollBarVisibility visibility = TBScrollBarVisibility::Solve(m_mode, ps.pref_w, ps.pref_h,
  246. GetRect().w, GetRect().h,
  247. scrollbar_x_h, scrollbar_y_w);
  248. m_scrollbar_x.SetOpacity(visibility.x_on ? 1.f : 0.f);
  249. m_scrollbar_y.SetOpacity(visibility.y_on ? 1.f : 0.f);
  250. m_root.SetRect(TBRect(0, 0, visibility.visible_w, visibility.visible_h));
  251. int content_w, content_h;
  252. if (m_adapt_content_size)
  253. {
  254. content_w = MAX(ps.pref_w, m_root.GetRect().w);
  255. content_h = MAX(ps.pref_h, m_root.GetRect().h);
  256. if (!visibility.x_on && m_root.GetRect().w < ps.pref_w)
  257. content_w = MIN(ps.pref_w, m_root.GetRect().w);
  258. }
  259. else
  260. {
  261. content_w = ps.pref_w;
  262. content_h = ps.pref_h;
  263. }
  264. content_child->SetRect(TBRect(0, 0, content_w, content_h));
  265. double limit_max_w = MAX(0, content_w - m_root.GetRect().w);
  266. double limit_max_h = MAX(0, content_h - m_root.GetRect().h);
  267. m_scrollbar_x.SetLimits(0, limit_max_w, m_root.GetRect().w);
  268. m_scrollbar_y.SetLimits(0, limit_max_h, m_root.GetRect().h);
  269. }
  270. }
  271. void TBScrollContainer::OnResized(int old_w, int old_h)
  272. {
  273. InvalidateLayout(INVALIDATE_LAYOUT_TARGET_ONLY);
  274. SizeConstraints sc(GetRect().w, GetRect().h);
  275. ValidateLayout(sc);
  276. }
  277. }; // namespace tb