/xbmc/guilib/GUIControlGroup.cpp

http://github.com/xbmc/xbmc · C++ · 537 lines · 451 code · 49 blank · 37 comment · 114 complexity · 1709079b3f5c0230776c213081614428 MD5 · raw file

  1. /*
  2. * Copyright (C) 2005-2018 Team Kodi
  3. * This file is part of Kodi - https://kodi.tv
  4. *
  5. * SPDX-License-Identifier: GPL-2.0-or-later
  6. * See LICENSES/README.md for more information.
  7. */
  8. #include "GUIControlGroup.h"
  9. #include "GUIMessage.h"
  10. #include <cassert>
  11. #include <utility>
  12. CGUIControlGroup::CGUIControlGroup()
  13. {
  14. m_defaultControl = 0;
  15. m_defaultAlways = false;
  16. m_focusedControl = 0;
  17. m_renderFocusedLast = false;
  18. ControlType = GUICONTROL_GROUP;
  19. }
  20. CGUIControlGroup::CGUIControlGroup(int parentID, int controlID, float posX, float posY, float width, float height)
  21. : CGUIControlLookup(parentID, controlID, posX, posY, width, height)
  22. {
  23. m_defaultControl = 0;
  24. m_defaultAlways = false;
  25. m_focusedControl = 0;
  26. m_renderFocusedLast = false;
  27. ControlType = GUICONTROL_GROUP;
  28. }
  29. CGUIControlGroup::CGUIControlGroup(const CGUIControlGroup &from)
  30. : CGUIControlLookup(from)
  31. {
  32. m_defaultControl = from.m_defaultControl;
  33. m_defaultAlways = from.m_defaultAlways;
  34. m_renderFocusedLast = from.m_renderFocusedLast;
  35. // run through and add our controls
  36. for (auto *i : from.m_children)
  37. AddControl(i->Clone());
  38. // defaults
  39. m_focusedControl = 0;
  40. ControlType = GUICONTROL_GROUP;
  41. }
  42. CGUIControlGroup::~CGUIControlGroup(void)
  43. {
  44. ClearAll();
  45. }
  46. void CGUIControlGroup::AllocResources()
  47. {
  48. CGUIControl::AllocResources();
  49. for (auto *control : m_children)
  50. {
  51. if (!control->IsDynamicallyAllocated())
  52. control->AllocResources();
  53. }
  54. }
  55. void CGUIControlGroup::FreeResources(bool immediately)
  56. {
  57. CGUIControl::FreeResources(immediately);
  58. for (auto *control : m_children)
  59. {
  60. control->FreeResources(immediately);
  61. }
  62. }
  63. void CGUIControlGroup::DynamicResourceAlloc(bool bOnOff)
  64. {
  65. for (auto *control : m_children)
  66. {
  67. control->DynamicResourceAlloc(bOnOff);
  68. }
  69. }
  70. void CGUIControlGroup::Process(unsigned int currentTime, CDirtyRegionList &dirtyregions)
  71. {
  72. CPoint pos(GetPosition());
  73. CServiceBroker::GetWinSystem()->GetGfxContext().SetOrigin(pos.x, pos.y);
  74. CRect rect;
  75. for (auto *control : m_children)
  76. {
  77. control->UpdateVisibility(nullptr);
  78. unsigned int oldDirty = dirtyregions.size();
  79. control->DoProcess(currentTime, dirtyregions);
  80. if (control->IsVisible() || (oldDirty != dirtyregions.size())) // visible or dirty (was visible?)
  81. rect.Union(control->GetRenderRegion());
  82. }
  83. CServiceBroker::GetWinSystem()->GetGfxContext().RestoreOrigin();
  84. CGUIControl::Process(currentTime, dirtyregions);
  85. m_renderRegion = rect;
  86. }
  87. void CGUIControlGroup::Render()
  88. {
  89. CPoint pos(GetPosition());
  90. CServiceBroker::GetWinSystem()->GetGfxContext().SetOrigin(pos.x, pos.y);
  91. CGUIControl *focusedControl = NULL;
  92. for (auto *control : m_children)
  93. {
  94. if (m_renderFocusedLast && control->HasFocus())
  95. focusedControl = control;
  96. else
  97. control->DoRender();
  98. }
  99. if (focusedControl)
  100. focusedControl->DoRender();
  101. CGUIControl::Render();
  102. CServiceBroker::GetWinSystem()->GetGfxContext().RestoreOrigin();
  103. }
  104. void CGUIControlGroup::RenderEx()
  105. {
  106. for (auto *control : m_children)
  107. control->RenderEx();
  108. CGUIControl::RenderEx();
  109. }
  110. bool CGUIControlGroup::OnAction(const CAction &action)
  111. {
  112. assert(false); // unimplemented
  113. return false;
  114. }
  115. bool CGUIControlGroup::HasFocus() const
  116. {
  117. for (auto *control : m_children)
  118. {
  119. if (control->HasFocus())
  120. return true;
  121. }
  122. return false;
  123. }
  124. bool CGUIControlGroup::OnMessage(CGUIMessage& message)
  125. {
  126. switch (message.GetMessage() )
  127. {
  128. case GUI_MSG_ITEM_SELECT:
  129. {
  130. if (message.GetControlId() == GetID())
  131. {
  132. m_focusedControl = message.GetParam1();
  133. return true;
  134. }
  135. break;
  136. }
  137. case GUI_MSG_ITEM_SELECTED:
  138. {
  139. if (message.GetControlId() == GetID())
  140. {
  141. message.SetParam1(m_focusedControl);
  142. return true;
  143. }
  144. break;
  145. }
  146. case GUI_MSG_FOCUSED:
  147. { // a control has been focused
  148. m_focusedControl = message.GetControlId();
  149. SetFocus(true);
  150. // tell our parent thatwe have focus
  151. if (m_parentControl)
  152. m_parentControl->OnMessage(message);
  153. return true;
  154. }
  155. case GUI_MSG_SETFOCUS:
  156. {
  157. // first try our last focused control...
  158. if (!m_defaultAlways && m_focusedControl)
  159. {
  160. CGUIControl *control = GetFirstFocusableControl(m_focusedControl);
  161. if (control)
  162. {
  163. CGUIMessage msg(GUI_MSG_SETFOCUS, GetParentID(), control->GetID());
  164. return control->OnMessage(msg);
  165. }
  166. }
  167. // ok, no previously focused control, try the default control first
  168. if (m_defaultControl)
  169. {
  170. CGUIControl *control = GetFirstFocusableControl(m_defaultControl);
  171. if (control)
  172. {
  173. CGUIMessage msg(GUI_MSG_SETFOCUS, GetParentID(), control->GetID());
  174. return control->OnMessage(msg);
  175. }
  176. }
  177. // no success with the default control, so just find one to focus
  178. CGUIControl *control = GetFirstFocusableControl(0);
  179. if (control)
  180. {
  181. CGUIMessage msg(GUI_MSG_SETFOCUS, GetParentID(), control->GetID());
  182. return control->OnMessage(msg);
  183. }
  184. // unsuccessful
  185. return false;
  186. break;
  187. }
  188. case GUI_MSG_LOSTFOCUS:
  189. {
  190. // set all subcontrols unfocused
  191. for (auto *control : m_children)
  192. control->SetFocus(false);
  193. if (!GetControl(message.GetParam1()))
  194. { // we don't have the new id, so unfocus
  195. SetFocus(false);
  196. if (m_parentControl)
  197. m_parentControl->OnMessage(message);
  198. }
  199. return true;
  200. }
  201. break;
  202. case GUI_MSG_PAGE_CHANGE:
  203. case GUI_MSG_REFRESH_THUMBS:
  204. case GUI_MSG_REFRESH_LIST:
  205. case GUI_MSG_WINDOW_RESIZE:
  206. { // send to all child controls (make sure the target is the control id)
  207. for (auto *control : m_children)
  208. {
  209. CGUIMessage msg(message.GetMessage(), message.GetSenderId(), control->GetID(), message.GetParam1());
  210. control->OnMessage(msg);
  211. }
  212. return true;
  213. }
  214. break;
  215. case GUI_MSG_REFRESH_TIMER:
  216. if (!IsVisible() || !IsVisibleFromSkin())
  217. return true;
  218. break;
  219. }
  220. bool handled(false);
  221. //not intented for any specific control, send to all childs and our base handler.
  222. if (message.GetControlId() == 0)
  223. {
  224. for (auto *control : m_children)
  225. {
  226. handled |= control->OnMessage(message);
  227. }
  228. return CGUIControl::OnMessage(message) || handled;
  229. }
  230. // if it's intended for us, then so be it
  231. if (message.GetControlId() == GetID())
  232. return CGUIControl::OnMessage(message);
  233. return SendControlMessage(message);
  234. }
  235. bool CGUIControlGroup::SendControlMessage(CGUIMessage &message)
  236. {
  237. IDCollector collector(m_idCollector);
  238. CGUIControl *ctrl(GetControl(message.GetControlId(), collector.m_collector));
  239. // see if a child matches, and send to the child control if so
  240. if (ctrl && ctrl->OnMessage(message))
  241. return true;
  242. // Unhandled - send to all matching invisible controls as well
  243. bool handled(false);
  244. for (auto *control : *collector.m_collector)
  245. if (control->OnMessage(message))
  246. handled = true;
  247. return handled;
  248. }
  249. bool CGUIControlGroup::CanFocus() const
  250. {
  251. if (!CGUIControl::CanFocus()) return false;
  252. // see if we have any children that can be focused
  253. for (auto *control : m_children)
  254. {
  255. if (control->CanFocus())
  256. return true;
  257. }
  258. return false;
  259. }
  260. void CGUIControlGroup::SetInitialVisibility()
  261. {
  262. CGUIControl::SetInitialVisibility();
  263. for (auto *control : m_children)
  264. control->SetInitialVisibility();
  265. }
  266. void CGUIControlGroup::QueueAnimation(ANIMATION_TYPE animType)
  267. {
  268. CGUIControl::QueueAnimation(animType);
  269. // send window level animations to our children as well
  270. if (animType == ANIM_TYPE_WINDOW_OPEN || animType == ANIM_TYPE_WINDOW_CLOSE)
  271. {
  272. for (auto *control : m_children)
  273. control->QueueAnimation(animType);
  274. }
  275. }
  276. void CGUIControlGroup::ResetAnimation(ANIMATION_TYPE animType)
  277. {
  278. CGUIControl::ResetAnimation(animType);
  279. // send window level animations to our children as well
  280. if (animType == ANIM_TYPE_WINDOW_OPEN || animType == ANIM_TYPE_WINDOW_CLOSE)
  281. {
  282. for (auto *control : m_children)
  283. control->ResetAnimation(animType);
  284. }
  285. }
  286. void CGUIControlGroup::ResetAnimations()
  287. { // resets all animations, regardless of condition
  288. CGUIControl::ResetAnimations();
  289. for (auto *control : m_children)
  290. control->ResetAnimations();
  291. }
  292. bool CGUIControlGroup::IsAnimating(ANIMATION_TYPE animType)
  293. {
  294. if (CGUIControl::IsAnimating(animType))
  295. return true;
  296. if (IsVisible())
  297. {
  298. for (auto *control : m_children)
  299. {
  300. if (control->IsAnimating(animType))
  301. return true;
  302. }
  303. }
  304. return false;
  305. }
  306. bool CGUIControlGroup::HasAnimation(ANIMATION_TYPE animType)
  307. {
  308. if (CGUIControl::HasAnimation(animType))
  309. return true;
  310. if (IsVisible())
  311. {
  312. for (auto *control : m_children)
  313. {
  314. if (control->HasAnimation(animType))
  315. return true;
  316. }
  317. }
  318. return false;
  319. }
  320. EVENT_RESULT CGUIControlGroup::SendMouseEvent(const CPoint &point, const CMouseEvent &event)
  321. {
  322. // transform our position into child coordinates
  323. CPoint childPoint(point);
  324. m_transform.InverseTransformPosition(childPoint.x, childPoint.y);
  325. if (CGUIControl::CanFocus())
  326. {
  327. CPoint pos(GetPosition());
  328. // run through our controls in reverse order (so that last rendered is checked first)
  329. for (rControls i = m_children.rbegin(); i != m_children.rend(); ++i)
  330. {
  331. CGUIControl *child = *i;
  332. EVENT_RESULT ret = child->SendMouseEvent(childPoint - pos, event);
  333. if (ret)
  334. { // we've handled the action, and/or have focused an item
  335. return ret;
  336. }
  337. }
  338. // none of our children want the event, but we may want it.
  339. EVENT_RESULT ret;
  340. if (HitTest(childPoint) && (ret = OnMouseEvent(childPoint, event)))
  341. return ret;
  342. }
  343. m_focusedControl = 0;
  344. return EVENT_RESULT_UNHANDLED;
  345. }
  346. void CGUIControlGroup::UnfocusFromPoint(const CPoint &point)
  347. {
  348. CPoint controlCoords(point);
  349. m_transform.InverseTransformPosition(controlCoords.x, controlCoords.y);
  350. controlCoords -= GetPosition();
  351. for (auto *child : m_children)
  352. {
  353. child->UnfocusFromPoint(controlCoords);
  354. }
  355. CGUIControl::UnfocusFromPoint(point);
  356. }
  357. int CGUIControlGroup::GetFocusedControlID() const
  358. {
  359. if (m_focusedControl) return m_focusedControl;
  360. CGUIControl *control = GetFocusedControl();
  361. if (control) return control->GetID();
  362. return 0;
  363. }
  364. CGUIControl *CGUIControlGroup::GetFocusedControl() const
  365. {
  366. // try lookup first
  367. if (m_focusedControl)
  368. {
  369. // we may have multiple controls with same id - we pick first that has focus
  370. std::pair<LookupMap::const_iterator, LookupMap::const_iterator> range = GetLookupControls(m_focusedControl);
  371. for (LookupMap::const_iterator i = range.first; i != range.second; ++i)
  372. {
  373. if (i->second->HasFocus())
  374. return i->second;
  375. }
  376. }
  377. // if lookup didn't find focused control, iterate m_children to find it
  378. for (auto *control : m_children)
  379. {
  380. // Avoid calling HasFocus() on control group as it will (possibly) recursively
  381. // traverse entire group tree just to check if there is focused control.
  382. // We are recursively traversing it here so no point in doing it twice.
  383. CGUIControlGroup *groupControl(dynamic_cast<CGUIControlGroup*>(control));
  384. if (groupControl)
  385. {
  386. CGUIControl* focusedControl = groupControl->GetFocusedControl();
  387. if (focusedControl)
  388. return focusedControl;
  389. }
  390. else if (control->HasFocus())
  391. return control;
  392. }
  393. return NULL;
  394. }
  395. // in the case of id == 0, we don't match id
  396. CGUIControl *CGUIControlGroup::GetFirstFocusableControl(int id)
  397. {
  398. if (!CanFocus()) return NULL;
  399. if (id && id == GetID()) return this; // we're focusable and they want us
  400. for (auto *pControl : m_children)
  401. {
  402. CGUIControlGroup *group(dynamic_cast<CGUIControlGroup*>(pControl));
  403. if (group)
  404. {
  405. CGUIControl *control = group->GetFirstFocusableControl(id);
  406. if (control) return control;
  407. }
  408. if ((!id || pControl->GetID() == id) && pControl->CanFocus())
  409. return pControl;
  410. }
  411. return NULL;
  412. }
  413. void CGUIControlGroup::AddControl(CGUIControl *control, int position /* = -1*/)
  414. {
  415. if (!control) return;
  416. if (position < 0 || position > (int)m_children.size())
  417. position = (int)m_children.size();
  418. m_children.insert(m_children.begin() + position, control);
  419. control->SetParentControl(this);
  420. control->SetControlStats(m_controlStats);
  421. control->SetPushUpdates(m_pushedUpdates);
  422. AddLookup(control);
  423. SetInvalid();
  424. }
  425. bool CGUIControlGroup::InsertControl(CGUIControl *control, const CGUIControl *insertPoint)
  426. {
  427. // find our position
  428. for (unsigned int i = 0; i < m_children.size(); i++)
  429. {
  430. CGUIControl *child = m_children[i];
  431. CGUIControlGroup *group(dynamic_cast<CGUIControlGroup*>(child));
  432. if (group && group->InsertControl(control, insertPoint))
  433. return true;
  434. else if (child == insertPoint)
  435. {
  436. AddControl(control, i);
  437. return true;
  438. }
  439. }
  440. return false;
  441. }
  442. void CGUIControlGroup::SaveStates(std::vector<CControlState> &states)
  443. {
  444. // save our state, and that of our children
  445. states.emplace_back(GetID(), m_focusedControl);
  446. for (auto *control : m_children)
  447. control->SaveStates(states);
  448. }
  449. // Note: This routine doesn't delete the control. It just removes it from the control list
  450. bool CGUIControlGroup::RemoveControl(const CGUIControl *control)
  451. {
  452. for (iControls it = m_children.begin(); it != m_children.end(); ++it)
  453. {
  454. CGUIControl *child = *it;
  455. CGUIControlGroup *group(dynamic_cast<CGUIControlGroup*>(child));
  456. if (group && group->RemoveControl(control))
  457. return true;
  458. if (control == child)
  459. {
  460. m_children.erase(it);
  461. RemoveLookup(child);
  462. SetInvalid();
  463. return true;
  464. }
  465. }
  466. return false;
  467. }
  468. void CGUIControlGroup::ClearAll()
  469. {
  470. // first remove from the lookup table
  471. RemoveLookup();
  472. // and delete all our children
  473. for (auto *control : m_children)
  474. {
  475. delete control;
  476. }
  477. m_focusedControl = 0;
  478. m_children.clear();
  479. ClearLookup();
  480. SetInvalid();
  481. }
  482. #ifdef _DEBUG
  483. void CGUIControlGroup::DumpTextureUse()
  484. {
  485. for (auto *control : m_children)
  486. control->DumpTextureUse();
  487. }
  488. #endif