tb_scroll_container.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  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_ignore_scroll_events(false)
  88. , m_mode(SCROLL_MODE_X_Y)
  89. {
  90. AddChild(&m_scrollbar_x);
  91. AddChild(&m_scrollbar_y);
  92. AddChild(&m_root);
  93. m_scrollbar_y.SetAxis(AXIS_Y);
  94. }
  95. TBScrollContainer::~TBScrollContainer()
  96. {
  97. RemoveChild(&m_root);
  98. RemoveChild(&m_scrollbar_y);
  99. RemoveChild(&m_scrollbar_x);
  100. }
  101. void TBScrollContainer::SetAdaptToContentSize(bool adapt)
  102. {
  103. if (m_adapt_to_content_size == adapt)
  104. return;
  105. InvalidateLayout(INVALIDATE_LAYOUT_RECURSIVE);
  106. m_adapt_to_content_size = adapt;
  107. InvalidateLayout(INVALIDATE_LAYOUT_RECURSIVE);
  108. }
  109. void TBScrollContainer::SetAdaptContentSize(bool adapt)
  110. {
  111. if (m_adapt_content_size == adapt)
  112. return;
  113. m_adapt_content_size = adapt;
  114. InvalidateLayout(INVALIDATE_LAYOUT_TARGET_ONLY);
  115. }
  116. void TBScrollContainer::SetScrollMode(SCROLL_MODE mode)
  117. {
  118. if (mode == m_mode)
  119. return;
  120. m_mode = mode;
  121. InvalidateLayout(INVALIDATE_LAYOUT_TARGET_ONLY);
  122. }
  123. void TBScrollContainer::ScrollTo(int x, int y)
  124. {
  125. int old_x = m_scrollbar_x.GetValue();
  126. int old_y = m_scrollbar_y.GetValue();
  127. m_scrollbar_x.SetValue(x);
  128. m_scrollbar_y.SetValue(y);
  129. if (old_x != m_scrollbar_x.GetValue() ||
  130. old_y != m_scrollbar_y.GetValue())
  131. Invalidate();
  132. }
  133. TBWidget::ScrollInfo TBScrollContainer::GetScrollInfo()
  134. {
  135. ScrollInfo info;
  136. info.min_x = static_cast<int>(m_scrollbar_x.GetMinValue());
  137. info.min_y = static_cast<int>(m_scrollbar_y.GetMinValue());
  138. info.max_x = static_cast<int>(m_scrollbar_x.GetMaxValue());
  139. info.max_y = static_cast<int>(m_scrollbar_y.GetMaxValue());
  140. info.x = m_scrollbar_x.GetValue();
  141. info.y = m_scrollbar_y.GetValue();
  142. return info;
  143. }
  144. void TBScrollContainer::InvalidateLayout(INVALIDATE_LAYOUT il)
  145. {
  146. m_layout_is_invalid = true;
  147. // No recursion up to parents here unless we adapt to content size.
  148. if (m_adapt_to_content_size)
  149. TBWidget::InvalidateLayout(il);
  150. }
  151. TBRect TBScrollContainer::GetPaddingRect()
  152. {
  153. int visible_w = GetRect().w;
  154. int visible_h = GetRect().h;
  155. if (m_scrollbar_x.GetOpacity())
  156. visible_h -= m_scrollbar_x.GetPreferredSize().pref_h;
  157. if (m_scrollbar_y.GetOpacity())
  158. visible_w -= m_scrollbar_y.GetPreferredSize().pref_w;
  159. return TBRect(0, 0, visible_w, visible_h);
  160. }
  161. PreferredSize TBScrollContainer::OnCalculatePreferredContentSize(const SizeConstraints &constraints)
  162. {
  163. PreferredSize ps;
  164. ps.pref_w = ps.pref_h = 100;
  165. ps.min_w = ps.min_h = 50;
  166. if (m_adapt_to_content_size)
  167. {
  168. if (TBWidget *content_child = m_root.GetFirstChild())
  169. {
  170. ps = content_child->GetPreferredSize(constraints);
  171. int scrollbar_y_w = m_scrollbar_y.GetPreferredSize().pref_w;
  172. int scrollbar_x_h = m_scrollbar_x.GetPreferredSize().pref_h;
  173. ps.pref_w += scrollbar_y_w;
  174. ps.max_w += scrollbar_y_w;
  175. if (m_mode == SCROLL_MODE_X_Y ||
  176. m_mode == SCROLL_MODE_X_AUTO_Y_AUTO)
  177. {
  178. ps.pref_h += scrollbar_x_h;
  179. ps.max_h += scrollbar_x_h;
  180. }
  181. }
  182. }
  183. return ps;
  184. }
  185. bool TBScrollContainer::OnEvent(const TBWidgetEvent &ev)
  186. {
  187. if (ev.type == EVENT_TYPE_CHANGED && (ev.target == &m_scrollbar_x || ev.target == &m_scrollbar_y))
  188. {
  189. Invalidate();
  190. OnScroll(m_scrollbar_x.GetValue(), m_scrollbar_y.GetValue());
  191. return true;
  192. }
  193. else if (ev.type == EVENT_TYPE_WHEEL && ev.modifierkeys == TB_MODIFIER_NONE && !m_ignore_scroll_events)
  194. {
  195. double old_val_y = m_scrollbar_y.GetValueDouble();
  196. m_scrollbar_y.SetValueDouble(old_val_y + ev.delta_y * TBSystem::GetPixelsPerLine());
  197. double old_val_x = m_scrollbar_x.GetValueDouble();
  198. m_scrollbar_x.SetValueDouble(old_val_x + ev.delta_x * TBSystem::GetPixelsPerLine());
  199. return (m_scrollbar_x.GetValueDouble() != old_val_x || m_scrollbar_y.GetValueDouble() != old_val_y);
  200. }
  201. else if (ev.type == EVENT_TYPE_KEY_DOWN && !m_ignore_scroll_events)
  202. {
  203. // ATOMIC: Disabling arrow key scroll
  204. /*
  205. if (ev.special_key == TB_KEY_LEFT && m_scrollbar_x.CanScrollNegative())
  206. ScrollBySmooth(-TBSystem::GetPixelsPerLine(), 0);
  207. else if (ev.special_key == TB_KEY_RIGHT && m_scrollbar_x.CanScrollPositive())
  208. ScrollBySmooth(TBSystem::GetPixelsPerLine(), 0);
  209. else if (ev.special_key == TB_KEY_UP && m_scrollbar_y.CanScrollNegative())
  210. ScrollBySmooth(0, -TBSystem::GetPixelsPerLine());
  211. else if (ev.special_key == TB_KEY_DOWN && m_scrollbar_y.CanScrollPositive())
  212. ScrollBySmooth(0, TBSystem::GetPixelsPerLine());
  213. else*/ if (ev.special_key == TB_KEY_PAGE_UP && m_scrollbar_y.CanScrollNegative())
  214. ScrollBySmooth(0, -GetPaddingRect().h);
  215. else if (ev.special_key == TB_KEY_PAGE_DOWN && m_scrollbar_y.CanScrollPositive())
  216. ScrollBySmooth(0, GetPaddingRect().h);
  217. else if (ev.special_key == TB_KEY_HOME)
  218. ScrollToSmooth(m_scrollbar_x.GetValue(), 0);
  219. else if (ev.special_key == TB_KEY_END)
  220. ScrollToSmooth(m_scrollbar_x.GetValue(), (int)m_scrollbar_y.GetMaxValue());
  221. else
  222. return false;
  223. return true;
  224. }
  225. return false;
  226. }
  227. void TBScrollContainer::OnProcess()
  228. {
  229. SizeConstraints sc(GetRect().w, GetRect().h);
  230. ValidateLayout(sc);
  231. }
  232. void TBScrollContainer::ValidateLayout(const SizeConstraints &constraints)
  233. {
  234. if (!m_layout_is_invalid)
  235. return;
  236. m_layout_is_invalid = false;
  237. // Layout scrollbars (no matter if they are visible or not)
  238. int scrollbar_y_w = m_scrollbar_y.GetPreferredSize().pref_w;
  239. int scrollbar_x_h = m_scrollbar_x.GetPreferredSize().pref_h;
  240. m_scrollbar_x.SetRect(TBRect(0, GetRect().h - scrollbar_x_h, GetRect().w - scrollbar_y_w, scrollbar_x_h));
  241. m_scrollbar_y.SetRect(TBRect(GetRect().w - scrollbar_y_w, 0, scrollbar_y_w, GetRect().h));
  242. if (TBWidget *content_child = m_root.GetFirstChild())
  243. {
  244. int horizontal_padding = TBScrollBarVisibility::IsAlwaysOnY(m_mode) ? scrollbar_y_w : 0;
  245. int vertical_padding = TBScrollBarVisibility::IsAlwaysOnX(m_mode) ? scrollbar_x_h : 0;
  246. SizeConstraints inner_sc = constraints.ConstrainByPadding(horizontal_padding, vertical_padding);
  247. PreferredSize ps = content_child->GetPreferredSize(inner_sc);
  248. TBScrollBarVisibility visibility = TBScrollBarVisibility::Solve(m_mode, ps.pref_w, ps.pref_h,
  249. GetRect().w, GetRect().h,
  250. scrollbar_x_h, scrollbar_y_w);
  251. m_scrollbar_x.SetOpacity(visibility.x_on ? 1.f : 0.f);
  252. m_scrollbar_y.SetOpacity(visibility.y_on ? 1.f : 0.f);
  253. m_root.SetRect(TBRect(0, 0, visibility.visible_w, visibility.visible_h));
  254. int content_w, content_h;
  255. if (m_adapt_content_size)
  256. {
  257. content_w = MAX(ps.pref_w, m_root.GetRect().w);
  258. content_h = MAX(ps.pref_h, m_root.GetRect().h);
  259. if (!visibility.x_on && m_root.GetRect().w < ps.pref_w)
  260. content_w = MIN(ps.pref_w, m_root.GetRect().w);
  261. }
  262. else
  263. {
  264. content_w = ps.pref_w;
  265. content_h = ps.pref_h;
  266. }
  267. content_child->SetRect(TBRect(0, 0, content_w, content_h));
  268. double limit_max_w = MAX(0, content_w - m_root.GetRect().w);
  269. double limit_max_h = MAX(0, content_h - m_root.GetRect().h);
  270. m_scrollbar_x.SetLimits(0, limit_max_w, m_root.GetRect().w);
  271. m_scrollbar_y.SetLimits(0, limit_max_h, m_root.GetRect().h);
  272. }
  273. }
  274. void TBScrollContainer::OnResized(int old_w, int old_h)
  275. {
  276. InvalidateLayout(INVALIDATE_LAYOUT_TARGET_ONLY);
  277. SizeConstraints sc(GetRect().w, GetRect().h);
  278. ValidateLayout(sc);
  279. // ATOMIC BEGIN So pure UIWidgets can handle resize
  280. if (GetDelegate()) { GetDelegate()->OnResized(old_w, old_h); }
  281. // ATOMIC END
  282. }
  283. }; // namespace tb