/xbmc/guilib/GUIFixedListContainer.cpp

http://github.com/xbmc/xbmc · C++ · 308 lines · 263 code · 23 blank · 22 comment · 54 complexity · d9ad334d1505faa81591622161bf24e9 MD5 · raw file

  1. /*
  2. * Copyright (C) 2005-2018 Team Kodi
  3. * This file is part of Kodi - https://kodi.tv
  4. *
  5. * SPDX-License-Identifier: GPL-2.0-or-later
  6. * See LICENSES/README.md for more information.
  7. */
  8. #include "GUIFixedListContainer.h"
  9. #include "GUIListItemLayout.h"
  10. #include "input/Key.h"
  11. CGUIFixedListContainer::CGUIFixedListContainer(int parentID, int controlID, float posX, float posY, float width, float height, ORIENTATION orientation, const CScroller& scroller, int preloadItems, int fixedPosition, int cursorRange)
  12. : CGUIBaseContainer(parentID, controlID, posX, posY, width, height, orientation, scroller, preloadItems)
  13. {
  14. ControlType = GUICONTAINER_FIXEDLIST;
  15. m_type = VIEW_TYPE_LIST;
  16. m_fixedCursor = fixedPosition;
  17. m_cursorRange = std::max(0, cursorRange);
  18. SetCursor(m_fixedCursor);
  19. }
  20. CGUIFixedListContainer::~CGUIFixedListContainer(void) = default;
  21. bool CGUIFixedListContainer::OnAction(const CAction &action)
  22. {
  23. switch (action.GetID())
  24. {
  25. case ACTION_PAGE_UP:
  26. {
  27. Scroll(-m_itemsPerPage);
  28. return true;
  29. }
  30. break;
  31. case ACTION_PAGE_DOWN:
  32. {
  33. Scroll(m_itemsPerPage);
  34. return true;
  35. }
  36. break;
  37. // smooth scrolling (for analog controls)
  38. case ACTION_SCROLL_UP:
  39. {
  40. m_analogScrollCount += action.GetAmount() * action.GetAmount();
  41. bool handled = false;
  42. while (m_analogScrollCount > 0.4)
  43. {
  44. handled = true;
  45. m_analogScrollCount -= 0.4f;
  46. Scroll(-1);
  47. }
  48. return handled;
  49. }
  50. break;
  51. case ACTION_SCROLL_DOWN:
  52. {
  53. m_analogScrollCount += action.GetAmount() * action.GetAmount();
  54. bool handled = false;
  55. while (m_analogScrollCount > 0.4)
  56. {
  57. handled = true;
  58. m_analogScrollCount -= 0.4f;
  59. Scroll(1);
  60. }
  61. return handled;
  62. }
  63. break;
  64. }
  65. return CGUIBaseContainer::OnAction(action);
  66. }
  67. bool CGUIFixedListContainer::MoveUp(bool wrapAround)
  68. {
  69. int item = GetSelectedItem();
  70. if (item > 0)
  71. SelectItem(item - 1);
  72. else if (wrapAround)
  73. {
  74. SelectItem((int)m_items.size() - 1);
  75. SetContainerMoving(-1);
  76. }
  77. else
  78. return false;
  79. return true;
  80. }
  81. bool CGUIFixedListContainer::MoveDown(bool wrapAround)
  82. {
  83. int item = GetSelectedItem();
  84. if (item < (int)m_items.size() - 1)
  85. SelectItem(item + 1);
  86. else if (wrapAround)
  87. { // move first item in list
  88. SelectItem(0);
  89. SetContainerMoving(1);
  90. }
  91. else
  92. return false;
  93. return true;
  94. }
  95. void CGUIFixedListContainer::Scroll(int amount)
  96. {
  97. // increase or decrease the offset within [-minCursor, m_items.size() - maxCursor]
  98. int minCursor, maxCursor;
  99. GetCursorRange(minCursor, maxCursor);
  100. const int nextCursor = GetCursor() + amount;
  101. int offset = GetOffset() + amount;
  102. if (offset < -minCursor)
  103. {
  104. offset = -minCursor;
  105. SetCursor(nextCursor < minCursor ? minCursor : nextCursor);
  106. }
  107. if (offset > (int)m_items.size() - 1 - maxCursor)
  108. {
  109. offset = m_items.size() - 1 - maxCursor;
  110. SetCursor(nextCursor > maxCursor ? maxCursor : nextCursor);
  111. }
  112. ScrollToOffset(offset);
  113. }
  114. bool CGUIFixedListContainer::GetOffsetRange(int &minOffset, int &maxOffset) const
  115. {
  116. GetCursorRange(minOffset, maxOffset);
  117. minOffset = -minOffset;
  118. maxOffset = m_items.size() - maxOffset - 1;
  119. return true;
  120. }
  121. void CGUIFixedListContainer::ValidateOffset()
  122. {
  123. if (!m_layout) return;
  124. // ensure our fixed cursor position is valid
  125. if (m_fixedCursor >= m_itemsPerPage)
  126. m_fixedCursor = m_itemsPerPage - 1;
  127. if (m_fixedCursor < 0)
  128. m_fixedCursor = 0;
  129. // compute our minimum and maximum cursor positions
  130. int minCursor, maxCursor;
  131. GetCursorRange(minCursor, maxCursor);
  132. // assure our cursor is between these limits
  133. SetCursor(std::max(GetCursor(), minCursor));
  134. SetCursor(std::min(GetCursor(), maxCursor));
  135. int minOffset, maxOffset;
  136. GetOffsetRange(minOffset, maxOffset);
  137. // and finally ensure our offset is valid
  138. // don't validate offset if we are scrolling in case the tween image exceed <0, 1> range
  139. if (GetOffset() > maxOffset || (!m_scroller.IsScrolling() && m_scroller.GetValue() > maxOffset * m_layout->Size(m_orientation)))
  140. {
  141. SetOffset(std::max(-minCursor, maxOffset));
  142. m_scroller.SetValue(GetOffset() * m_layout->Size(m_orientation));
  143. }
  144. if (GetOffset() < minOffset || (!m_scroller.IsScrolling() && m_scroller.GetValue() < minOffset * m_layout->Size(m_orientation)))
  145. {
  146. SetOffset(minOffset);
  147. m_scroller.SetValue(GetOffset() * m_layout->Size(m_orientation));
  148. }
  149. }
  150. int CGUIFixedListContainer::GetCursorFromPoint(const CPoint &point, CPoint *itemPoint) const
  151. {
  152. if (!m_focusedLayout || !m_layout)
  153. return -1;
  154. int minCursor, maxCursor;
  155. GetCursorRange(minCursor, maxCursor);
  156. // see if the point is either side of our focus range
  157. float start = (minCursor + 0.2f) * m_layout->Size(m_orientation);
  158. float end = (maxCursor - 0.2f) * m_layout->Size(m_orientation) + m_focusedLayout->Size(m_orientation);
  159. float pos = (m_orientation == VERTICAL) ? point.y : point.x;
  160. if (pos >= start && pos <= end)
  161. { // select the appropriate item
  162. pos -= minCursor * m_layout->Size(m_orientation);
  163. for (int row = minCursor; row <= maxCursor; row++)
  164. {
  165. const CGUIListItemLayout *layout = (row == GetCursor()) ? m_focusedLayout : m_layout;
  166. if (pos < layout->Size(m_orientation))
  167. {
  168. if (!InsideLayout(layout, point))
  169. return -1;
  170. return row;
  171. }
  172. pos -= layout->Size(m_orientation);
  173. }
  174. }
  175. return -1;
  176. }
  177. bool CGUIFixedListContainer::SelectItemFromPoint(const CPoint &point)
  178. {
  179. if (!m_focusedLayout || !m_layout)
  180. return false;
  181. MarkDirtyRegion();
  182. const float mouse_scroll_speed = 0.25f;
  183. const float mouse_max_amount = 1.5f;
  184. float sizeOfItem = m_layout->Size(m_orientation);
  185. int minCursor, maxCursor;
  186. GetCursorRange(minCursor, maxCursor);
  187. // see if the point is either side of our focus range
  188. float start = (minCursor + 0.2f) * sizeOfItem;
  189. float end = (maxCursor - 0.2f) * sizeOfItem + m_focusedLayout->Size(m_orientation);
  190. float pos = (m_orientation == VERTICAL) ? point.y : point.x;
  191. if (pos < start && GetOffset() > -minCursor)
  192. { // scroll backward
  193. if (!InsideLayout(m_layout, point))
  194. return false;
  195. float amount = std::min((start - pos) / sizeOfItem, mouse_max_amount);
  196. m_analogScrollCount += amount * amount * mouse_scroll_speed;
  197. if (m_analogScrollCount > 1)
  198. {
  199. ScrollToOffset(GetOffset() - 1);
  200. m_analogScrollCount = 0;
  201. }
  202. return true;
  203. }
  204. else if (pos > end && GetOffset() + maxCursor < (int)m_items.size() - 1)
  205. {
  206. if (!InsideLayout(m_layout, point))
  207. return false;
  208. // scroll forward
  209. float amount = std::min((pos - end) / sizeOfItem, mouse_max_amount);
  210. m_analogScrollCount += amount * amount * mouse_scroll_speed;
  211. if (m_analogScrollCount > 1)
  212. {
  213. ScrollToOffset(GetOffset() + 1);
  214. m_analogScrollCount = 0;
  215. }
  216. return true;
  217. }
  218. else
  219. { // select the appropriate item
  220. int cursor = GetCursorFromPoint(point);
  221. if (cursor < 0)
  222. return false;
  223. // calling SelectItem() here will focus the item and scroll, which isn't really what we're after
  224. SetCursor(cursor);
  225. return true;
  226. }
  227. }
  228. void CGUIFixedListContainer::SelectItem(int item)
  229. {
  230. // Check that GetOffset() is valid
  231. ValidateOffset();
  232. // only select an item if it's in a valid range
  233. if (item >= 0 && item < (int)m_items.size())
  234. {
  235. // Select the item requested - we first set the cursor position
  236. // which may be different at either end of the list, then the offset
  237. int minCursor, maxCursor;
  238. GetCursorRange(minCursor, maxCursor);
  239. int cursor;
  240. if ((int)m_items.size() - 1 - item <= maxCursor - m_fixedCursor)
  241. cursor = std::max(m_fixedCursor, maxCursor + item - (int)m_items.size() + 1);
  242. else if (item <= m_fixedCursor - minCursor)
  243. cursor = std::min(m_fixedCursor, minCursor + item);
  244. else
  245. cursor = m_fixedCursor;
  246. if (cursor != GetCursor())
  247. SetContainerMoving(cursor - GetCursor());
  248. SetCursor(cursor);
  249. ScrollToOffset(item - GetCursor());
  250. MarkDirtyRegion();
  251. }
  252. }
  253. bool CGUIFixedListContainer::HasPreviousPage() const
  254. {
  255. return (GetOffset() > 0);
  256. }
  257. bool CGUIFixedListContainer::HasNextPage() const
  258. {
  259. return (GetOffset() < (int)m_items.size() - m_itemsPerPage && (int)m_items.size() >= m_itemsPerPage);
  260. }
  261. int CGUIFixedListContainer::GetCurrentPage() const
  262. {
  263. int offset = CorrectOffset(GetOffset(), GetCursor());
  264. if (offset + m_itemsPerPage - GetCursor() >= (int)GetRows()) // last page
  265. return (GetRows() + m_itemsPerPage - 1) / m_itemsPerPage;
  266. return offset / m_itemsPerPage + 1;
  267. }
  268. void CGUIFixedListContainer::GetCursorRange(int &minCursor, int &maxCursor) const
  269. {
  270. minCursor = std::max(m_fixedCursor - m_cursorRange, 0);
  271. maxCursor = std::min(m_fixedCursor + m_cursorRange, m_itemsPerPage);
  272. if (!m_items.size())
  273. {
  274. minCursor = m_fixedCursor;
  275. maxCursor = m_fixedCursor;
  276. return;
  277. }
  278. while (maxCursor - minCursor > (int)m_items.size() - 1)
  279. {
  280. if (maxCursor - m_fixedCursor > m_fixedCursor - minCursor)
  281. maxCursor--;
  282. else
  283. minCursor++;
  284. }
  285. }