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"
16 CGUIControlGroup::CGUIControlGroup()
19 m_defaultAlways
= false;
21 m_renderFocusedLast
= false;
22 ControlType
= GUICONTROL_GROUP
;
25 CGUIControlGroup::CGUIControlGroup(int parentID
, int controlID
, float posX
, float posY
, float width
, float height
)
26 : CGUIControlLookup(parentID
, controlID
, posX
, posY
, width
, height
)
29 m_defaultAlways
= false;
31 m_renderFocusedLast
= false;
32 ControlType
= GUICONTROL_GROUP
;
35 CGUIControlGroup::CGUIControlGroup(const CGUIControlGroup
&from
)
36 : CGUIControlLookup(from
)
38 m_defaultControl
= from
.m_defaultControl
;
39 m_defaultAlways
= from
.m_defaultAlways
;
40 m_renderFocusedLast
= from
.m_renderFocusedLast
;
42 // run through and add our controls
43 for (auto *i
: from
.m_children
)
44 AddControl(i
->Clone());
48 ControlType
= GUICONTROL_GROUP
;
51 CGUIControlGroup::~CGUIControlGroup(void)
56 void CGUIControlGroup::AllocResources()
58 CGUIControl::AllocResources();
59 for (auto *control
: m_children
)
61 if (!control
->IsDynamicallyAllocated())
62 control
->AllocResources();
66 void CGUIControlGroup::FreeResources(bool immediately
)
68 CGUIControl::FreeResources(immediately
);
69 for (auto *control
: m_children
)
71 control
->FreeResources(immediately
);
75 void CGUIControlGroup::DynamicResourceAlloc(bool bOnOff
)
77 for (auto *control
: m_children
)
79 control
->DynamicResourceAlloc(bOnOff
);
83 void CGUIControlGroup::Process(unsigned int currentTime
, CDirtyRegionList
&dirtyregions
)
85 CPoint
pos(GetPosition());
86 CServiceBroker::GetWinSystem()->GetGfxContext().SetOrigin(pos
.x
, pos
.y
);
89 for (auto *control
: m_children
)
91 control
->UpdateVisibility(nullptr);
92 unsigned int oldDirty
= dirtyregions
.size();
93 control
->DoProcess(currentTime
, dirtyregions
);
94 if (control
->IsVisible() || (oldDirty
!= dirtyregions
.size())) // visible or dirty (was visible?)
95 rect
.Union(control
->GetRenderRegion());
98 CServiceBroker::GetWinSystem()->GetGfxContext().RestoreOrigin();
99 CGUIControl::Process(currentTime
, dirtyregions
);
100 m_renderRegion
= rect
;
103 void CGUIControlGroup::Render()
105 CPoint
pos(GetPosition());
106 CServiceBroker::GetWinSystem()->GetGfxContext().SetOrigin(pos
.x
, pos
.y
);
107 CGUIControl
*focusedControl
= NULL
;
108 for (auto *control
: m_children
)
110 if (m_renderFocusedLast
&& control
->HasFocus())
111 focusedControl
= control
;
116 focusedControl
->DoRender();
117 CGUIControl::Render();
118 CServiceBroker::GetWinSystem()->GetGfxContext().RestoreOrigin();
121 void CGUIControlGroup::RenderEx()
123 for (auto *control
: m_children
)
125 CGUIControl::RenderEx();
128 bool CGUIControlGroup::OnAction(const CAction
&action
)
130 assert(false); // unimplemented
134 bool CGUIControlGroup::HasFocus() const
136 for (auto *control
: m_children
)
138 if (control
->HasFocus())
144 bool CGUIControlGroup::OnMessage(CGUIMessage
& message
)
146 switch (message
.GetMessage() )
148 case GUI_MSG_ITEM_SELECT
:
150 if (message
.GetControlId() == GetID())
152 m_focusedControl
= message
.GetParam1();
157 case GUI_MSG_ITEM_SELECTED
:
159 if (message
.GetControlId() == GetID())
161 message
.SetParam1(m_focusedControl
);
166 case GUI_MSG_FOCUSED
:
167 { // a control has been focused
168 m_focusedControl
= message
.GetControlId();
170 // tell our parent thatwe have focus
172 m_parentControl
->OnMessage(message
);
175 case GUI_MSG_SETFOCUS
:
177 // first try our last focused control...
178 if (!m_defaultAlways
&& m_focusedControl
)
180 CGUIControl
*control
= GetFirstFocusableControl(m_focusedControl
);
183 CGUIMessage
msg(GUI_MSG_SETFOCUS
, GetParentID(), control
->GetID());
184 return control
->OnMessage(msg
);
187 // ok, no previously focused control, try the default control first
188 if (m_defaultControl
)
190 CGUIControl
*control
= GetFirstFocusableControl(m_defaultControl
);
193 CGUIMessage
msg(GUI_MSG_SETFOCUS
, GetParentID(), control
->GetID());
194 return control
->OnMessage(msg
);
197 // no success with the default control, so just find one to focus
198 CGUIControl
*control
= GetFirstFocusableControl(0);
201 CGUIMessage
msg(GUI_MSG_SETFOCUS
, GetParentID(), control
->GetID());
202 return control
->OnMessage(msg
);
208 case GUI_MSG_LOSTFOCUS
:
210 // set all subcontrols unfocused
211 for (auto *control
: m_children
)
212 control
->SetFocus(false);
213 if (!GetControl(message
.GetParam1()))
214 { // we don't have the new id, so unfocus
217 m_parentControl
->OnMessage(message
);
222 case GUI_MSG_PAGE_CHANGE
:
223 case GUI_MSG_REFRESH_THUMBS
:
224 case GUI_MSG_REFRESH_LIST
:
225 case GUI_MSG_WINDOW_RESIZE
:
226 { // send to all child controls (make sure the target is the control id)
227 for (auto *control
: m_children
)
229 CGUIMessage
msg(message
.GetMessage(), message
.GetSenderId(), control
->GetID(), message
.GetParam1());
230 control
->OnMessage(msg
);
235 case GUI_MSG_REFRESH_TIMER
:
236 if (!IsVisible() || !IsVisibleFromSkin())
241 //not intended for any specific control, send to all childs and our base handler.
242 if (message
.GetControlId() == 0)
244 for (auto *control
: m_children
)
246 handled
|= control
->OnMessage(message
);
248 return CGUIControl::OnMessage(message
) || handled
;
250 // if it's intended for us, then so be it
251 if (message
.GetControlId() == GetID())
252 return CGUIControl::OnMessage(message
);
254 return SendControlMessage(message
);
257 bool CGUIControlGroup::SendControlMessage(CGUIMessage
&message
)
259 IDCollector
collector(m_idCollector
);
261 CGUIControl
*ctrl(GetControl(message
.GetControlId(), collector
.m_collector
));
262 // see if a child matches, and send to the child control if so
263 if (ctrl
&& ctrl
->OnMessage(message
))
266 // Unhandled - send to all matching invisible controls as well
268 for (auto *control
: *collector
.m_collector
)
269 if (control
->OnMessage(message
))
275 bool CGUIControlGroup::CanFocus() const
277 if (!CGUIControl::CanFocus()) return false;
278 // see if we have any children that can be focused
279 for (auto *control
: m_children
)
281 if (control
->CanFocus())
287 void CGUIControlGroup::SetInitialVisibility()
289 CGUIControl::SetInitialVisibility();
290 for (auto *control
: m_children
)
291 control
->SetInitialVisibility();
294 void CGUIControlGroup::QueueAnimation(ANIMATION_TYPE animType
)
296 CGUIControl::QueueAnimation(animType
);
297 // send window level animations to our children as well
298 if (animType
== ANIM_TYPE_WINDOW_OPEN
|| animType
== ANIM_TYPE_WINDOW_CLOSE
)
300 for (auto *control
: m_children
)
301 control
->QueueAnimation(animType
);
305 void CGUIControlGroup::ResetAnimation(ANIMATION_TYPE animType
)
307 CGUIControl::ResetAnimation(animType
);
308 // send window level animations to our children as well
309 if (animType
== ANIM_TYPE_WINDOW_OPEN
|| animType
== ANIM_TYPE_WINDOW_CLOSE
)
311 for (auto *control
: m_children
)
312 control
->ResetAnimation(animType
);
316 void CGUIControlGroup::ResetAnimations()
317 { // resets all animations, regardless of condition
318 CGUIControl::ResetAnimations();
319 for (auto *control
: m_children
)
320 control
->ResetAnimations();
323 bool CGUIControlGroup::IsAnimating(ANIMATION_TYPE animType
)
325 if (CGUIControl::IsAnimating(animType
))
330 for (auto *control
: m_children
)
332 if (control
->IsAnimating(animType
))
339 bool CGUIControlGroup::HasAnimation(ANIMATION_TYPE animType
)
341 if (CGUIControl::HasAnimation(animType
))
346 for (auto *control
: m_children
)
348 if (control
->HasAnimation(animType
))
355 EVENT_RESULT
CGUIControlGroup::SendMouseEvent(const CPoint
&point
, const CMouseEvent
&event
)
357 // transform our position into child coordinates
358 CPoint
childPoint(point
);
359 m_transform
.InverseTransformPosition(childPoint
.x
, childPoint
.y
);
361 if (CGUIControl::CanFocus())
363 CPoint
pos(GetPosition());
364 // run through our controls in reverse order (so that last rendered is checked first)
365 for (rControls i
= m_children
.rbegin(); i
!= m_children
.rend(); ++i
)
367 CGUIControl
*child
= *i
;
368 EVENT_RESULT ret
= child
->SendMouseEvent(childPoint
- pos
, event
);
370 { // we've handled the action, and/or have focused an item
374 // none of our children want the event, but we may want it.
376 if (HitTest(childPoint
) && (ret
= OnMouseEvent(childPoint
, event
)))
379 m_focusedControl
= 0;
380 return EVENT_RESULT_UNHANDLED
;
383 void CGUIControlGroup::UnfocusFromPoint(const CPoint
&point
)
385 CPoint
controlCoords(point
);
386 m_transform
.InverseTransformPosition(controlCoords
.x
, controlCoords
.y
);
387 controlCoords
-= GetPosition();
388 for (auto *child
: m_children
)
390 child
->UnfocusFromPoint(controlCoords
);
392 CGUIControl::UnfocusFromPoint(point
);
395 int CGUIControlGroup::GetFocusedControlID() const
397 if (m_focusedControl
) return m_focusedControl
;
398 CGUIControl
*control
= GetFocusedControl();
399 if (control
) return control
->GetID();
403 CGUIControl
*CGUIControlGroup::GetFocusedControl() const
406 if (m_focusedControl
)
408 // we may have multiple controls with same id - we pick first that has focus
409 std::pair
<LookupMap::const_iterator
, LookupMap::const_iterator
> range
= GetLookupControls(m_focusedControl
);
411 for (LookupMap::const_iterator i
= range
.first
; i
!= range
.second
; ++i
)
413 if (i
->second
->HasFocus())
418 // if lookup didn't find focused control, iterate m_children to find it
419 for (auto *control
: m_children
)
421 // Avoid calling HasFocus() on control group as it will (possibly) recursively
422 // traverse entire group tree just to check if there is focused control.
423 // We are recursively traversing it here so no point in doing it twice.
424 CGUIControlGroup
*groupControl(dynamic_cast<CGUIControlGroup
*>(control
));
427 CGUIControl
* focusedControl
= groupControl
->GetFocusedControl();
429 return focusedControl
;
431 else if (control
->HasFocus())
437 // in the case of id == 0, we don't match id
438 CGUIControl
*CGUIControlGroup::GetFirstFocusableControl(int id
)
440 if (!CanFocus()) return NULL
;
441 if (id
&& id
== GetID()) return this; // we're focusable and they want us
442 for (auto *pControl
: m_children
)
444 CGUIControlGroup
*group(dynamic_cast<CGUIControlGroup
*>(pControl
));
447 CGUIControl
*control
= group
->GetFirstFocusableControl(id
);
448 if (control
) return control
;
450 if ((!id
|| pControl
->GetID() == id
) && pControl
->CanFocus())
456 void CGUIControlGroup::AddControl(CGUIControl
*control
, int position
/* = -1*/)
458 if (!control
) return;
459 if (position
< 0 || position
> (int)m_children
.size())
460 position
= (int)m_children
.size();
461 m_children
.insert(m_children
.begin() + position
, control
);
462 control
->SetParentControl(this);
463 control
->SetControlStats(m_controlStats
);
464 control
->SetPushUpdates(m_pushedUpdates
);
469 bool CGUIControlGroup::InsertControl(CGUIControl
*control
, const CGUIControl
*insertPoint
)
472 for (unsigned int i
= 0; i
< m_children
.size(); i
++)
474 CGUIControl
*child
= m_children
[i
];
475 CGUIControlGroup
*group(dynamic_cast<CGUIControlGroup
*>(child
));
476 if (group
&& group
->InsertControl(control
, insertPoint
))
478 else if (child
== insertPoint
)
480 AddControl(control
, i
);
487 void CGUIControlGroup::SaveStates(std::vector
<CControlState
> &states
)
489 // save our state, and that of our children
490 states
.emplace_back(GetID(), m_focusedControl
);
491 for (auto *control
: m_children
)
492 control
->SaveStates(states
);
495 // Note: This routine doesn't delete the control. It just removes it from the control list
496 bool CGUIControlGroup::RemoveControl(const CGUIControl
*control
)
498 for (iControls it
= m_children
.begin(); it
!= m_children
.end(); ++it
)
500 CGUIControl
*child
= *it
;
501 CGUIControlGroup
*group(dynamic_cast<CGUIControlGroup
*>(child
));
502 if (group
&& group
->RemoveControl(control
))
504 if (control
== child
)
506 m_children
.erase(it
);
515 void CGUIControlGroup::ClearAll()
517 // first remove from the lookup table
520 // and delete all our children
521 for (auto *control
: m_children
)
525 m_focusedControl
= 0;
532 void CGUIControlGroup::DumpTextureUse()
534 for (auto *control
: m_children
)
535 control
->DumpTextureUse();