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 "GUIListContainer.h"
11 #include "GUIListItemLayout.h"
12 #include "GUIMessage.h"
13 #include "input/actions/Action.h"
14 #include "input/actions/ActionIDs.h"
15 #include "utils/StringUtils.h"
17 CGUIListContainer::CGUIListContainer(int parentID
, int controlID
, float posX
, float posY
, float width
, float height
, ORIENTATION orientation
, const CScroller
& scroller
, int preloadItems
)
18 : CGUIBaseContainer(parentID
, controlID
, posX
, posY
, width
, height
, orientation
, scroller
, preloadItems
)
20 ControlType
= GUICONTAINER_LIST
;
21 m_type
= VIEW_TYPE_LIST
;
24 CGUIListContainer::CGUIListContainer(const CGUIListContainer
& other
) : CGUIBaseContainer(other
)
28 CGUIListContainer::~CGUIListContainer(void) = default;
30 bool CGUIListContainer::OnAction(const CAction
&action
)
32 switch (action
.GetID())
37 { // already on the first page, so move to the first item
41 { // scroll up to the previous page
42 Scroll( -m_itemsPerPage
);
47 case ACTION_PAGE_DOWN
:
49 if (GetOffset() == (int)m_items
.size() - m_itemsPerPage
|| (int)m_items
.size() < m_itemsPerPage
)
50 { // already at the last page, so move to the last item.
51 SetCursor(m_items
.size() - GetOffset() - 1);
54 { // scroll down to the next page
55 Scroll(m_itemsPerPage
);
60 // smooth scrolling (for analog controls)
61 case ACTION_SCROLL_UP
:
63 m_analogScrollCount
+= action
.GetAmount() * action
.GetAmount();
65 while (m_analogScrollCount
> 0.4f
)
68 m_analogScrollCount
-= 0.4f
;
69 if (GetOffset() > 0 && GetCursor() <= m_itemsPerPage
/ 2)
73 else if (GetCursor() > 0)
75 SetCursor(GetCursor() - 1);
81 case ACTION_SCROLL_DOWN
:
83 m_analogScrollCount
+= action
.GetAmount() * action
.GetAmount();
85 while (m_analogScrollCount
> 0.4f
)
88 m_analogScrollCount
-= 0.4f
;
89 if (GetOffset() + m_itemsPerPage
< (int)m_items
.size() && GetCursor() >= m_itemsPerPage
/ 2)
93 else if (GetCursor() < m_itemsPerPage
- 1 && GetOffset() + GetCursor() < (int)m_items
.size() - 1)
95 SetCursor(GetCursor() + 1);
102 return CGUIBaseContainer::OnAction(action
);
105 bool CGUIListContainer::OnMessage(CGUIMessage
& message
)
107 if (message
.GetControlId() == GetID() )
109 if (message
.GetMessage() == GUI_MSG_LABEL_RESET
)
113 m_scroller
.SetValue(0);
116 return CGUIBaseContainer::OnMessage(message
);
119 bool CGUIListContainer::MoveUp(bool wrapAround
)
123 SetCursor(GetCursor() - 1);
125 else if (GetCursor() == 0 && GetOffset())
127 ScrollToOffset(GetOffset() - 1);
131 if (!m_items
.empty())
132 { // move 2 last item in list, and set our container moving up
133 int offset
= m_items
.size() - m_itemsPerPage
;
134 if (offset
< 0) offset
= 0;
135 SetCursor(m_items
.size() - offset
- 1);
136 ScrollToOffset(offset
);
137 SetContainerMoving(-1);
145 bool CGUIListContainer::MoveDown(bool wrapAround
)
147 if (GetOffset() + GetCursor() + 1 < (int)m_items
.size())
149 if (GetCursor() + 1 < m_itemsPerPage
)
151 SetCursor(GetCursor() + 1);
155 ScrollToOffset(GetOffset() + 1);
159 { // move first item in list, and set our container moving in the "down" direction
162 SetContainerMoving(1);
169 // scrolls the said amount
170 void CGUIListContainer::Scroll(int amount
)
172 // increase or decrease the offset
173 int offset
= GetOffset() + amount
;
174 if (offset
> (int)m_items
.size() - m_itemsPerPage
)
176 offset
= m_items
.size() - m_itemsPerPage
;
178 if (offset
< 0) offset
= 0;
179 ScrollToOffset(offset
);
182 void CGUIListContainer::ValidateOffset()
184 if (!m_layout
) return;
185 // first thing is we check the range of our offset
186 // don't validate offset if we are scrolling in case the tween image exceed <0, 1> range
187 int minOffset
, maxOffset
;
188 GetOffsetRange(minOffset
, maxOffset
);
189 if (GetOffset() > maxOffset
|| (!m_scroller
.IsScrolling() && m_scroller
.GetValue() > maxOffset
* m_layout
->Size(m_orientation
)))
191 SetOffset(std::max(0, maxOffset
));
192 m_scroller
.SetValue(GetOffset() * m_layout
->Size(m_orientation
));
194 if (GetOffset() < 0 || (!m_scroller
.IsScrolling() && m_scroller
.GetValue() < 0))
197 m_scroller
.SetValue(0);
201 void CGUIListContainer::SetCursor(int cursor
)
203 if (cursor
> m_itemsPerPage
- 1) cursor
= m_itemsPerPage
- 1;
204 if (cursor
< 0) cursor
= 0;
206 SetContainerMoving(cursor
- GetCursor());
207 CGUIBaseContainer::SetCursor(cursor
);
210 void CGUIListContainer::SelectItem(int item
)
212 // Check that our offset is valid
214 // only select an item if it's in a valid range
215 if (item
>= 0 && item
< (int)m_items
.size())
217 // Select the item requested
218 if (item
>= GetOffset() && item
< GetOffset() + m_itemsPerPage
)
219 { // the item is on the current page, so don't change it.
220 SetCursor(item
- GetOffset());
222 else if (item
< GetOffset())
223 { // item is on a previous page - make it the first item on the page
225 ScrollToOffset(item
);
227 else // (item >= GetOffset()+m_itemsPerPage)
228 { // item is on a later page - make it the last item on the page
229 SetCursor(m_itemsPerPage
- 1);
230 ScrollToOffset(item
- GetCursor());
235 int CGUIListContainer::GetCursorFromPoint(const CPoint
&point
, CPoint
*itemPoint
) const
237 if (!m_focusedLayout
|| !m_layout
)
241 float pos
= (m_orientation
== VERTICAL
) ? point
.y
: point
.x
;
242 while (row
< m_itemsPerPage
+ 1) // 1 more to ensure we get the (possible) half item at the end.
244 const CGUIListItemLayout
*layout
= (row
== GetCursor()) ? m_focusedLayout
: m_layout
;
245 if (pos
< layout
->Size(m_orientation
) && row
+ GetOffset() < (int)m_items
.size())
246 { // found correct "row" -> check horizontal
247 if (!InsideLayout(layout
, point
))
251 *itemPoint
= m_orientation
== VERTICAL
? CPoint(point
.x
, pos
) : CPoint(pos
, point
.y
);
255 pos
-= layout
->Size(m_orientation
);
260 bool CGUIListContainer::SelectItemFromPoint(const CPoint
&point
)
263 int row
= GetCursorFromPoint(point
, &itemPoint
);
268 CGUIListItemLayout
*focusedLayout
= GetFocusedLayout();
270 focusedLayout
->SelectItemFromPoint(itemPoint
);
274 //#ifdef GUILIB_PYTHON_COMPATIBILITY
275 CGUIListContainer::CGUIListContainer(int parentID
, int controlID
, float posX
, float posY
, float width
, float height
,
276 const CLabelInfo
& labelInfo
, const CLabelInfo
& labelInfo2
,
277 const CTextureInfo
& textureButton
, const CTextureInfo
& textureButtonFocus
,
278 float textureHeight
, float itemWidth
, float itemHeight
, float spaceBetweenItems
)
279 : CGUIBaseContainer(parentID
, controlID
, posX
, posY
, width
, height
, VERTICAL
, 200, 0)
281 m_layouts
.emplace_back();
282 m_layouts
.back().CreateListControlLayouts(width
, textureHeight
+ spaceBetweenItems
, false, labelInfo
, labelInfo2
, textureButton
, textureButtonFocus
, textureHeight
, itemWidth
, itemHeight
, "", "");
283 std::string condition
= StringUtils::Format("control.hasfocus({})", controlID
);
284 std::string condition2
= "!" + condition
;
285 m_focusedLayouts
.emplace_back();
286 m_focusedLayouts
.back().CreateListControlLayouts(width
, textureHeight
+ spaceBetweenItems
, true, labelInfo
, labelInfo2
, textureButton
, textureButtonFocus
, textureHeight
, itemWidth
, itemHeight
, condition2
, condition
);
287 m_height
= floor(m_height
/ (textureHeight
+ spaceBetweenItems
)) * (textureHeight
+ spaceBetweenItems
);
288 ControlType
= GUICONTAINER_LIST
;
292 bool CGUIListContainer::HasNextPage() const
294 return (GetOffset() != (int)m_items
.size() - m_itemsPerPage
&& (int)m_items
.size() >= m_itemsPerPage
);
297 bool CGUIListContainer::HasPreviousPage() const
299 return (GetOffset() > 0);