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 "GUIControlGroup.h"
11 #include "GUIMessage.h"
12 #include "input/mouse/MouseEvent.h"
19 CGUIControlGroup::CGUIControlGroup()
22 m_defaultAlways
= false;
24 m_renderFocusedLast
= false;
25 ControlType
= GUICONTROL_GROUP
;
28 CGUIControlGroup::CGUIControlGroup(int parentID
, int controlID
, float posX
, float posY
, float width
, float height
)
29 : CGUIControlLookup(parentID
, controlID
, posX
, posY
, width
, height
)
32 m_defaultAlways
= false;
34 m_renderFocusedLast
= false;
35 ControlType
= GUICONTROL_GROUP
;
38 CGUIControlGroup::CGUIControlGroup(const CGUIControlGroup
&from
)
39 : CGUIControlLookup(from
)
41 m_defaultControl
= from
.m_defaultControl
;
42 m_defaultAlways
= from
.m_defaultAlways
;
43 m_renderFocusedLast
= from
.m_renderFocusedLast
;
45 // run through and add our controls
46 for (auto *i
: from
.m_children
)
47 AddControl(i
->Clone());
51 ControlType
= GUICONTROL_GROUP
;
54 CGUIControlGroup::~CGUIControlGroup(void)
59 void CGUIControlGroup::AllocResources()
61 CGUIControl::AllocResources();
62 for (auto *control
: m_children
)
64 if (!control
->IsDynamicallyAllocated())
65 control
->AllocResources();
69 void CGUIControlGroup::FreeResources(bool immediately
)
71 CGUIControl::FreeResources(immediately
);
72 for (auto *control
: m_children
)
74 control
->FreeResources(immediately
);
78 void CGUIControlGroup::DynamicResourceAlloc(bool bOnOff
)
80 for (auto *control
: m_children
)
82 control
->DynamicResourceAlloc(bOnOff
);
86 void CGUIControlGroup::Process(unsigned int currentTime
, CDirtyRegionList
&dirtyregions
)
88 CPoint
pos(GetPosition());
89 CServiceBroker::GetWinSystem()->GetGfxContext().SetOrigin(pos
.x
, pos
.y
);
92 for (auto *control
: m_children
)
94 control
->UpdateVisibility(nullptr);
95 unsigned int oldDirty
= dirtyregions
.size();
96 control
->DoProcess(currentTime
, dirtyregions
);
97 if (control
->IsVisible() || (oldDirty
!= dirtyregions
.size())) // visible or dirty (was visible?)
98 rect
.Union(control
->GetRenderRegion());
101 CServiceBroker::GetWinSystem()->GetGfxContext().RestoreOrigin();
102 CGUIControl::Process(currentTime
, dirtyregions
);
103 m_renderRegion
= rect
;
106 void CGUIControlGroup::Render()
108 CPoint
pos(GetPosition());
109 CServiceBroker::GetWinSystem()->GetGfxContext().SetOrigin(pos
.x
, pos
.y
);
110 CGUIControl
*focusedControl
= NULL
;
111 if (CServiceBroker::GetWinSystem()->GetGfxContext().GetRenderOrder() ==
112 RENDER_ORDER_FRONT_TO_BACK
)
114 for (auto it
= m_children
.rbegin(); it
!= m_children
.rend(); ++it
)
116 if (m_renderFocusedLast
&& (*it
)->HasFocus())
117 focusedControl
= (*it
);
124 for (auto* control
: m_children
)
126 if (m_renderFocusedLast
&& control
->HasFocus())
127 focusedControl
= control
;
133 focusedControl
->DoRender();
134 CGUIControl::Render();
135 CServiceBroker::GetWinSystem()->GetGfxContext().RestoreOrigin();
138 void CGUIControlGroup::RenderEx()
140 for (auto *control
: m_children
)
142 CGUIControl::RenderEx();
145 bool CGUIControlGroup::OnAction(const CAction
&action
)
150 bool CGUIControlGroup::HasFocus() const
152 for (auto *control
: m_children
)
154 if (control
->HasFocus())
160 bool CGUIControlGroup::OnMessage(CGUIMessage
& message
)
162 switch (message
.GetMessage() )
164 case GUI_MSG_ITEM_SELECT
:
166 if (message
.GetControlId() == GetID())
168 m_focusedControl
= message
.GetParam1();
173 case GUI_MSG_ITEM_SELECTED
:
175 if (message
.GetControlId() == GetID())
177 message
.SetParam1(m_focusedControl
);
182 case GUI_MSG_FOCUSED
:
183 { // a control has been focused
184 m_focusedControl
= message
.GetControlId();
186 // tell our parent thatwe have focus
188 m_parentControl
->OnMessage(message
);
191 case GUI_MSG_SETFOCUS
:
193 // first try our last focused control...
194 if (!m_defaultAlways
&& m_focusedControl
)
196 CGUIControl
*control
= GetFirstFocusableControl(m_focusedControl
);
199 CGUIMessage
msg(GUI_MSG_SETFOCUS
, GetParentID(), control
->GetID());
200 return control
->OnMessage(msg
);
203 // ok, no previously focused control, try the default control first
204 if (m_defaultControl
)
206 CGUIControl
*control
= GetFirstFocusableControl(m_defaultControl
);
209 CGUIMessage
msg(GUI_MSG_SETFOCUS
, GetParentID(), control
->GetID());
210 return control
->OnMessage(msg
);
213 // no success with the default control, so just find one to focus
214 CGUIControl
*control
= GetFirstFocusableControl(0);
217 CGUIMessage
msg(GUI_MSG_SETFOCUS
, GetParentID(), control
->GetID());
218 return control
->OnMessage(msg
);
224 case GUI_MSG_LOSTFOCUS
:
226 // set all subcontrols unfocused
227 for (auto *control
: m_children
)
228 control
->SetFocus(false);
229 if (!GetControl(message
.GetParam1()))
230 { // we don't have the new id, so unfocus
233 m_parentControl
->OnMessage(message
);
238 case GUI_MSG_PAGE_CHANGE
:
239 case GUI_MSG_REFRESH_THUMBS
:
240 case GUI_MSG_REFRESH_LIST
:
241 case GUI_MSG_WINDOW_RESIZE
:
242 { // send to all child controls (make sure the target is the control id)
243 for (auto *control
: m_children
)
245 CGUIMessage
msg(message
.GetMessage(), message
.GetSenderId(), control
->GetID(), message
.GetParam1());
246 control
->OnMessage(msg
);
251 case GUI_MSG_REFRESH_TIMER
:
252 if (!IsVisible() || !IsVisibleFromSkin())
257 //not intended for any specific control, send to all childs and our base handler.
258 if (message
.GetControlId() == 0)
260 for (auto *control
: m_children
)
262 handled
|= control
->OnMessage(message
);
264 return CGUIControl::OnMessage(message
) || handled
;
266 // if it's intended for us, then so be it
267 if (message
.GetControlId() == GetID())
268 return CGUIControl::OnMessage(message
);
270 return SendControlMessage(message
);
273 bool CGUIControlGroup::SendControlMessage(CGUIMessage
&message
)
275 IDCollector
collector(m_idCollector
);
277 CGUIControl
*ctrl(GetControl(message
.GetControlId(), collector
.m_collector
));
278 // see if a child matches, and send to the child control if so
279 if (ctrl
&& ctrl
->OnMessage(message
))
282 // Unhandled - send to all matching invisible controls as well
284 for (auto *control
: *collector
.m_collector
)
285 if (control
->OnMessage(message
))
291 bool CGUIControlGroup::CanFocus() const
293 if (!CGUIControl::CanFocus()) return false;
294 // see if we have any children that can be focused
295 for (auto *control
: m_children
)
297 if (control
->CanFocus())
303 void CGUIControlGroup::AssignDepth()
305 CGUIControl
* focusedControl
= nullptr;
306 if (m_children
.size())
308 for (auto* control
: m_children
)
310 if (m_renderFocusedLast
&& control
->HasFocus())
311 focusedControl
= control
;
313 control
->AssignDepth();
317 focusedControl
->AssignDepth();
320 void CGUIControlGroup::SetInitialVisibility()
322 CGUIControl::SetInitialVisibility();
323 for (auto *control
: m_children
)
324 control
->SetInitialVisibility();
327 void CGUIControlGroup::QueueAnimation(ANIMATION_TYPE animType
)
329 CGUIControl::QueueAnimation(animType
);
330 // send window level animations to our children as well
331 if (animType
== ANIM_TYPE_WINDOW_OPEN
|| animType
== ANIM_TYPE_WINDOW_CLOSE
)
333 for (auto *control
: m_children
)
334 control
->QueueAnimation(animType
);
338 void CGUIControlGroup::ResetAnimation(ANIMATION_TYPE animType
)
340 CGUIControl::ResetAnimation(animType
);
341 // send window level animations to our children as well
342 if (animType
== ANIM_TYPE_WINDOW_OPEN
|| animType
== ANIM_TYPE_WINDOW_CLOSE
)
344 for (auto *control
: m_children
)
345 control
->ResetAnimation(animType
);
349 void CGUIControlGroup::ResetAnimations()
350 { // resets all animations, regardless of condition
351 CGUIControl::ResetAnimations();
352 for (auto *control
: m_children
)
353 control
->ResetAnimations();
356 bool CGUIControlGroup::IsAnimating(ANIMATION_TYPE animType
)
358 if (CGUIControl::IsAnimating(animType
))
363 for (auto *control
: m_children
)
365 if (control
->IsAnimating(animType
))
372 bool CGUIControlGroup::HasAnimation(ANIMATION_TYPE animType
)
374 if (CGUIControl::HasAnimation(animType
))
379 for (auto *control
: m_children
)
381 if (control
->HasAnimation(animType
))
388 EVENT_RESULT
CGUIControlGroup::SendMouseEvent(const CPoint
& point
, const MOUSE::CMouseEvent
& event
)
390 // transform our position into child coordinates
391 CPoint
childPoint(point
);
392 m_transform
.InverseTransformPosition(childPoint
.x
, childPoint
.y
);
394 if (CGUIControl::CanFocus())
396 CPoint
pos(GetPosition());
397 // run through our controls in reverse order (so that last rendered is checked first)
398 for (rControls i
= m_children
.rbegin(); i
!= m_children
.rend(); ++i
)
400 CGUIControl
*child
= *i
;
401 EVENT_RESULT ret
= child
->SendMouseEvent(childPoint
- pos
, event
);
403 { // we've handled the action, and/or have focused an item
407 // none of our children want the event, but we may want it.
409 if (HitTest(childPoint
) && (ret
= OnMouseEvent(childPoint
, event
)))
412 m_focusedControl
= 0;
413 return EVENT_RESULT_UNHANDLED
;
416 void CGUIControlGroup::UnfocusFromPoint(const CPoint
&point
)
418 CPoint
controlCoords(point
);
419 m_transform
.InverseTransformPosition(controlCoords
.x
, controlCoords
.y
);
420 controlCoords
-= GetPosition();
421 for (auto *child
: m_children
)
423 child
->UnfocusFromPoint(controlCoords
);
425 CGUIControl::UnfocusFromPoint(point
);
428 int CGUIControlGroup::GetFocusedControlID() const
430 if (m_focusedControl
) return m_focusedControl
;
431 CGUIControl
*control
= GetFocusedControl();
432 if (control
) return control
->GetID();
436 CGUIControl
*CGUIControlGroup::GetFocusedControl() const
439 if (m_focusedControl
)
441 // we may have multiple controls with same id - we pick first that has focus
442 std::pair
<LookupMap::const_iterator
, LookupMap::const_iterator
> range
= GetLookupControls(m_focusedControl
);
444 for (LookupMap::const_iterator i
= range
.first
; i
!= range
.second
; ++i
)
446 if (i
->second
->HasFocus())
451 // if lookup didn't find focused control, iterate m_children to find it
452 for (auto *control
: m_children
)
454 // Avoid calling HasFocus() on control group as it will (possibly) recursively
455 // traverse entire group tree just to check if there is focused control.
456 // We are recursively traversing it here so no point in doing it twice.
457 CGUIControlGroup
*groupControl(dynamic_cast<CGUIControlGroup
*>(control
));
460 CGUIControl
* focusedControl
= groupControl
->GetFocusedControl();
462 return focusedControl
;
464 else if (control
->HasFocus())
470 // in the case of id == 0, we don't match id
471 CGUIControl
*CGUIControlGroup::GetFirstFocusableControl(int id
)
473 if (!CanFocus()) return NULL
;
474 if (id
&& id
== GetID()) return this; // we're focusable and they want us
475 for (auto *pControl
: m_children
)
477 CGUIControlGroup
*group(dynamic_cast<CGUIControlGroup
*>(pControl
));
480 CGUIControl
*control
= group
->GetFirstFocusableControl(id
);
481 if (control
) return control
;
483 if ((!id
|| pControl
->GetID() == id
) && pControl
->CanFocus())
489 void CGUIControlGroup::AddControl(CGUIControl
*control
, int position
/* = -1*/)
491 if (!control
) return;
492 if (position
< 0 || position
> (int)m_children
.size())
493 position
= (int)m_children
.size();
494 m_children
.insert(m_children
.begin() + position
, control
);
495 control
->SetParentControl(this);
496 control
->SetControlStats(m_controlStats
);
497 control
->SetPushUpdates(m_pushedUpdates
);
502 bool CGUIControlGroup::InsertControl(CGUIControl
*control
, const CGUIControl
*insertPoint
)
505 for (unsigned int i
= 0; i
< m_children
.size(); i
++)
507 CGUIControl
*child
= m_children
[i
];
508 CGUIControlGroup
*group(dynamic_cast<CGUIControlGroup
*>(child
));
509 if (group
&& group
->InsertControl(control
, insertPoint
))
511 else if (child
== insertPoint
)
513 AddControl(control
, i
);
520 void CGUIControlGroup::SaveStates(std::vector
<CControlState
> &states
)
522 // save our state, and that of our children
523 states
.emplace_back(GetID(), m_focusedControl
);
524 for (auto *control
: m_children
)
525 control
->SaveStates(states
);
528 // Note: This routine doesn't delete the control. It just removes it from the control list
529 bool CGUIControlGroup::RemoveControl(const CGUIControl
*control
)
531 for (iControls it
= m_children
.begin(); it
!= m_children
.end(); ++it
)
533 CGUIControl
*child
= *it
;
534 CGUIControlGroup
*group(dynamic_cast<CGUIControlGroup
*>(child
));
535 if (group
&& group
->RemoveControl(control
))
537 if (control
== child
)
539 m_children
.erase(it
);
548 void CGUIControlGroup::ClearAll()
550 // first remove from the lookup table
553 // and delete all our children
554 for (auto *control
: m_children
)
558 m_focusedControl
= 0;
565 void CGUIControlGroup::DumpTextureUse()
567 for (auto *control
: m_children
)
568 control
->DumpTextureUse();