Merge pull request #22816 from CastagnaIT/fix_tx3g
[xbmc.git] / xbmc / guilib / GUIControlGroup.cpp
blobf737f9fef3cc0e86405dadbdb4ea00479fe178af
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 "GUIControlGroup.h"
11 #include "GUIMessage.h"
13 #include <cassert>
14 #include <utility>
16 CGUIControlGroup::CGUIControlGroup()
18 m_defaultControl = 0;
19 m_defaultAlways = false;
20 m_focusedControl = 0;
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)
28 m_defaultControl = 0;
29 m_defaultAlways = false;
30 m_focusedControl = 0;
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());
46 // defaults
47 m_focusedControl = 0;
48 ControlType = GUICONTROL_GROUP;
51 CGUIControlGroup::~CGUIControlGroup(void)
53 ClearAll();
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);
88 CRect rect;
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;
112 else
113 control->DoRender();
115 if (focusedControl)
116 focusedControl->DoRender();
117 CGUIControl::Render();
118 CServiceBroker::GetWinSystem()->GetGfxContext().RestoreOrigin();
121 void CGUIControlGroup::RenderEx()
123 for (auto *control : m_children)
124 control->RenderEx();
125 CGUIControl::RenderEx();
128 bool CGUIControlGroup::OnAction(const CAction &action)
130 assert(false); // unimplemented
131 return false;
134 bool CGUIControlGroup::HasFocus() const
136 for (auto *control : m_children)
138 if (control->HasFocus())
139 return true;
141 return false;
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();
153 return true;
155 break;
157 case GUI_MSG_ITEM_SELECTED:
159 if (message.GetControlId() == GetID())
161 message.SetParam1(m_focusedControl);
162 return true;
164 break;
166 case GUI_MSG_FOCUSED:
167 { // a control has been focused
168 m_focusedControl = message.GetControlId();
169 SetFocus(true);
170 // tell our parent thatwe have focus
171 if (m_parentControl)
172 m_parentControl->OnMessage(message);
173 return true;
175 case GUI_MSG_SETFOCUS:
177 // first try our last focused control...
178 if (!m_defaultAlways && m_focusedControl)
180 CGUIControl *control = GetFirstFocusableControl(m_focusedControl);
181 if (control)
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);
191 if (control)
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);
199 if (control)
201 CGUIMessage msg(GUI_MSG_SETFOCUS, GetParentID(), control->GetID());
202 return control->OnMessage(msg);
204 // unsuccessful
205 return false;
206 break;
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
215 SetFocus(false);
216 if (m_parentControl)
217 m_parentControl->OnMessage(message);
219 return true;
221 break;
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);
232 return true;
234 break;
235 case GUI_MSG_REFRESH_TIMER:
236 if (!IsVisible() || !IsVisibleFromSkin())
237 return true;
238 break;
240 bool handled(false);
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))
264 return true;
266 // Unhandled - send to all matching invisible controls as well
267 bool handled(false);
268 for (auto *control : *collector.m_collector)
269 if (control->OnMessage(message))
270 handled = true;
272 return handled;
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())
282 return true;
284 return false;
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))
326 return true;
328 if (IsVisible())
330 for (auto *control : m_children)
332 if (control->IsAnimating(animType))
333 return true;
336 return false;
339 bool CGUIControlGroup::HasAnimation(ANIMATION_TYPE animType)
341 if (CGUIControl::HasAnimation(animType))
342 return true;
344 if (IsVisible())
346 for (auto *control : m_children)
348 if (control->HasAnimation(animType))
349 return true;
352 return false;
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);
369 if (ret)
370 { // we've handled the action, and/or have focused an item
371 return ret;
374 // none of our children want the event, but we may want it.
375 EVENT_RESULT ret;
376 if (HitTest(childPoint) && (ret = OnMouseEvent(childPoint, event)))
377 return ret;
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();
400 return 0;
403 CGUIControl *CGUIControlGroup::GetFocusedControl() const
405 // try lookup first
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())
414 return i->second;
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));
425 if (groupControl)
427 CGUIControl* focusedControl = groupControl->GetFocusedControl();
428 if (focusedControl)
429 return focusedControl;
431 else if (control->HasFocus())
432 return control;
434 return NULL;
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));
445 if (group)
447 CGUIControl *control = group->GetFirstFocusableControl(id);
448 if (control) return control;
450 if ((!id || pControl->GetID() == id) && pControl->CanFocus())
451 return pControl;
453 return NULL;
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);
465 AddLookup(control);
466 SetInvalid();
469 bool CGUIControlGroup::InsertControl(CGUIControl *control, const CGUIControl *insertPoint)
471 // find our position
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))
477 return true;
478 else if (child == insertPoint)
480 AddControl(control, i);
481 return true;
484 return false;
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))
503 return true;
504 if (control == child)
506 m_children.erase(it);
507 RemoveLookup(child);
508 SetInvalid();
509 return true;
512 return false;
515 void CGUIControlGroup::ClearAll()
517 // first remove from the lookup table
518 RemoveLookup();
520 // and delete all our children
521 for (auto *control : m_children)
523 delete control;
525 m_focusedControl = 0;
526 m_children.clear();
527 ClearLookup();
528 SetInvalid();
531 #ifdef _DEBUG
532 void CGUIControlGroup::DumpTextureUse()
534 for (auto *control : m_children)
535 control->DumpTextureUse();
537 #endif