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 "GUIControlGroupList.h"
11 #include "GUIAction.h"
12 #include "GUIControlProfiler.h"
13 #include "GUIFont.h" // for XBFONT_* definitions
14 #include "GUIMessage.h"
15 #include "guilib/guiinfo/GUIInfoLabels.h"
16 #include "input/actions/Action.h"
17 #include "input/actions/ActionIDs.h"
18 #include "input/mouse/MouseEvent.h"
19 #include "utils/StringUtils.h"
23 CGUIControlGroupList::CGUIControlGroupList(int parentID
, int controlID
, float posX
, float posY
, float width
, float height
, float itemGap
, int pageControl
, ORIENTATION orientation
, bool useControlPositions
, uint32_t alignment
, const CScroller
& scroller
)
24 : CGUIControlGroup(parentID
, controlID
, posX
, posY
, width
, height
)
25 , m_scroller(scroller
)
28 m_pageControl
= pageControl
;
29 m_focusedPosition
= 0;
31 m_orientation
= orientation
;
32 m_alignment
= alignment
;
33 m_lastScrollerValue
= -1;
34 m_useControlPositions
= useControlPositions
;
35 ControlType
= GUICONTROL_GROUPLIST
;
39 CGUIControlGroupList::~CGUIControlGroupList(void) = default;
41 void CGUIControlGroupList::Process(unsigned int currentTime
, CDirtyRegionList
&dirtyregions
)
43 if (m_scroller
.Update(currentTime
))
46 // first we update visibility of all our items, to ensure our size and
47 // alignment computations are correct.
48 for (iControls it
= m_children
.begin(); it
!= m_children
.end(); ++it
)
50 CGUIControl
*control
= *it
;
51 GUIPROFILER_VISIBILITY_BEGIN(control
);
52 control
->UpdateVisibility(nullptr);
53 GUIPROFILER_VISIBILITY_END(control
);
56 // visibility status of some of the list items may have changed. Thus, the group list size
57 // may now be different and the scroller needs to be updated
58 int previousTotalSize
= m_totalSize
;
59 ValidateOffset(); // m_totalSize is updated here
60 bool sizeChanged
= previousTotalSize
!= m_totalSize
;
62 if (m_pageControl
&& (m_lastScrollerValue
!= m_scroller
.GetValue() || sizeChanged
))
64 CGUIMessage
message(GUI_MSG_LABEL_RESET
, GetParentID(), m_pageControl
, (int)Size(), (int)m_totalSize
);
65 SendWindowMessage(message
);
66 CGUIMessage
message2(GUI_MSG_ITEM_SELECT
, GetParentID(), m_pageControl
, (int)m_scroller
.GetValue());
67 SendWindowMessage(message2
);
68 m_lastScrollerValue
= static_cast<int>(m_scroller
.GetValue());
70 // we run through the controls, rendering as we go
72 float pos
= GetAlignOffset();
73 for (iControls it
= m_children
.begin(); it
!= m_children
.end(); ++it
)
75 // note we render all controls, even if they're offscreen, as then they'll be updated
76 // with respect to animations
77 CGUIControl
*control
= *it
;
78 if (m_orientation
== VERTICAL
)
79 CServiceBroker::GetWinSystem()->GetGfxContext().SetOrigin(m_posX
, m_posY
+ pos
- m_scroller
.GetValue());
81 CServiceBroker::GetWinSystem()->GetGfxContext().SetOrigin(m_posX
+ pos
- m_scroller
.GetValue(), m_posY
);
82 control
->DoProcess(currentTime
, dirtyregions
);
84 if (control
->IsVisible())
86 if (IsControlOnScreen(pos
, control
))
88 if (control
->HasFocus())
89 m_focusedPosition
= index
;
93 pos
+= Size(control
) + m_itemGap
;
95 CServiceBroker::GetWinSystem()->GetGfxContext().RestoreOrigin();
97 CGUIControl::Process(currentTime
, dirtyregions
);
100 void CGUIControlGroupList::Render()
102 // we run through the controls, rendering as we go
103 bool render(CServiceBroker::GetWinSystem()->GetGfxContext().SetClipRegion(m_posX
, m_posY
, m_width
, m_height
));
104 float pos
= GetAlignOffset();
105 float focusedPos
= 0;
106 CGUIControl
*focusedControl
= NULL
;
107 for (iControls it
= m_children
.begin(); it
!= m_children
.end(); ++it
)
109 // note we render all controls, even if they're offscreen, as then they'll be updated
110 // with respect to animations
111 CGUIControl
*control
= *it
;
112 if (m_renderFocusedLast
&& control
->HasFocus())
114 focusedControl
= control
;
119 if (m_orientation
== VERTICAL
)
120 CServiceBroker::GetWinSystem()->GetGfxContext().SetOrigin(m_posX
, m_posY
+ pos
- m_scroller
.GetValue());
122 CServiceBroker::GetWinSystem()->GetGfxContext().SetOrigin(m_posX
+ pos
- m_scroller
.GetValue(), m_posY
);
125 if (control
->IsVisible())
126 pos
+= Size(control
) + m_itemGap
;
127 CServiceBroker::GetWinSystem()->GetGfxContext().RestoreOrigin();
131 if (m_orientation
== VERTICAL
)
132 CServiceBroker::GetWinSystem()->GetGfxContext().SetOrigin(m_posX
, m_posY
+ focusedPos
- m_scroller
.GetValue());
134 CServiceBroker::GetWinSystem()->GetGfxContext().SetOrigin(m_posX
+ focusedPos
- m_scroller
.GetValue(), m_posY
);
135 focusedControl
->DoRender();
137 if (render
) CServiceBroker::GetWinSystem()->GetGfxContext().RestoreClipRegion();
138 CGUIControl::Render();
141 bool CGUIControlGroupList::OnAction(const CAction
& action
)
143 switch (action
.GetID())
149 case ACTION_PAGE_DOWN
:
153 case ACTION_FIRST_PAGE
:
154 MoveTo(GetFirstFocusableControl(), 0.f
);
157 case ACTION_LAST_PAGE
:
158 MoveTo(GetLastFocusableControl(), m_totalSize
- Size());
161 return CGUIControlGroup::OnAction(action
);
164 bool CGUIControlGroupList::OnMessage(CGUIMessage
& message
)
166 switch (message
.GetMessage() )
168 case GUI_MSG_FOCUSED
:
169 { // a control has been focused
170 // scroll if we need to and update our page control
173 for (iControls it
= m_children
.begin(); it
!= m_children
.end(); ++it
)
175 CGUIControl
*control
= *it
;
176 if (!control
->IsVisible())
178 if (control
->GetControl(message
.GetControlId()))
180 // find out whether this is the first or last control
181 if (control
== GetFirstFocusableControl())
183 else if (control
== GetLastFocusableControl())
184 ScrollTo(m_totalSize
- Size());
185 else if (offset
< m_scroller
.GetValue())
187 else if (offset
+ Size(control
) > m_scroller
.GetValue() + Size())
188 ScrollTo(offset
+ Size(control
) - Size());
191 offset
+= Size(control
) + m_itemGap
;
195 case GUI_MSG_SETFOCUS
:
197 // we've been asked to focus. We focus the last control if it's on this page,
198 // else we'll focus the first focusable control from our offset (after verifying it)
200 // now check the focusControl's offset
202 for (iControls it
= m_children
.begin(); it
!= m_children
.end(); ++it
)
204 CGUIControl
*control
= *it
;
205 if (!control
->IsVisible())
207 if (control
->GetControl(m_focusedControl
))
209 if (IsControlOnScreen(offset
, control
))
210 return CGUIControlGroup::OnMessage(message
);
213 offset
+= Size(control
) + m_itemGap
;
215 // find the first control on this page
217 for (iControls it
= m_children
.begin(); it
!= m_children
.end(); ++it
)
219 CGUIControl
*control
= *it
;
220 if (!control
->IsVisible())
222 if (control
->CanFocus() && IsControlOnScreen(offset
, control
))
224 m_focusedControl
= control
->GetID();
227 offset
+= Size(control
) + m_itemGap
;
231 case GUI_MSG_PAGE_CHANGE
:
233 if (message
.GetSenderId() == m_pageControl
)
234 { // it's from our page control
235 ScrollTo((float)message
.GetParam1());
241 return CGUIControlGroup::OnMessage(message
);
244 void CGUIControlGroupList::ValidateOffset()
246 // calculate item gap. this needs to be done
247 // before fetching the total size
249 // calculate how many items we have on this page
250 m_totalSize
= GetTotalSize();
251 // check our m_offset range
252 if (m_scroller
.GetValue() > m_totalSize
- Size())
253 m_scroller
.SetValue(m_totalSize
- Size());
254 if (m_scroller
.GetValue() < 0) m_scroller
.SetValue(0);
257 void CGUIControlGroupList::AddControl(CGUIControl
*control
, int position
/*= -1*/)
259 // NOTE: We override control navigation here, but we don't override the <onleft> etc. builtins
261 if (position
< 0 || position
> (int)m_children
.size()) // add at the end
262 position
= (int)m_children
.size();
265 { // set the navigation of items so that they form a list
266 CGUIAction beforeAction
= GetAction((m_orientation
== VERTICAL
) ? ACTION_MOVE_UP
: ACTION_MOVE_LEFT
);
267 CGUIAction afterAction
= GetAction((m_orientation
== VERTICAL
) ? ACTION_MOVE_DOWN
: ACTION_MOVE_RIGHT
);
268 if (m_children
.size())
270 // we're inserting at the given position, so grab the items above and below and alter
271 // their navigation accordingly
272 CGUIControl
*before
= NULL
;
273 CGUIControl
*after
= NULL
;
275 { // inserting at the beginning
276 after
= m_children
[0];
277 if (!afterAction
.HasActionsMeetingCondition() || afterAction
.GetNavigation() == GetID()) // we're wrapping around bottom->top, so we have to update the last item
278 before
= m_children
[m_children
.size() - 1];
279 if (!beforeAction
.HasActionsMeetingCondition() || beforeAction
.GetNavigation() == GetID()) // we're wrapping around top->bottom
280 beforeAction
= CGUIAction(m_children
[m_children
.size() - 1]->GetID());
281 afterAction
= CGUIAction(after
->GetID());
283 else if (position
== (int)m_children
.size())
284 { // inserting at the end
285 before
= m_children
[m_children
.size() - 1];
286 if (!beforeAction
.HasActionsMeetingCondition() || beforeAction
.GetNavigation() == GetID()) // we're wrapping around top->bottom, so we have to update the first item
287 after
= m_children
[0];
288 if (!afterAction
.HasActionsMeetingCondition() || afterAction
.GetNavigation() == GetID()) // we're wrapping around bottom->top
289 afterAction
= CGUIAction(m_children
[0]->GetID());
290 beforeAction
= CGUIAction(before
->GetID());
293 { // inserting somewhere in the middle
294 before
= m_children
[position
- 1];
295 after
= m_children
[position
];
296 beforeAction
= CGUIAction(before
->GetID());
297 afterAction
= CGUIAction(after
->GetID());
299 if (m_orientation
== VERTICAL
)
301 if (before
) // update the DOWN action to point to us
302 before
->SetAction(ACTION_MOVE_DOWN
, CGUIAction(control
->GetID()));
303 if (after
) // update the UP action to point to us
304 after
->SetAction(ACTION_MOVE_UP
, CGUIAction(control
->GetID()));
308 if (before
) // update the RIGHT action to point to us
309 before
->SetAction(ACTION_MOVE_RIGHT
, CGUIAction(control
->GetID()));
310 if (after
) // update the LEFT action to point to us
311 after
->SetAction(ACTION_MOVE_LEFT
, CGUIAction(control
->GetID()));
314 // now the control's nav
315 // set navigation path on orientation axis
316 // and try to apply other nav actions from grouplist
317 // don't override them if child have already defined actions
318 if (m_orientation
== VERTICAL
)
320 control
->SetAction(ACTION_MOVE_UP
, beforeAction
);
321 control
->SetAction(ACTION_MOVE_DOWN
, afterAction
);
322 control
->SetAction(ACTION_MOVE_LEFT
, GetAction(ACTION_MOVE_LEFT
), false);
323 control
->SetAction(ACTION_MOVE_RIGHT
, GetAction(ACTION_MOVE_RIGHT
), false);
327 control
->SetAction(ACTION_MOVE_LEFT
, beforeAction
);
328 control
->SetAction(ACTION_MOVE_RIGHT
, afterAction
);
329 control
->SetAction(ACTION_MOVE_UP
, GetAction(ACTION_MOVE_UP
), false);
330 control
->SetAction(ACTION_MOVE_DOWN
, GetAction(ACTION_MOVE_DOWN
), false);
332 control
->SetAction(ACTION_NAV_BACK
, GetAction(ACTION_NAV_BACK
), false);
334 if (!m_useControlPositions
)
335 control
->SetPosition(0,0);
336 CGUIControlGroup::AddControl(control
, position
);
337 m_totalSize
= GetTotalSize();
341 void CGUIControlGroupList::ClearAll()
344 CGUIControlGroup::ClearAll();
345 m_scroller
.SetValue(0);
348 #define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
350 float CGUIControlGroupList::GetWidth() const
352 if (m_orientation
== HORIZONTAL
)
353 return CLAMP(m_totalSize
, m_minSize
, m_width
);
354 return CGUIControlGroup::GetWidth();
357 float CGUIControlGroupList::GetHeight() const
359 if (m_orientation
== VERTICAL
)
360 return CLAMP(m_totalSize
, m_minSize
, m_height
);
361 return CGUIControlGroup::GetHeight();
364 void CGUIControlGroupList::SetMinSize(float minWidth
, float minHeight
)
366 if (m_orientation
== VERTICAL
)
367 m_minSize
= minHeight
;
369 m_minSize
= minWidth
;
372 float CGUIControlGroupList::Size(const CGUIControl
*control
) const
374 return (m_orientation
== VERTICAL
) ? control
->GetYPosition() + control
->GetHeight() : control
->GetXPosition() + control
->GetWidth();
377 inline float CGUIControlGroupList::Size() const
379 return (m_orientation
== VERTICAL
) ? m_height
: m_width
;
382 void CGUIControlGroupList::SetInvalid()
384 CGUIControl::SetInvalid();
385 // Force a message to the scrollbar
386 m_lastScrollerValue
= -1;
389 void CGUIControlGroupList::ScrollTo(float offset
)
391 m_scroller
.ScrollTo(offset
);
392 if (m_scroller
.IsScrolling())
397 EVENT_RESULT
CGUIControlGroupList::SendMouseEvent(const CPoint
& point
,
398 const MOUSE::CMouseEvent
& event
)
400 // transform our position into child coordinates
401 CPoint
childPoint(point
);
402 m_transform
.InverseTransformPosition(childPoint
.x
, childPoint
.y
);
403 if (CGUIControl::CanFocus())
406 float alignOffset
= GetAlignOffset();
407 for (ciControls i
= m_children
.begin(); i
!= m_children
.end(); ++i
)
409 CGUIControl
*child
= *i
;
410 if (child
->IsVisible())
412 if (IsControlOnScreen(pos
, child
))
414 float offsetX
= m_orientation
== VERTICAL
? m_posX
: m_posX
+ alignOffset
+ pos
- m_scroller
.GetValue();
415 float offsetY
= m_orientation
== VERTICAL
? m_posY
+ alignOffset
+ pos
- m_scroller
.GetValue() : m_posY
;
416 EVENT_RESULT ret
= child
->SendMouseEvent(childPoint
- CPoint(offsetX
, offsetY
), event
);
418 { // we've handled the action, and/or have focused an item
422 pos
+= Size(child
) + m_itemGap
;
425 // none of our children want the event, but we may want it.
427 if (HitTest(childPoint
) && (ret
= OnMouseEvent(childPoint
, event
)))
430 m_focusedControl
= 0;
431 return EVENT_RESULT_UNHANDLED
;
434 void CGUIControlGroupList::UnfocusFromPoint(const CPoint
&point
)
437 CPoint
controlCoords(point
);
438 m_transform
.InverseTransformPosition(controlCoords
.x
, controlCoords
.y
);
439 float alignOffset
= GetAlignOffset();
440 for (iControls it
= m_children
.begin(); it
!= m_children
.end(); ++it
)
442 CGUIControl
*child
= *it
;
443 if (child
->IsVisible())
445 if (IsControlOnScreen(pos
, child
))
447 CPoint offset
= (m_orientation
== VERTICAL
) ? CPoint(m_posX
, m_posY
+ alignOffset
+ pos
- m_scroller
.GetValue()) : CPoint(m_posX
+ alignOffset
+ pos
- m_scroller
.GetValue(), m_posY
);
448 child
->UnfocusFromPoint(controlCoords
- offset
);
450 pos
+= Size(child
) + m_itemGap
;
453 CGUIControl::UnfocusFromPoint(point
);
456 bool CGUIControlGroupList::GetCondition(int condition
, int data
) const
460 case CONTAINER_HAS_NEXT
:
461 return (m_totalSize
>= Size() && m_scroller
.GetValue() < m_totalSize
- Size());
462 case CONTAINER_HAS_PREVIOUS
:
463 return (m_scroller
.GetValue() > 0);
464 case CONTAINER_POSITION
:
465 return (m_focusedPosition
== data
);
471 std::string
CGUIControlGroupList::GetLabel(int info
) const
475 case CONTAINER_CURRENT_ITEM
:
476 return std::to_string(GetSelectedItem());
477 case CONTAINER_NUM_ITEMS
:
478 return std::to_string(GetNumItems());
479 case CONTAINER_POSITION
:
480 return std::to_string(m_focusedPosition
);
487 int CGUIControlGroupList::GetNumItems() const
489 return std::count_if(m_children
.begin(), m_children
.end(), [&](const CGUIControl
*child
) {
490 return (child
->IsVisible() && child
->CanFocus());
494 int CGUIControlGroupList::GetSelectedItem() const
497 for (const auto& child
: m_children
)
499 if (child
->IsVisible() && child
->CanFocus())
501 if (child
->HasFocus())
509 bool CGUIControlGroupList::IsControlOnScreen(float pos
, const CGUIControl
*control
) const
511 return (pos
>= m_scroller
.GetValue() && pos
+ Size(control
) <= m_scroller
.GetValue() + Size());
514 void CGUIControlGroupList::CalculateItemGap()
516 if (m_alignment
& XBFONT_JUSTIFIED
)
520 for (const auto& child
: m_children
)
522 if (child
->IsVisible())
524 itemsSize
+= Size(child
);
530 m_itemGap
= (Size() - itemsSize
) / itemsCount
;
534 float CGUIControlGroupList::GetAlignOffset() const
536 if (m_totalSize
< Size())
538 if (m_alignment
& XBFONT_RIGHT
)
539 return Size() - m_totalSize
;
540 if (m_alignment
& (XBFONT_CENTER_X
| XBFONT_JUSTIFIED
))
541 return (Size() - m_totalSize
)*0.5f
;
546 EVENT_RESULT
CGUIControlGroupList::OnMouseEvent(const CPoint
& point
,
547 const MOUSE::CMouseEvent
& event
)
549 if (event
.m_id
== ACTION_MOUSE_WHEEL_UP
|| event
.m_id
== ACTION_MOUSE_WHEEL_DOWN
)
551 // find the current control and move to the next or previous
553 for (ciControls it
= m_children
.begin(); it
!= m_children
.end(); ++it
)
555 CGUIControl
*control
= *it
;
556 if (!control
->IsVisible()) continue;
557 float nextOffset
= offset
+ Size(control
) + m_itemGap
;
558 if (event
.m_id
== ACTION_MOUSE_WHEEL_DOWN
&& nextOffset
> m_scroller
.GetValue() && m_scroller
.GetValue() < m_totalSize
- Size()) // past our current offset
560 ScrollTo(nextOffset
);
561 return EVENT_RESULT_HANDLED
;
563 else if (event
.m_id
== ACTION_MOUSE_WHEEL_UP
&& nextOffset
>= m_scroller
.GetValue() && m_scroller
.GetValue() > 0) // at least at our current offset
566 return EVENT_RESULT_HANDLED
;
571 else if (event
.m_id
== ACTION_GESTURE_BEGIN
)
572 { // grab exclusive access
573 CGUIMessage
msg(GUI_MSG_EXCLUSIVE_MOUSE
, GetID(), GetParentID());
574 SendWindowMessage(msg
);
575 return EVENT_RESULT_HANDLED
;
577 else if (event
.m_id
== ACTION_GESTURE_END
|| event
.m_id
== ACTION_GESTURE_ABORT
)
578 { // release exclusive access
579 CGUIMessage
msg(GUI_MSG_EXCLUSIVE_MOUSE
, 0, GetParentID());
580 SendWindowMessage(msg
);
581 return EVENT_RESULT_HANDLED
;
583 else if (event
.m_id
== ACTION_GESTURE_PAN
)
584 { // do the drag and validate our offset (corrects for end of scroll)
585 m_scroller
.SetValue(CLAMP(m_scroller
.GetValue() - ((m_orientation
== HORIZONTAL
) ? event
.m_offsetX
: event
.m_offsetY
), 0, m_totalSize
- Size()));
587 return EVENT_RESULT_HANDLED
;
590 return EVENT_RESULT_UNHANDLED
;
593 float CGUIControlGroupList::GetTotalSize() const
596 for (ciControls it
= m_children
.begin(); it
!= m_children
.end(); ++it
)
598 CGUIControl
*control
= *it
;
599 if (!control
->IsVisible()) continue;
600 totalSize
+= Size(control
) + m_itemGap
;
602 if (totalSize
> 0) totalSize
-= m_itemGap
;
606 float CGUIControlGroupList::GetControlOffset(const CGUIControl
* control
) const
610 for (CGUIControl
* child
: m_children
)
612 if (child
->IsVisible())
614 if (child
== control
)
619 offset
+= Size(child
) + m_itemGap
;
622 return (found
? offset
: -1.f
);
625 SGUIControlAndOffset
CGUIControlGroupList::GetFocusableControlAt(float target
, int direction
) const
628 CGUIControl
* lastFocusable
{nullptr};
629 float lastFocusableOffset
{0.f
};
631 for (CGUIControl
* child
: m_children
)
633 if (child
->IsVisible())
635 if (child
->CanFocus())
637 // The target is at the beginning or inside a focusable control > perfect match
638 if (offset
<= target
&& offset
+ Size(child
) > target
)
639 return SGUIControlAndOffset
{child
, offset
};
640 // The control at the target position was not focusable
641 // or the target was before the first focusable control.
642 // Since the children are always iterated from first to last and their positions increase,
643 // this control is the next best match when moving down (position increasing).
644 // When moving up, the next best match is the focusable control before this one.
645 else if (offset
>= target
)
647 if (direction
> 0.f
|| !lastFocusable
)
648 return SGUIControlAndOffset
{child
, offset
};
650 return SGUIControlAndOffset
{lastFocusable
, lastFocusableOffset
};
652 lastFocusable
= child
;
653 lastFocusableOffset
= offset
;
655 offset
+= Size(child
) + m_itemGap
;
658 // No visible focusable controls or the target is beyond the last focusable control
659 return SGUIControlAndOffset
{lastFocusable
, lastFocusableOffset
};
662 void CGUIControlGroupList::ScrollPages(float pages
)
666 float currOffset
= m_scroller
.GetValue();
667 float newOffset
= currOffset
+ pages
* Size();
671 else if (newOffset
> m_totalSize
- Size())
672 newOffset
= m_totalSize
- Size();
674 CGUIControl
* focusedControl
= GetFocusedControl();
677 // Using the middle of the focused control as origin helps deal with the alignment thrown off
678 // by controls of different sizes. The expected target control can be missed by a few pixels otherwise.
679 float origin
= GetControlOffset(focusedControl
) + Size(focusedControl
) / 2;
680 SGUIControlAndOffset newFocusedControl
= GetFocusableControlAt(origin
+ pages
* Size(), pages
);
682 if (newFocusedControl
.control
)
684 CGUIMessage
message(GUI_MSG_LOSTFOCUS
, GetID(), focusedControl
->GetID(),
685 newFocusedControl
.control
->GetID());
686 focusedControl
->OnMessage(message
);
688 CGUIMessage
message2(GUI_MSG_SETFOCUS
, GetID(), newFocusedControl
.control
->GetID());
689 newFocusedControl
.control
->OnMessage(message2
);
691 // Adjust the new view offset so that the new focused control is fully visible
692 if (newOffset
> newFocusedControl
.offset
)
693 newOffset
= newFocusedControl
.offset
;
694 else if (newOffset
+ Size() < newFocusedControl
.offset
+ Size(newFocusedControl
.control
))
695 newOffset
= newFocusedControl
.offset
+ Size(newFocusedControl
.control
) - Size();
698 // The GUI_MSG_SETFOCUS message only makes the selection visible
699 // Restore the relative position of the selection in the view
703 void CGUIControlGroupList::MoveTo(CGUIControl
* control
, float offset
)
705 CGUIControl
* focusedControl
= GetFocusedControl();
707 if (focusedControl
&& control
)
709 CGUIMessage
message(GUI_MSG_LOSTFOCUS
, GetID(), focusedControl
->GetID(), control
->GetID());
710 focusedControl
->OnMessage(message
);
712 CGUIMessage
message2(GUI_MSG_SETFOCUS
, GetID(), control
->GetID());
713 control
->OnMessage(message2
);
718 CGUIControl
* CGUIControlGroupList::GetFirstFocusableControl() const
720 for (ciControls it
= m_children
.begin(); it
!= m_children
.end(); ++it
)
722 CGUIControl
* child
= *it
;
723 if (child
->CanFocus())
729 CGUIControl
* CGUIControlGroupList::GetLastFocusableControl() const
731 for (crControls it
= m_children
.rbegin(); it
!= m_children
.rend(); ++it
)
733 CGUIControl
* child
= *it
;
734 if (child
->CanFocus())