2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
9 #include "GUIFixedListContainer.h"
11 #include "GUIListItemLayout.h"
12 #include "input/actions/Action.h"
13 #include "input/actions/ActionIDs.h"
15 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
)
16 : CGUIBaseContainer(parentID
, controlID
, posX
, posY
, width
, height
, orientation
, scroller
, preloadItems
)
18 ControlType
= GUICONTAINER_FIXEDLIST
;
19 m_type
= VIEW_TYPE_LIST
;
20 m_fixedCursor
= fixedPosition
;
21 m_cursorRange
= std::max(0, cursorRange
);
22 SetCursor(m_fixedCursor
);
25 CGUIFixedListContainer::~CGUIFixedListContainer(void) = default;
27 bool CGUIFixedListContainer::OnAction(const CAction
&action
)
29 switch (action
.GetID())
33 Scroll(-m_itemsPerPage
);
37 case ACTION_PAGE_DOWN
:
39 Scroll(m_itemsPerPage
);
43 // smooth scrolling (for analog controls)
44 case ACTION_SCROLL_UP
:
46 m_analogScrollCount
+= action
.GetAmount() * action
.GetAmount();
48 while (m_analogScrollCount
> 0.4f
)
51 m_analogScrollCount
-= 0.4f
;
57 case ACTION_SCROLL_DOWN
:
59 m_analogScrollCount
+= action
.GetAmount() * action
.GetAmount();
61 while (m_analogScrollCount
> 0.4f
)
64 m_analogScrollCount
-= 0.4f
;
71 return CGUIBaseContainer::OnAction(action
);
74 bool CGUIFixedListContainer::MoveUp(bool wrapAround
)
76 int item
= GetSelectedItem();
81 SelectItem((int)m_items
.size() - 1);
82 SetContainerMoving(-1);
89 bool CGUIFixedListContainer::MoveDown(bool wrapAround
)
91 int item
= GetSelectedItem();
92 if (item
< (int)m_items
.size() - 1)
95 { // move first item in list
97 SetContainerMoving(1);
104 void CGUIFixedListContainer::Scroll(int amount
)
106 // increase or decrease the offset within [-minCursor, m_items.size() - maxCursor]
107 int minCursor
, maxCursor
;
108 GetCursorRange(minCursor
, maxCursor
);
109 const int nextCursor
= GetCursor() + amount
;
110 int offset
= GetOffset() + amount
;
111 if (offset
< -minCursor
)
114 SetCursor(nextCursor
< minCursor
? minCursor
: nextCursor
);
116 if (offset
> (int)m_items
.size() - 1 - maxCursor
)
118 offset
= m_items
.size() - 1 - maxCursor
;
119 SetCursor(nextCursor
> maxCursor
? maxCursor
: nextCursor
);
121 ScrollToOffset(offset
);
124 bool CGUIFixedListContainer::GetOffsetRange(int &minOffset
, int &maxOffset
) const
126 GetCursorRange(minOffset
, maxOffset
);
127 minOffset
= -minOffset
;
128 maxOffset
= m_items
.size() - maxOffset
- 1;
132 void CGUIFixedListContainer::ValidateOffset()
134 if (!m_layout
) return;
135 // ensure our fixed cursor position is valid
136 if (m_fixedCursor
>= m_itemsPerPage
)
137 m_fixedCursor
= m_itemsPerPage
- 1;
138 if (m_fixedCursor
< 0)
140 // compute our minimum and maximum cursor positions
141 int minCursor
, maxCursor
;
142 GetCursorRange(minCursor
, maxCursor
);
143 // assure our cursor is between these limits
144 SetCursor(std::max(GetCursor(), minCursor
));
145 SetCursor(std::min(GetCursor(), maxCursor
));
146 int minOffset
, maxOffset
;
147 GetOffsetRange(minOffset
, maxOffset
);
148 // and finally ensure our offset is valid
149 // don't validate offset if we are scrolling in case the tween image exceed <0, 1> range
150 if (GetOffset() > maxOffset
|| (!m_scroller
.IsScrolling() && m_scroller
.GetValue() > maxOffset
* m_layout
->Size(m_orientation
)))
152 SetOffset(std::max(-minCursor
, maxOffset
));
153 m_scroller
.SetValue(GetOffset() * m_layout
->Size(m_orientation
));
155 if (GetOffset() < minOffset
|| (!m_scroller
.IsScrolling() && m_scroller
.GetValue() < minOffset
* m_layout
->Size(m_orientation
)))
157 SetOffset(minOffset
);
158 m_scroller
.SetValue(GetOffset() * m_layout
->Size(m_orientation
));
162 int CGUIFixedListContainer::GetCursorFromPoint(const CPoint
&point
, CPoint
*itemPoint
) const
164 if (!m_focusedLayout
|| !m_layout
)
166 int minCursor
, maxCursor
;
167 GetCursorRange(minCursor
, maxCursor
);
168 // see if the point is either side of our focus range
169 float start
= (minCursor
+ 0.2f
) * m_layout
->Size(m_orientation
);
170 float end
= (maxCursor
- 0.2f
) * m_layout
->Size(m_orientation
) + m_focusedLayout
->Size(m_orientation
);
171 float pos
= (m_orientation
== VERTICAL
) ? point
.y
: point
.x
;
172 if (pos
>= start
&& pos
<= end
)
173 { // select the appropriate item
174 pos
-= minCursor
* m_layout
->Size(m_orientation
);
175 for (int row
= minCursor
; row
<= maxCursor
; row
++)
177 const CGUIListItemLayout
*layout
= (row
== GetCursor()) ? m_focusedLayout
: m_layout
;
178 if (pos
< layout
->Size(m_orientation
))
180 if (!InsideLayout(layout
, point
))
184 pos
-= layout
->Size(m_orientation
);
190 bool CGUIFixedListContainer::SelectItemFromPoint(const CPoint
&point
)
192 if (!m_focusedLayout
|| !m_layout
)
197 const float mouse_scroll_speed
= 0.25f
;
198 const float mouse_max_amount
= 1.5f
;
199 float sizeOfItem
= m_layout
->Size(m_orientation
);
200 int minCursor
, maxCursor
;
201 GetCursorRange(minCursor
, maxCursor
);
202 // see if the point is either side of our focus range
203 float start
= (minCursor
+ 0.2f
) * sizeOfItem
;
204 float end
= (maxCursor
- 0.2f
) * sizeOfItem
+ m_focusedLayout
->Size(m_orientation
);
205 float pos
= (m_orientation
== VERTICAL
) ? point
.y
: point
.x
;
206 if (pos
< start
&& GetOffset() > -minCursor
)
208 if (!InsideLayout(m_layout
, point
))
210 float amount
= std::min((start
- pos
) / sizeOfItem
, mouse_max_amount
);
211 m_analogScrollCount
+= amount
* amount
* mouse_scroll_speed
;
212 if (m_analogScrollCount
> 1)
214 ScrollToOffset(GetOffset() - 1);
215 m_analogScrollCount
= 0;
219 else if (pos
> end
&& GetOffset() + maxCursor
< (int)m_items
.size() - 1)
221 if (!InsideLayout(m_layout
, point
))
224 float amount
= std::min((pos
- end
) / sizeOfItem
, mouse_max_amount
);
225 m_analogScrollCount
+= amount
* amount
* mouse_scroll_speed
;
226 if (m_analogScrollCount
> 1)
228 ScrollToOffset(GetOffset() + 1);
229 m_analogScrollCount
= 0;
234 { // select the appropriate item
235 int cursor
= GetCursorFromPoint(point
);
238 // calling SelectItem() here will focus the item and scroll, which isn't really what we're after
244 void CGUIFixedListContainer::SelectItem(int item
)
246 // Check that GetOffset() is valid
248 // only select an item if it's in a valid range
249 if (item
>= 0 && item
< (int)m_items
.size())
251 // Select the item requested - we first set the cursor position
252 // which may be different at either end of the list, then the offset
253 int minCursor
, maxCursor
;
254 GetCursorRange(minCursor
, maxCursor
);
257 if ((int)m_items
.size() - 1 - item
<= maxCursor
- m_fixedCursor
)
258 cursor
= std::max(m_fixedCursor
, maxCursor
+ item
- (int)m_items
.size() + 1);
259 else if (item
<= m_fixedCursor
- minCursor
)
260 cursor
= std::min(m_fixedCursor
, minCursor
+ item
);
262 cursor
= m_fixedCursor
;
263 if (cursor
!= GetCursor())
264 SetContainerMoving(cursor
- GetCursor());
266 ScrollToOffset(item
- GetCursor());
271 bool CGUIFixedListContainer::HasPreviousPage() const
273 return (GetOffset() > 0);
276 bool CGUIFixedListContainer::HasNextPage() const
278 return (GetOffset() < (int)m_items
.size() - m_itemsPerPage
&& (int)m_items
.size() >= m_itemsPerPage
);
281 int CGUIFixedListContainer::GetCurrentPage() const
283 int offset
= CorrectOffset(GetOffset(), GetCursor());
284 if (offset
+ m_itemsPerPage
- GetCursor() >= (int)GetRows()) // last page
285 return (GetRows() + m_itemsPerPage
- 1) / m_itemsPerPage
;
286 return offset
/ m_itemsPerPage
+ 1;
289 void CGUIFixedListContainer::GetCursorRange(int &minCursor
, int &maxCursor
) const
291 minCursor
= std::max(m_fixedCursor
- m_cursorRange
, 0);
292 maxCursor
= std::min(m_fixedCursor
+ m_cursorRange
, m_itemsPerPage
);
296 minCursor
= m_fixedCursor
;
297 maxCursor
= m_fixedCursor
;
301 while (maxCursor
- minCursor
> (int)m_items
.size() - 1)
303 if (maxCursor
- m_fixedCursor
> m_fixedCursor
- minCursor
)