PageRenderTime 36ms CodeModel.GetById 11ms app.highlight 21ms RepoModel.GetById 1ms app.codeStats 0ms

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