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
9 #include "GUIControl.h"
10
11 #include "GUIAction.h"
12 #include "GUIComponent.h"
13 #include "GUIControlProfiler.h"
14 #include "GUIInfoManager.h"
15 #include "GUIMessage.h"
16 #include "GUITexture.h"
17 #include "GUIWindowManager.h"
18 #include "ServiceBroker.h"
19 #include "input/InputManager.h"
20 #include "input/Key.h"
21 #include "input/mouse/MouseStat.h"
22 #include "utils/log.h"
23
24 using namespace KODI::GUILIB;
25
CGUIControl()26 CGUIControl::CGUIControl()
27 {
28 m_hasProcessed = false;
29 m_bHasFocus = false;
30 m_controlID = 0;
31 m_parentID = 0;
32 m_visible = VISIBLE;
33 m_visibleFromSkinCondition = true;
34 m_forceHidden = false;
35 m_enabled = true;
36 m_posX = 0;
37 m_posY = 0;
38 m_width = 0;
39 m_height = 0;
40 ControlType = GUICONTROL_UNKNOWN;
41 m_bInvalidated = true;
42 m_bAllocated=false;
43 m_parentControl = NULL;
44 m_hasCamera = false;
45 m_pushedUpdates = false;
46 m_pulseOnSelect = false;
47 m_controlDirtyState = DIRTY_STATE_CONTROL;
48 m_stereo = 0.0f;
49 m_controlStats = nullptr;
50 }
51
CGUIControl(int parentID,int controlID,float posX,float posY,float width,float height)52 CGUIControl::CGUIControl(int parentID, int controlID, float posX, float posY, float width, float height)
53 : m_hitRect(posX, posY, posX + width, posY + height),
54 m_diffuseColor(0xffffffff)
55 {
56 m_posX = posX;
57 m_posY = posY;
58 m_width = width;
59 m_height = height;
60 m_bHasFocus = false;
61 m_controlID = controlID;
62 m_parentID = parentID;
63 m_visible = VISIBLE;
64 m_visibleFromSkinCondition = true;
65 m_forceHidden = false;
66 m_enabled = true;
67 ControlType = GUICONTROL_UNKNOWN;
68 m_bInvalidated = true;
69 m_bAllocated=false;
70 m_hasProcessed = false;
71 m_parentControl = NULL;
72 m_hasCamera = false;
73 m_pushedUpdates = false;
74 m_pulseOnSelect = false;
75 m_controlDirtyState = DIRTY_STATE_CONTROL;
76 m_stereo = 0.0f;
77 m_controlStats = nullptr;
78 }
79
80 CGUIControl::CGUIControl(const CGUIControl &) = default;
81
82 CGUIControl::~CGUIControl(void) = default;
83
AllocResources()84 void CGUIControl::AllocResources()
85 {
86 m_hasProcessed = false;
87 m_bInvalidated = true;
88 m_bAllocated=true;
89 }
90
FreeResources(bool immediately)91 void CGUIControl::FreeResources(bool immediately)
92 {
93 if (m_bAllocated)
94 {
95 // Reset our animation states - not conditional anims though.
96 // I'm not sure if this is needed for most cases anyway. I believe it's only here
97 // because some windows aren't loaded on demand
98 for (unsigned int i = 0; i < m_animations.size(); i++)
99 {
100 CAnimation &anim = m_animations[i];
101 if (anim.GetType() != ANIM_TYPE_CONDITIONAL)
102 anim.ResetAnimation();
103 }
104 m_bAllocated=false;
105 }
106 m_hasProcessed = false;
107 }
108
DynamicResourceAlloc(bool bOnOff)109 void CGUIControl::DynamicResourceAlloc(bool bOnOff)
110 {
111
112 }
113
114 // the main processing routine.
115 // 1. animate and set animation transform
116 // 2. if visible, process
117 // 3. reset the animation transform
DoProcess(unsigned int currentTime,CDirtyRegionList & dirtyregions)118 void CGUIControl::DoProcess(unsigned int currentTime, CDirtyRegionList &dirtyregions)
119 {
120 CRect dirtyRegion = m_renderRegion;
121
122 bool changed = (m_controlDirtyState & DIRTY_STATE_CONTROL) != 0 || (m_bInvalidated && IsVisible());
123 m_controlDirtyState = 0;
124
125 if (Animate(currentTime))
126 MarkDirtyRegion();
127
128 if (IsVisible())
129 {
130 m_cachedTransform = CServiceBroker::GetWinSystem()->GetGfxContext().AddTransform(m_transform);
131 if (m_hasCamera)
132 CServiceBroker::GetWinSystem()->GetGfxContext().SetCameraPosition(m_camera);
133
134 Process(currentTime, dirtyregions);
135 m_bInvalidated = false;
136
137 if (dirtyRegion != m_renderRegion)
138 {
139 dirtyRegion.Union(m_renderRegion);
140 changed = true;
141 }
142
143 if (m_hasCamera)
144 CServiceBroker::GetWinSystem()->GetGfxContext().RestoreCameraPosition();
145 CServiceBroker::GetWinSystem()->GetGfxContext().RemoveTransform();
146 }
147
148 UpdateControlStats();
149
150 changed |= (m_controlDirtyState & DIRTY_STATE_CONTROL) != 0;
151
152 if (changed)
153 {
154 dirtyregions.emplace_back(dirtyRegion);
155 }
156 }
157
Process(unsigned int currentTime,CDirtyRegionList & dirtyregions)158 void CGUIControl::Process(unsigned int currentTime, CDirtyRegionList &dirtyregions)
159 {
160 // update our render region
161 m_renderRegion = CServiceBroker::GetWinSystem()->GetGfxContext().GenerateAABB(CalcRenderRegion());
162 m_hasProcessed = true;
163 }
164
165 // the main render routine.
166 // 1. set the animation transform
167 // 2. if visible, paint
168 // 3. reset the animation transform
DoRender()169 void CGUIControl::DoRender()
170 {
171 if (IsVisible())
172 {
173 bool hasStereo = m_stereo != 0.0
174 && CServiceBroker::GetWinSystem()->GetGfxContext().GetStereoMode() != RENDER_STEREO_MODE_MONO
175 && CServiceBroker::GetWinSystem()->GetGfxContext().GetStereoMode() != RENDER_STEREO_MODE_OFF;
176
177 CServiceBroker::GetWinSystem()->GetGfxContext().SetTransform(m_cachedTransform);
178 if (m_hasCamera)
179 CServiceBroker::GetWinSystem()->GetGfxContext().SetCameraPosition(m_camera);
180 if (hasStereo)
181 CServiceBroker::GetWinSystem()->GetGfxContext().SetStereoFactor(m_stereo);
182
183 GUIPROFILER_RENDER_BEGIN(this);
184
185 if (m_hitColor != 0xffffffff)
186 {
187 UTILS::Color color = CServiceBroker::GetWinSystem()->GetGfxContext().MergeAlpha(m_hitColor);
188 CGUITexture::DrawQuad(CServiceBroker::GetWinSystem()->GetGfxContext().GenerateAABB(m_hitRect), color);
189 }
190
191 Render();
192
193 GUIPROFILER_RENDER_END(this);
194
195 if (hasStereo)
196 CServiceBroker::GetWinSystem()->GetGfxContext().RestoreStereoFactor();
197 if (m_hasCamera)
198 CServiceBroker::GetWinSystem()->GetGfxContext().RestoreCameraPosition();
199 CServiceBroker::GetWinSystem()->GetGfxContext().RemoveTransform();
200 }
201 }
202
OnAction(const CAction & action)203 bool CGUIControl::OnAction(const CAction &action)
204 {
205 if (HasFocus())
206 {
207 switch (action.GetID())
208 {
209 case ACTION_MOVE_DOWN:
210 OnDown();
211 return true;
212
213 case ACTION_MOVE_UP:
214 OnUp();
215 return true;
216
217 case ACTION_MOVE_LEFT:
218 OnLeft();
219 return true;
220
221 case ACTION_MOVE_RIGHT:
222 OnRight();
223 return true;
224
225 case ACTION_SHOW_INFO:
226 return OnInfo();
227
228 case ACTION_NAV_BACK:
229 return OnBack();
230
231 case ACTION_NEXT_CONTROL:
232 OnNextControl();
233 return true;
234
235 case ACTION_PREV_CONTROL:
236 OnPrevControl();
237 return true;
238 }
239 }
240 return false;
241 }
242
Navigate(int direction) const243 bool CGUIControl::Navigate(int direction) const
244 {
245 if (HasFocus())
246 {
247 CGUIMessage msg(GUI_MSG_MOVE, GetParentID(), GetID(), direction);
248 return SendWindowMessage(msg);
249 }
250 return false;
251 }
252
253 // Movement controls (derived classes can override)
OnUp()254 void CGUIControl::OnUp()
255 {
256 Navigate(ACTION_MOVE_UP);
257 }
258
OnDown()259 void CGUIControl::OnDown()
260 {
261 Navigate(ACTION_MOVE_DOWN);
262 }
263
OnLeft()264 void CGUIControl::OnLeft()
265 {
266 Navigate(ACTION_MOVE_LEFT);
267 }
268
OnRight()269 void CGUIControl::OnRight()
270 {
271 Navigate(ACTION_MOVE_RIGHT);
272 }
273
OnBack()274 bool CGUIControl::OnBack()
275 {
276 return Navigate(ACTION_NAV_BACK);
277 }
278
OnInfo()279 bool CGUIControl::OnInfo()
280 {
281 CGUIAction action = GetAction(ACTION_SHOW_INFO);
282 if (action.HasAnyActions())
283 return action.ExecuteActions(GetID(), GetParentID());
284 return false;
285 }
286
OnNextControl()287 void CGUIControl::OnNextControl()
288 {
289 Navigate(ACTION_NEXT_CONTROL);
290 }
291
OnPrevControl()292 void CGUIControl::OnPrevControl()
293 {
294 Navigate(ACTION_PREV_CONTROL);
295 }
296
SendWindowMessage(CGUIMessage & message) const297 bool CGUIControl::SendWindowMessage(CGUIMessage &message) const
298 {
299 CGUIWindow *pWindow = CServiceBroker::GetGUI()->GetWindowManager().GetWindow(GetParentID());
300 if (pWindow)
301 return pWindow->OnMessage(message);
302 return CServiceBroker::GetGUI()->GetWindowManager().SendMessage(message);
303 }
304
GetID(void) const305 int CGUIControl::GetID(void) const
306 {
307 return m_controlID;
308 }
309
310
GetParentID(void) const311 int CGUIControl::GetParentID(void) const
312 {
313 return m_parentID;
314 }
315
HasFocus(void) const316 bool CGUIControl::HasFocus(void) const
317 {
318 return m_bHasFocus;
319 }
320
SetFocus(bool focus)321 void CGUIControl::SetFocus(bool focus)
322 {
323 if (m_bHasFocus && !focus)
324 QueueAnimation(ANIM_TYPE_UNFOCUS);
325 else if (!m_bHasFocus && focus)
326 QueueAnimation(ANIM_TYPE_FOCUS);
327 m_bHasFocus = focus;
328 }
329
OnMessage(CGUIMessage & message)330 bool CGUIControl::OnMessage(CGUIMessage& message)
331 {
332 if ( message.GetControlId() == GetID() )
333 {
334 switch (message.GetMessage() )
335 {
336 case GUI_MSG_SETFOCUS:
337 // if control is disabled then move 2 the next control
338 if ( !CanFocus() )
339 {
340 CLog::Log(LOGERROR, "Control %u in window %u has been asked to focus, "
341 "but it can't",
342 GetID(), GetParentID());
343 return false;
344 }
345 SetFocus(true);
346 {
347 // inform our parent window that this has happened
348 CGUIMessage message(GUI_MSG_FOCUSED, GetParentID(), GetID());
349 if (m_parentControl)
350 m_parentControl->OnMessage(message);
351 }
352 return true;
353 break;
354
355 case GUI_MSG_LOSTFOCUS:
356 {
357 SetFocus(false);
358 // and tell our parent so it can unfocus
359 if (m_parentControl)
360 m_parentControl->OnMessage(message);
361 return true;
362 }
363 break;
364
365 case GUI_MSG_VISIBLE:
366 SetVisible(true, true);
367 return true;
368 break;
369
370 case GUI_MSG_HIDDEN:
371 SetVisible(false);
372 return true;
373
374 // Note that the skin <enable> tag will override these messages
375 case GUI_MSG_ENABLED:
376 SetEnabled(true);
377 return true;
378
379 case GUI_MSG_DISABLED:
380 SetEnabled(false);
381 return true;
382
383 case GUI_MSG_WINDOW_RESIZE:
384 // invalidate controls to get them to recalculate sizing information
385 SetInvalid();
386 return true;
387 }
388 }
389 return false;
390 }
391
CanFocus() const392 bool CGUIControl::CanFocus() const
393 {
394 if (!IsVisible() && !m_allowHiddenFocus) return false;
395 if (IsDisabled()) return false;
396 return true;
397 }
398
IsVisible() const399 bool CGUIControl::IsVisible() const
400 {
401 if (m_forceHidden) return false;
402 return m_visible == VISIBLE;
403 }
404
IsDisabled() const405 bool CGUIControl::IsDisabled() const
406 {
407 return !m_enabled;
408 }
409
SetEnabled(bool bEnable)410 void CGUIControl::SetEnabled(bool bEnable)
411 {
412 if (bEnable != m_enabled)
413 {
414 m_enabled = bEnable;
415 SetInvalid();
416 }
417 }
418
SetEnableCondition(const std::string & expression)419 void CGUIControl::SetEnableCondition(const std::string &expression)
420 {
421 if (expression == "true")
422 m_enabled = true;
423 else if (expression == "false")
424 m_enabled = false;
425 else
426 m_enableCondition = CServiceBroker::GetGUI()->GetInfoManager().Register(expression, GetParentID());
427 }
428
SetPosition(float posX,float posY)429 void CGUIControl::SetPosition(float posX, float posY)
430 {
431 if ((m_posX != posX) || (m_posY != posY))
432 {
433 MarkDirtyRegion();
434
435 m_hitRect += CPoint(posX - m_posX, posY - m_posY);
436 m_posX = posX;
437 m_posY = posY;
438
439 SetInvalid();
440 }
441 }
442
SetColorDiffuse(const GUIINFO::CGUIInfoColor & color)443 bool CGUIControl::SetColorDiffuse(const GUIINFO::CGUIInfoColor &color)
444 {
445 bool changed = m_diffuseColor != color;
446 m_diffuseColor = color;
447 return changed;
448 }
449
GetXPosition() const450 float CGUIControl::GetXPosition() const
451 {
452 return m_posX;
453 }
454
GetYPosition() const455 float CGUIControl::GetYPosition() const
456 {
457 return m_posY;
458 }
459
GetWidth() const460 float CGUIControl::GetWidth() const
461 {
462 return m_width;
463 }
464
GetHeight() const465 float CGUIControl::GetHeight() const
466 {
467 return m_height;
468 }
469
MarkDirtyRegion(const unsigned int dirtyState)470 void CGUIControl::MarkDirtyRegion(const unsigned int dirtyState)
471 {
472 if (!m_controlDirtyState && m_parentControl)
473 m_parentControl->MarkDirtyRegion(DIRTY_STATE_CHILD);
474
475 m_controlDirtyState |= dirtyState;
476 }
477
CalcRenderRegion() const478 CRect CGUIControl::CalcRenderRegion() const
479 {
480 CPoint tl(GetXPosition(), GetYPosition());
481 CPoint br(tl.x + GetWidth(), tl.y + GetHeight());
482
483 return CRect(tl.x, tl.y, br.x, br.y);
484 }
485
SetActions(const ActionMap & actions)486 void CGUIControl::SetActions(const ActionMap &actions)
487 {
488 m_actions = actions;
489 }
490
SetAction(int actionID,const CGUIAction & action,bool replace)491 void CGUIControl::SetAction(int actionID, const CGUIAction &action, bool replace /*= true*/)
492 {
493 ActionMap::iterator i = m_actions.find(actionID);
494 if (i == m_actions.end() || !i->second.HasAnyActions() || replace)
495 m_actions[actionID] = action;
496 }
497
SetWidth(float width)498 void CGUIControl::SetWidth(float width)
499 {
500 if (m_width != width)
501 {
502 MarkDirtyRegion();
503 m_width = width;
504 m_hitRect.x2 = m_hitRect.x1 + width;
505 SetInvalid();
506 }
507 }
508
SetHeight(float height)509 void CGUIControl::SetHeight(float height)
510 {
511 if (m_height != height)
512 {
513 MarkDirtyRegion();
514 m_height = height;
515 m_hitRect.y2 = m_hitRect.y1 + height;
516 SetInvalid();
517 }
518 }
519
SetVisible(bool bVisible,bool setVisState)520 void CGUIControl::SetVisible(bool bVisible, bool setVisState)
521 {
522 if (bVisible && setVisState)
523 { //! @todo currently we only update m_visible from GUI_MSG_VISIBLE (SET_CONTROL_VISIBLE)
524 //! otherwise we just set m_forceHidden
525 GUIVISIBLE visible;
526 if (m_visibleCondition)
527 visible = m_visibleCondition->Get() ? VISIBLE : HIDDEN;
528 else
529 visible = VISIBLE;
530 if (visible != m_visible)
531 {
532 m_visible = visible;
533 SetInvalid();
534 }
535 }
536 if (m_forceHidden == bVisible)
537 {
538 m_forceHidden = !bVisible;
539 SetInvalid();
540 if (m_forceHidden)
541 MarkDirtyRegion();
542 }
543 if (m_forceHidden)
544 { // reset any visible animations that are in process
545 if (IsAnimating(ANIM_TYPE_VISIBLE))
546 {
547 // CLog::Log(LOGDEBUG, "Resetting visible animation on control %i (we are %s)", m_controlID, m_visible ? "visible" : "hidden");
548 CAnimation *visibleAnim = GetAnimation(ANIM_TYPE_VISIBLE);
549 if (visibleAnim) visibleAnim->ResetAnimation();
550 }
551 }
552 }
553
HitTest(const CPoint & point) const554 bool CGUIControl::HitTest(const CPoint &point) const
555 {
556 return m_hitRect.PtInRect(point);
557 }
558
SendMouseEvent(const CPoint & point,const CMouseEvent & event)559 EVENT_RESULT CGUIControl::SendMouseEvent(const CPoint &point, const CMouseEvent &event)
560 {
561 CPoint childPoint(point);
562 m_transform.InverseTransformPosition(childPoint.x, childPoint.y);
563 if (!CanFocusFromPoint(childPoint))
564 return EVENT_RESULT_UNHANDLED;
565
566 bool handled = event.m_id != ACTION_MOUSE_MOVE || OnMouseOver(childPoint);
567 EVENT_RESULT ret = OnMouseEvent(childPoint, event);
568 if (ret)
569 return ret;
570 return (handled && (event.m_id == ACTION_MOUSE_MOVE)) ? EVENT_RESULT_HANDLED : EVENT_RESULT_UNHANDLED;
571 }
572
573 // override this function to implement custom mouse behaviour
OnMouseOver(const CPoint & point)574 bool CGUIControl::OnMouseOver(const CPoint &point)
575 {
576 if (CServiceBroker::GetInputManager().GetMouseState() != MOUSE_STATE_DRAG)
577 CServiceBroker::GetInputManager().SetMouseState(MOUSE_STATE_FOCUS);
578 if (!CanFocus()) return false;
579 if (!HasFocus())
580 {
581 CGUIMessage msg(GUI_MSG_SETFOCUS, GetParentID(), GetID());
582 OnMessage(msg);
583 }
584 return true;
585 }
586
UpdateVisibility(const CGUIListItem * item)587 void CGUIControl::UpdateVisibility(const CGUIListItem *item)
588 {
589 if (m_visibleCondition)
590 {
591 bool bWasVisible = m_visibleFromSkinCondition;
592 m_visibleFromSkinCondition = m_visibleCondition->Get(item);
593 if (!bWasVisible && m_visibleFromSkinCondition)
594 { // automatic change of visibility - queue the in effect
595 // CLog::Log(LOGDEBUG, "Visibility changed to visible for control id %i", m_controlID);
596 QueueAnimation(ANIM_TYPE_VISIBLE);
597 }
598 else if (bWasVisible && !m_visibleFromSkinCondition)
599 { // automatic change of visibility - do the out effect
600 // CLog::Log(LOGDEBUG, "Visibility changed to hidden for control id %i", m_controlID);
601 QueueAnimation(ANIM_TYPE_HIDDEN);
602 }
603 }
604 // check for conditional animations
605 for (unsigned int i = 0; i < m_animations.size(); i++)
606 {
607 CAnimation &anim = m_animations[i];
608 if (anim.GetType() == ANIM_TYPE_CONDITIONAL)
609 anim.UpdateCondition(item);
610 }
611 // and check for conditional enabling - note this overrides SetEnabled() from the code currently
612 // this may need to be reviewed at a later date
613 bool enabled = m_enabled;
614 if (m_enableCondition)
615 m_enabled = m_enableCondition->Get(item);
616
617 if (m_enabled != enabled)
618 MarkDirtyRegion();
619
620 m_allowHiddenFocus.Update(item);
621 if (UpdateColors())
622 MarkDirtyRegion();
623 // and finally, update our control information (if not pushed)
624 if (!m_pushedUpdates)
625 UpdateInfo(item);
626 }
627
UpdateColors()628 bool CGUIControl::UpdateColors()
629 {
630 return m_diffuseColor.Update();
631 }
632
SetInitialVisibility()633 void CGUIControl::SetInitialVisibility()
634 {
635 if (m_visibleCondition)
636 {
637 m_visibleFromSkinCondition = m_visibleCondition->Get();
638 m_visible = m_visibleFromSkinCondition ? VISIBLE : HIDDEN;
639 // CLog::Log(LOGDEBUG, "Set initial visibility for control %i: %s", m_controlID, m_visible == VISIBLE ? "visible" : "hidden");
640 }
641 else if (m_visible == DELAYED)
642 m_visible = VISIBLE;
643 // and handle animation conditions as well
644 for (unsigned int i = 0; i < m_animations.size(); i++)
645 {
646 CAnimation &anim = m_animations[i];
647 if (anim.GetType() == ANIM_TYPE_CONDITIONAL)
648 anim.SetInitialCondition();
649 }
650 // and check for conditional enabling - note this overrides SetEnabled() from the code currently
651 // this may need to be reviewed at a later date
652 if (m_enableCondition)
653 m_enabled = m_enableCondition->Get();
654 m_allowHiddenFocus.Update();
655 UpdateColors();
656
657 MarkDirtyRegion();
658 }
659
SetVisibleCondition(const std::string & expression,const std::string & allowHiddenFocus)660 void CGUIControl::SetVisibleCondition(const std::string &expression, const std::string &allowHiddenFocus)
661 {
662 if (expression == "true")
663 m_visible = VISIBLE;
664 else if (expression == "false")
665 m_visible = HIDDEN;
666 else // register with the infomanager for updates
667 m_visibleCondition = CServiceBroker::GetGUI()->GetInfoManager().Register(expression, GetParentID());
668 m_allowHiddenFocus.Parse(allowHiddenFocus, GetParentID());
669 }
670
SetAnimations(const std::vector<CAnimation> & animations)671 void CGUIControl::SetAnimations(const std::vector<CAnimation> &animations)
672 {
673 m_animations = animations;
674 MarkDirtyRegion();
675 }
676
ResetAnimation(ANIMATION_TYPE type)677 void CGUIControl::ResetAnimation(ANIMATION_TYPE type)
678 {
679 MarkDirtyRegion();
680
681 for (unsigned int i = 0; i < m_animations.size(); i++)
682 {
683 if (m_animations[i].GetType() == type)
684 m_animations[i].ResetAnimation();
685 }
686 }
687
ResetAnimations()688 void CGUIControl::ResetAnimations()
689 {
690 MarkDirtyRegion();
691
692 for (unsigned int i = 0; i < m_animations.size(); i++)
693 m_animations[i].ResetAnimation();
694
695 MarkDirtyRegion();
696 }
697
CheckAnimation(ANIMATION_TYPE animType)698 bool CGUIControl::CheckAnimation(ANIMATION_TYPE animType)
699 {
700 // rule out the animations we shouldn't perform
701 if (!IsVisible() || !HasProcessed())
702 { // hidden or never processed - don't allow exit or entry animations for this control
703 if (animType == ANIM_TYPE_WINDOW_CLOSE)
704 { // could be animating a (delayed) window open anim, so reset it
705 ResetAnimation(ANIM_TYPE_WINDOW_OPEN);
706 return false;
707 }
708 }
709 if (!IsVisible())
710 { // hidden - only allow hidden anims if we're animating a visible anim
711 if (animType == ANIM_TYPE_HIDDEN && !IsAnimating(ANIM_TYPE_VISIBLE))
712 {
713 // update states to force it hidden
714 UpdateStates(animType, ANIM_PROCESS_NORMAL, ANIM_STATE_APPLIED);
715 return false;
716 }
717 if (animType == ANIM_TYPE_WINDOW_OPEN)
718 return false;
719 }
720 return true;
721 }
722
QueueAnimation(ANIMATION_TYPE animType)723 void CGUIControl::QueueAnimation(ANIMATION_TYPE animType)
724 {
725 if (!CheckAnimation(animType))
726 return;
727
728 MarkDirtyRegion();
729
730 CAnimation *reverseAnim = GetAnimation((ANIMATION_TYPE)-animType, false);
731 CAnimation *forwardAnim = GetAnimation(animType);
732 // we first check whether the reverse animation is in progress (and reverse it)
733 // then we check for the normal animation, and queue it
734 if (reverseAnim && reverseAnim->IsReversible() && (reverseAnim->GetState() == ANIM_STATE_IN_PROCESS || reverseAnim->GetState() == ANIM_STATE_DELAYED))
735 {
736 reverseAnim->QueueAnimation(ANIM_PROCESS_REVERSE);
737 if (forwardAnim) forwardAnim->ResetAnimation();
738 }
739 else if (forwardAnim)
740 {
741 forwardAnim->QueueAnimation(ANIM_PROCESS_NORMAL);
742 if (reverseAnim) reverseAnim->ResetAnimation();
743 }
744 else
745 { // hidden and visible animations delay the change of state. If there is no animations
746 // to perform, then we should just change the state straightaway
747 if (reverseAnim) reverseAnim->ResetAnimation();
748 UpdateStates(animType, ANIM_PROCESS_NORMAL, ANIM_STATE_APPLIED);
749 }
750 }
751
GetAnimation(ANIMATION_TYPE type,bool checkConditions)752 CAnimation *CGUIControl::GetAnimation(ANIMATION_TYPE type, bool checkConditions /* = true */)
753 {
754 for (unsigned int i = 0; i < m_animations.size(); i++)
755 {
756 CAnimation &anim = m_animations[i];
757 if (anim.GetType() == type)
758 {
759 if (!checkConditions || anim.CheckCondition())
760 return &anim;
761 }
762 }
763 return NULL;
764 }
765
HasAnimation(ANIMATION_TYPE type)766 bool CGUIControl::HasAnimation(ANIMATION_TYPE type)
767 {
768 return (NULL != GetAnimation(type, true));
769 }
770
UpdateStates(ANIMATION_TYPE type,ANIMATION_PROCESS currentProcess,ANIMATION_STATE currentState)771 void CGUIControl::UpdateStates(ANIMATION_TYPE type, ANIMATION_PROCESS currentProcess, ANIMATION_STATE currentState)
772 {
773 // Make sure control is hidden or visible at the appropriate times
774 // while processing a visible or hidden animation it needs to be visible,
775 // but when finished a hidden operation it needs to be hidden
776 if (type == ANIM_TYPE_VISIBLE)
777 {
778 if (currentProcess == ANIM_PROCESS_REVERSE)
779 {
780 if (currentState == ANIM_STATE_APPLIED)
781 m_visible = HIDDEN;
782 }
783 else if (currentProcess == ANIM_PROCESS_NORMAL)
784 {
785 if (currentState == ANIM_STATE_DELAYED)
786 m_visible = DELAYED;
787 else
788 m_visible = m_visibleFromSkinCondition ? VISIBLE : HIDDEN;
789 }
790 }
791 else if (type == ANIM_TYPE_HIDDEN)
792 {
793 if (currentProcess == ANIM_PROCESS_NORMAL) // a hide animation
794 {
795 if (currentState == ANIM_STATE_APPLIED)
796 m_visible = HIDDEN; // finished
797 else
798 m_visible = VISIBLE; // have to be visible until we are finished
799 }
800 else if (currentProcess == ANIM_PROCESS_REVERSE) // a visible animation
801 { // no delay involved here - just make sure it's visible
802 m_visible = m_visibleFromSkinCondition ? VISIBLE : HIDDEN;
803 }
804 }
805 else if (type == ANIM_TYPE_WINDOW_OPEN)
806 {
807 if (currentProcess == ANIM_PROCESS_NORMAL)
808 {
809 if (currentState == ANIM_STATE_DELAYED)
810 m_visible = DELAYED; // delayed
811 else
812 m_visible = m_visibleFromSkinCondition ? VISIBLE : HIDDEN;
813 }
814 }
815 else if (type == ANIM_TYPE_FOCUS)
816 {
817 // call the focus function if we have finished a focus animation
818 // (buttons can "click" on focus)
819 if (currentProcess == ANIM_PROCESS_NORMAL && currentState == ANIM_STATE_APPLIED)
820 OnFocus();
821 }
822 else if (type == ANIM_TYPE_UNFOCUS)
823 {
824 // call the unfocus function if we have finished a focus animation
825 // (buttons can "click" on focus)
826 if (currentProcess == ANIM_PROCESS_NORMAL && currentState == ANIM_STATE_APPLIED)
827 OnUnFocus();
828 }
829 }
830
Animate(unsigned int currentTime)831 bool CGUIControl::Animate(unsigned int currentTime)
832 {
833 // check visible state outside the loop, as it could change
834 GUIVISIBLE visible = m_visible;
835
836 m_transform.Reset();
837 bool changed = false;
838
839 CPoint center(GetXPosition() + GetWidth() * 0.5f, GetYPosition() + GetHeight() * 0.5f);
840 for (unsigned int i = 0; i < m_animations.size(); i++)
841 {
842 CAnimation &anim = m_animations[i];
843 anim.Animate(currentTime, HasProcessed() || visible == DELAYED);
844 // Update the control states (such as visibility)
845 UpdateStates(anim.GetType(), anim.GetProcess(), anim.GetState());
846 // and render the animation effect
847 changed |= (anim.GetProcess() != ANIM_PROCESS_NONE);
848 anim.RenderAnimation(m_transform, center);
849
850 // debug stuff
851 //if (anim.GetProcess() != ANIM_PROCESS_NONE && IsVisible())
852 //{
853 // CLog::Log(LOGDEBUG, "Animating control %d", m_controlID);
854 //}
855 }
856
857 return changed;
858 }
859
IsAnimating(ANIMATION_TYPE animType)860 bool CGUIControl::IsAnimating(ANIMATION_TYPE animType)
861 {
862 for (unsigned int i = 0; i < m_animations.size(); i++)
863 {
864 CAnimation &anim = m_animations[i];
865 if (anim.GetType() == animType)
866 {
867 if (anim.GetQueuedProcess() == ANIM_PROCESS_NORMAL)
868 return true;
869 if (anim.GetProcess() == ANIM_PROCESS_NORMAL)
870 return true;
871 }
872 else if (anim.GetType() == -animType)
873 {
874 if (anim.GetQueuedProcess() == ANIM_PROCESS_REVERSE)
875 return true;
876 if (anim.GetProcess() == ANIM_PROCESS_REVERSE)
877 return true;
878 }
879 }
880 return false;
881 }
882
GetAction(int actionID) const883 CGUIAction CGUIControl::GetAction(int actionID) const
884 {
885 ActionMap::const_iterator i = m_actions.find(actionID);
886 if (i != m_actions.end())
887 return i->second;
888 return CGUIAction();
889 }
890
CanFocusFromPoint(const CPoint & point) const891 bool CGUIControl::CanFocusFromPoint(const CPoint &point) const
892 {
893 return CanFocus() && HitTest(point);
894 }
895
UnfocusFromPoint(const CPoint & point)896 void CGUIControl::UnfocusFromPoint(const CPoint &point)
897 {
898 if (HasFocus())
899 {
900 CPoint controlPoint(point);
901 m_transform.InverseTransformPosition(controlPoint.x, controlPoint.y);
902 if (!HitTest(controlPoint))
903 {
904 SetFocus(false);
905
906 // and tell our parent so it can unfocus
907 if (m_parentControl)
908 {
909 CGUIMessage msgLostFocus(GUI_MSG_LOSTFOCUS, GetID(), GetID());
910 m_parentControl->OnMessage(msgLostFocus);
911 }
912 }
913 }
914 }
915
SaveStates(std::vector<CControlState> & states)916 void CGUIControl::SaveStates(std::vector<CControlState> &states)
917 {
918 // empty for now - do nothing with the majority of controls
919 }
920
GetControl(int iControl,std::vector<CGUIControl * > * idCollector)921 CGUIControl *CGUIControl::GetControl(int iControl, std::vector<CGUIControl*> *idCollector)
922 {
923 return (iControl == m_controlID) ? this : nullptr;
924 }
925
926
UpdateControlStats()927 void CGUIControl::UpdateControlStats()
928 {
929 if (m_controlStats)
930 {
931 ++m_controlStats->nCountTotal;
932 if (IsVisible() && IsVisibleFromSkin())
933 ++m_controlStats->nCountVisible;
934 }
935 }
936
SetHitRect(const CRect & rect,const UTILS::Color & color)937 void CGUIControl::SetHitRect(const CRect &rect, const UTILS::Color &color)
938 {
939 m_hitRect = rect;
940 m_hitColor = color;
941 }
942
SetCamera(const CPoint & camera)943 void CGUIControl::SetCamera(const CPoint &camera)
944 {
945 m_camera = camera;
946 m_hasCamera = true;
947 }
948
GetRenderPosition() const949 CPoint CGUIControl::GetRenderPosition() const
950 {
951 float z = 0;
952 CPoint point(GetPosition());
953 m_transform.TransformPosition(point.x, point.y, z);
954 if (m_parentControl)
955 point += m_parentControl->GetRenderPosition();
956 return point;
957 }
958
SetStereoFactor(const float & factor)959 void CGUIControl::SetStereoFactor(const float &factor)
960 {
961 m_stereo = factor;
962 }
963