[WASAPI] fix stream types and frequencies enumeration
[xbmc.git] / xbmc / guilib / GUIControlGroupList.cpp
blobb0940ef5fe17547380990371bb561ec4066589b4
1 /*
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.
7 */
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"
21 using namespace KODI;
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)
27 m_itemGap = itemGap;
28 m_pageControl = pageControl;
29 m_focusedPosition = 0;
30 m_totalSize = 0;
31 m_orientation = orientation;
32 m_alignment = alignment;
33 m_lastScrollerValue = -1;
34 m_useControlPositions = useControlPositions;
35 ControlType = GUICONTROL_GROUPLIST;
36 m_minSize = 0;
39 CGUIControlGroupList::~CGUIControlGroupList(void) = default;
41 void CGUIControlGroupList::Process(unsigned int currentTime, CDirtyRegionList &dirtyregions)
43 if (m_scroller.Update(currentTime))
44 MarkDirtyRegion();
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
71 int index = 0;
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());
80 else
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;
90 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;
115 focusedPos = pos;
117 else
119 if (m_orientation == VERTICAL)
120 CServiceBroker::GetWinSystem()->GetGfxContext().SetOrigin(m_posX, m_posY + pos - m_scroller.GetValue());
121 else
122 CServiceBroker::GetWinSystem()->GetGfxContext().SetOrigin(m_posX + pos - m_scroller.GetValue(), m_posY);
123 control->DoRender();
125 if (control->IsVisible())
126 pos += Size(control) + m_itemGap;
127 CServiceBroker::GetWinSystem()->GetGfxContext().RestoreOrigin();
129 if (focusedControl)
131 if (m_orientation == VERTICAL)
132 CServiceBroker::GetWinSystem()->GetGfxContext().SetOrigin(m_posX, m_posY + focusedPos - m_scroller.GetValue());
133 else
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())
145 case ACTION_PAGE_UP:
146 ScrollPages(-1.f);
147 return true;
149 case ACTION_PAGE_DOWN:
150 ScrollPages(1.f);
151 return true;
153 case ACTION_FIRST_PAGE:
154 MoveTo(GetFirstFocusableControl(), 0.f);
155 return true;
157 case ACTION_LAST_PAGE:
158 MoveTo(GetLastFocusableControl(), m_totalSize - Size());
159 return true;
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
171 ValidateOffset();
172 float offset = 0;
173 for (iControls it = m_children.begin(); it != m_children.end(); ++it)
175 CGUIControl *control = *it;
176 if (!control->IsVisible())
177 continue;
178 if (control->GetControl(message.GetControlId()))
180 // find out whether this is the first or last control
181 if (control == GetFirstFocusableControl())
182 ScrollTo(0);
183 else if (control == GetLastFocusableControl())
184 ScrollTo(m_totalSize - Size());
185 else if (offset < m_scroller.GetValue())
186 ScrollTo(offset);
187 else if (offset + Size(control) > m_scroller.GetValue() + Size())
188 ScrollTo(offset + Size(control) - Size());
189 break;
191 offset += Size(control) + m_itemGap;
194 break;
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)
199 ValidateOffset();
200 // now check the focusControl's offset
201 float offset = 0;
202 for (iControls it = m_children.begin(); it != m_children.end(); ++it)
204 CGUIControl *control = *it;
205 if (!control->IsVisible())
206 continue;
207 if (control->GetControl(m_focusedControl))
209 if (IsControlOnScreen(offset, control))
210 return CGUIControlGroup::OnMessage(message);
211 break;
213 offset += Size(control) + m_itemGap;
215 // find the first control on this page
216 offset = 0;
217 for (iControls it = m_children.begin(); it != m_children.end(); ++it)
219 CGUIControl *control = *it;
220 if (!control->IsVisible())
221 continue;
222 if (control->CanFocus() && IsControlOnScreen(offset, control))
224 m_focusedControl = control->GetID();
225 break;
227 offset += Size(control) + m_itemGap;
230 break;
231 case GUI_MSG_PAGE_CHANGE:
233 if (message.GetSenderId() == m_pageControl)
234 { // it's from our page control
235 ScrollTo((float)message.GetParam1());
236 return true;
239 break;
241 return CGUIControlGroup::OnMessage(message);
244 void CGUIControlGroupList::ValidateOffset()
246 // calculate item gap. this needs to be done
247 // before fetching the total size
248 CalculateItemGap();
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
260 // if specified.
261 if (position < 0 || position > (int)m_children.size()) // add at the end
262 position = (int)m_children.size();
264 if (control)
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;
274 if (position == 0)
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());
292 else
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()));
306 else
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);
325 else
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()
343 m_totalSize = 0;
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;
368 else
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())
393 SetInvalid();
394 MarkDirtyRegion();
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())
405 float pos = 0;
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))
413 { // we're on screen
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);
417 if (ret)
418 { // we've handled the action, and/or have focused an item
419 return ret;
422 pos += Size(child) + m_itemGap;
425 // none of our children want the event, but we may want it.
426 EVENT_RESULT ret;
427 if (HitTest(childPoint) && (ret = OnMouseEvent(childPoint, event)))
428 return ret;
430 m_focusedControl = 0;
431 return EVENT_RESULT_UNHANDLED;
434 void CGUIControlGroupList::UnfocusFromPoint(const CPoint &point)
436 float pos = 0;
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))
446 { // we're on screen
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
458 switch (condition)
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);
466 default:
467 return false;
471 std::string CGUIControlGroupList::GetLabel(int info) const
473 switch (info)
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);
481 default:
482 break;
484 return "";
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
496 int index = 1;
497 for (const auto& child : m_children)
499 if (child->IsVisible() && child->CanFocus())
501 if (child->HasFocus())
502 return index;
503 index++;
506 return -1;
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)
518 int itemsCount = 0;
519 float itemsSize = 0;
520 for (const auto& child : m_children)
522 if (child->IsVisible())
524 itemsSize += Size(child);
525 itemsCount++;
529 if (itemsCount > 0)
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;
543 return 0.0f;
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
552 float offset = 0;
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
565 ScrollTo(offset);
566 return EVENT_RESULT_HANDLED;
568 offset = nextOffset;
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()));
586 SetInvalid();
587 return EVENT_RESULT_HANDLED;
590 return EVENT_RESULT_UNHANDLED;
593 float CGUIControlGroupList::GetTotalSize() const
595 float totalSize = 0;
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;
603 return totalSize;
606 float CGUIControlGroupList::GetControlOffset(const CGUIControl* control) const
608 bool found{false};
609 float offset{0.f};
610 for (CGUIControl* child : m_children)
612 if (child->IsVisible())
614 if (child == control)
616 found = true;
617 break;
619 offset += Size(child) + m_itemGap;
622 return (found ? offset : -1.f);
625 SGUIControlAndOffset CGUIControlGroupList::GetFocusableControlAt(float target, int direction) const
627 float offset{0.f};
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};
649 else
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)
664 ValidateOffset();
666 float currOffset = m_scroller.GetValue();
667 float newOffset = currOffset + pages * Size();
669 if (newOffset < 0.f)
670 newOffset = 0.f;
671 else if (newOffset > m_totalSize - Size())
672 newOffset = m_totalSize - Size();
674 CGUIControl* focusedControl = GetFocusedControl();
675 if (focusedControl)
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
700 ScrollTo(newOffset);
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);
715 ScrollTo(offset);
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())
724 return child;
726 return nullptr;
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())
735 return child;
737 return nullptr;