diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 18:07:22 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 18:07:22 +0000 |
commit | c04dcc2e7d834218ef2d4194331e383402495ae1 (patch) | |
tree | 7333e38d10d75386e60f336b80c2443c1166031d /xbmc/guilib/GUIControl.cpp | |
parent | Initial commit. (diff) | |
download | kodi-c04dcc2e7d834218ef2d4194331e383402495ae1.tar.xz kodi-c04dcc2e7d834218ef2d4194331e383402495ae1.zip |
Adding upstream version 2:20.4+dfsg.upstream/2%20.4+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'xbmc/guilib/GUIControl.cpp')
-rw-r--r-- | xbmc/guilib/GUIControl.cpp | 979 |
1 files changed, 979 insertions, 0 deletions
diff --git a/xbmc/guilib/GUIControl.cpp b/xbmc/guilib/GUIControl.cpp new file mode 100644 index 0000000..bd40475 --- /dev/null +++ b/xbmc/guilib/GUIControl.cpp @@ -0,0 +1,979 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "GUIControl.h" + +#include "GUIAction.h" +#include "GUIComponent.h" +#include "GUIControlProfiler.h" +#include "GUIInfoManager.h" +#include "GUIMessage.h" +#include "GUITexture.h" +#include "GUIWindowManager.h" +#include "ServiceBroker.h" +#include "input/InputManager.h" +#include "input/Key.h" +#include "input/mouse/MouseStat.h" +#include "utils/log.h" + +using namespace KODI::GUILIB; + +CGUIControl::CGUIControl() +{ + m_hasProcessed = false; + m_bHasFocus = false; + m_controlID = 0; + m_parentID = 0; + m_visible = VISIBLE; + m_visibleFromSkinCondition = true; + m_forceHidden = false; + m_enabled = true; + m_posX = 0; + m_posY = 0; + m_width = 0; + m_height = 0; + ControlType = GUICONTROL_UNKNOWN; + m_bInvalidated = true; + m_bAllocated=false; + m_parentControl = NULL; + m_hasCamera = false; + m_pushedUpdates = false; + m_pulseOnSelect = false; + m_controlDirtyState = DIRTY_STATE_CONTROL; + m_stereo = 0.0f; + m_controlStats = nullptr; +} + +CGUIControl::CGUIControl(int parentID, int controlID, float posX, float posY, float width, float height) +: m_hitRect(posX, posY, posX + width, posY + height), + m_diffuseColor(0xffffffff) +{ + m_posX = posX; + m_posY = posY; + m_width = width; + m_height = height; + m_bHasFocus = false; + m_controlID = controlID; + m_parentID = parentID; + m_visible = VISIBLE; + m_visibleFromSkinCondition = true; + m_forceHidden = false; + m_enabled = true; + ControlType = GUICONTROL_UNKNOWN; + m_bInvalidated = true; + m_bAllocated=false; + m_hasProcessed = false; + m_parentControl = NULL; + m_hasCamera = false; + m_pushedUpdates = false; + m_pulseOnSelect = false; + m_controlDirtyState = DIRTY_STATE_CONTROL; + m_stereo = 0.0f; + m_controlStats = nullptr; +} + +CGUIControl::CGUIControl(const CGUIControl &) = default; + +CGUIControl::~CGUIControl(void) = default; + +void CGUIControl::AllocResources() +{ + m_hasProcessed = false; + m_bInvalidated = true; + m_bAllocated=true; +} + +void CGUIControl::FreeResources(bool immediately) +{ + if (m_bAllocated) + { + // Reset our animation states - not conditional anims though. + // I'm not sure if this is needed for most cases anyway. I believe it's only here + // because some windows aren't loaded on demand + for (unsigned int i = 0; i < m_animations.size(); i++) + { + CAnimation &anim = m_animations[i]; + if (anim.GetType() != ANIM_TYPE_CONDITIONAL) + anim.ResetAnimation(); + } + m_bAllocated=false; + } + m_hasProcessed = false; +} + +void CGUIControl::DynamicResourceAlloc(bool bOnOff) +{ + +} + +// the main processing routine. +// 1. animate and set animation transform +// 2. if visible, process +// 3. reset the animation transform +void CGUIControl::DoProcess(unsigned int currentTime, CDirtyRegionList &dirtyregions) +{ + CRect dirtyRegion = m_renderRegion; + + bool changed = (m_controlDirtyState & DIRTY_STATE_CONTROL) != 0 || (m_bInvalidated && IsVisible()); + m_controlDirtyState = 0; + + if (Animate(currentTime)) + MarkDirtyRegion(); + + // if the control changed culling state from true to false, mark it + const bool culled = m_transform.alpha <= 0.01f; + if (m_isCulled != culled) + { + m_isCulled = false; + MarkDirtyRegion(); + } + m_isCulled = culled; + + if (IsVisible()) + { + m_cachedTransform = CServiceBroker::GetWinSystem()->GetGfxContext().AddTransform(m_transform); + if (m_hasCamera) + CServiceBroker::GetWinSystem()->GetGfxContext().SetCameraPosition(m_camera); + + Process(currentTime, dirtyregions); + m_bInvalidated = false; + + if (dirtyRegion != m_renderRegion) + { + dirtyRegion.Union(m_renderRegion); + changed = true; + } + + if (m_hasCamera) + CServiceBroker::GetWinSystem()->GetGfxContext().RestoreCameraPosition(); + CServiceBroker::GetWinSystem()->GetGfxContext().RemoveTransform(); + } + + UpdateControlStats(); + + changed |= (m_controlDirtyState & DIRTY_STATE_CONTROL) != 0; + + if (changed) + { + dirtyregions.emplace_back(dirtyRegion); + } +} + +void CGUIControl::Process(unsigned int currentTime, CDirtyRegionList &dirtyregions) +{ + // update our render region + m_renderRegion = CServiceBroker::GetWinSystem()->GetGfxContext().GenerateAABB(CalcRenderRegion()); + m_hasProcessed = true; +} + +// the main render routine. +// 1. set the animation transform +// 2. if visible, paint +// 3. reset the animation transform +void CGUIControl::DoRender() +{ + if (IsVisible() && !m_isCulled) + { + bool hasStereo = + m_stereo != 0.0f && + CServiceBroker::GetWinSystem()->GetGfxContext().GetStereoMode() != + RENDER_STEREO_MODE_MONO && + CServiceBroker::GetWinSystem()->GetGfxContext().GetStereoMode() != RENDER_STEREO_MODE_OFF; + + CServiceBroker::GetWinSystem()->GetGfxContext().SetTransform(m_cachedTransform); + if (m_hasCamera) + CServiceBroker::GetWinSystem()->GetGfxContext().SetCameraPosition(m_camera); + if (hasStereo) + CServiceBroker::GetWinSystem()->GetGfxContext().SetStereoFactor(m_stereo); + + GUIPROFILER_RENDER_BEGIN(this); + + if (m_hitColor != 0xffffffff) + { + UTILS::COLOR::Color color = + CServiceBroker::GetWinSystem()->GetGfxContext().MergeAlpha(m_hitColor); + CGUITexture::DrawQuad(CServiceBroker::GetWinSystem()->GetGfxContext().GenerateAABB(m_hitRect), color); + } + + Render(); + + GUIPROFILER_RENDER_END(this); + + if (hasStereo) + CServiceBroker::GetWinSystem()->GetGfxContext().RestoreStereoFactor(); + if (m_hasCamera) + CServiceBroker::GetWinSystem()->GetGfxContext().RestoreCameraPosition(); + CServiceBroker::GetWinSystem()->GetGfxContext().RemoveTransform(); + } +} + +bool CGUIControl::OnAction(const CAction &action) +{ + if (HasFocus()) + { + switch (action.GetID()) + { + case ACTION_MOVE_DOWN: + OnDown(); + return true; + + case ACTION_MOVE_UP: + OnUp(); + return true; + + case ACTION_MOVE_LEFT: + OnLeft(); + return true; + + case ACTION_MOVE_RIGHT: + OnRight(); + return true; + + case ACTION_SHOW_INFO: + return OnInfo(); + + case ACTION_NAV_BACK: + return OnBack(); + + case ACTION_NEXT_CONTROL: + OnNextControl(); + return true; + + case ACTION_PREV_CONTROL: + OnPrevControl(); + return true; + } + } + return false; +} + +bool CGUIControl::Navigate(int direction) const +{ + if (HasFocus()) + { + CGUIMessage msg(GUI_MSG_MOVE, GetParentID(), GetID(), direction); + return SendWindowMessage(msg); + } + return false; +} + +// Movement controls (derived classes can override) +void CGUIControl::OnUp() +{ + Navigate(ACTION_MOVE_UP); +} + +void CGUIControl::OnDown() +{ + Navigate(ACTION_MOVE_DOWN); +} + +void CGUIControl::OnLeft() +{ + Navigate(ACTION_MOVE_LEFT); +} + +void CGUIControl::OnRight() +{ + Navigate(ACTION_MOVE_RIGHT); +} + +bool CGUIControl::OnBack() +{ + return Navigate(ACTION_NAV_BACK); +} + +bool CGUIControl::OnInfo() +{ + CGUIAction action = GetAction(ACTION_SHOW_INFO); + if (action.HasAnyActions()) + return action.ExecuteActions(GetID(), GetParentID()); + return false; +} + +void CGUIControl::OnNextControl() +{ + Navigate(ACTION_NEXT_CONTROL); +} + +void CGUIControl::OnPrevControl() +{ + Navigate(ACTION_PREV_CONTROL); +} + +bool CGUIControl::SendWindowMessage(CGUIMessage &message) const +{ + CGUIWindow *pWindow = CServiceBroker::GetGUI()->GetWindowManager().GetWindow(GetParentID()); + if (pWindow) + return pWindow->OnMessage(message); + return CServiceBroker::GetGUI()->GetWindowManager().SendMessage(message); +} + +int CGUIControl::GetID(void) const +{ + return m_controlID; +} + + +int CGUIControl::GetParentID(void) const +{ + return m_parentID; +} + +bool CGUIControl::HasFocus(void) const +{ + return m_bHasFocus; +} + +void CGUIControl::SetFocus(bool focus) +{ + if (m_bHasFocus && !focus) + QueueAnimation(ANIM_TYPE_UNFOCUS); + else if (!m_bHasFocus && focus) + QueueAnimation(ANIM_TYPE_FOCUS); + m_bHasFocus = focus; +} + +bool CGUIControl::OnMessage(CGUIMessage& message) +{ + if ( message.GetControlId() == GetID() ) + { + switch (message.GetMessage() ) + { + case GUI_MSG_SETFOCUS: + // if control is disabled then move 2 the next control + if ( !CanFocus() ) + { + CLog::Log(LOGERROR, + "Control {} in window {} has been asked to focus, " + "but it can't", + GetID(), GetParentID()); + return false; + } + SetFocus(true); + { + // inform our parent window that this has happened + CGUIMessage message(GUI_MSG_FOCUSED, GetParentID(), GetID()); + if (m_parentControl) + m_parentControl->OnMessage(message); + } + return true; + break; + + case GUI_MSG_LOSTFOCUS: + { + SetFocus(false); + // and tell our parent so it can unfocus + if (m_parentControl) + m_parentControl->OnMessage(message); + return true; + } + break; + + case GUI_MSG_VISIBLE: + SetVisible(true, true); + return true; + break; + + case GUI_MSG_HIDDEN: + SetVisible(false); + return true; + + // Note that the skin <enable> tag will override these messages + case GUI_MSG_ENABLED: + SetEnabled(true); + return true; + + case GUI_MSG_DISABLED: + SetEnabled(false); + return true; + + case GUI_MSG_WINDOW_RESIZE: + // invalidate controls to get them to recalculate sizing information + SetInvalid(); + return true; + } + } + return false; +} + +bool CGUIControl::CanFocus() const +{ + if (!IsVisible() && !m_allowHiddenFocus) return false; + if (IsDisabled()) return false; + return true; +} + +bool CGUIControl::IsVisible() const +{ + if (m_forceHidden) + return false; + return m_visible == VISIBLE; +} + +bool CGUIControl::IsDisabled() const +{ + return !m_enabled; +} + +void CGUIControl::SetEnabled(bool bEnable) +{ + if (bEnable != m_enabled) + { + m_enabled = bEnable; + SetInvalid(); + } +} + +void CGUIControl::SetEnableCondition(const std::string &expression) +{ + if (expression == "true") + m_enabled = true; + else if (expression == "false") + m_enabled = false; + else + m_enableCondition = CServiceBroker::GetGUI()->GetInfoManager().Register(expression, GetParentID()); +} + +void CGUIControl::SetPosition(float posX, float posY) +{ + if ((m_posX != posX) || (m_posY != posY)) + { + MarkDirtyRegion(); + + m_hitRect += CPoint(posX - m_posX, posY - m_posY); + m_posX = posX; + m_posY = posY; + + SetInvalid(); + } +} + +bool CGUIControl::SetColorDiffuse(const GUIINFO::CGUIInfoColor &color) +{ + bool changed = m_diffuseColor != color; + m_diffuseColor = color; + return changed; +} + +float CGUIControl::GetXPosition() const +{ + return m_posX; +} + +float CGUIControl::GetYPosition() const +{ + return m_posY; +} + +float CGUIControl::GetWidth() const +{ + return m_width; +} + +float CGUIControl::GetHeight() const +{ + return m_height; +} + +void CGUIControl::MarkDirtyRegion(const unsigned int dirtyState) +{ + // if the control is culled, bail + if (dirtyState == DIRTY_STATE_CONTROL && m_isCulled) + return; + if (!m_controlDirtyState && m_parentControl) + m_parentControl->MarkDirtyRegion(DIRTY_STATE_CHILD); + + m_controlDirtyState |= dirtyState; +} + +CRect CGUIControl::CalcRenderRegion() const +{ + CPoint tl(GetXPosition(), GetYPosition()); + CPoint br(tl.x + GetWidth(), tl.y + GetHeight()); + + return CRect(tl.x, tl.y, br.x, br.y); +} + +void CGUIControl::SetActions(const ActionMap &actions) +{ + m_actions = actions; +} + +void CGUIControl::SetAction(int actionID, const CGUIAction &action, bool replace /*= true*/) +{ + ActionMap::iterator i = m_actions.find(actionID); + if (i == m_actions.end() || !i->second.HasAnyActions() || replace) + m_actions[actionID] = action; +} + +void CGUIControl::SetWidth(float width) +{ + if (m_width != width) + { + MarkDirtyRegion(); + m_width = width; + m_hitRect.x2 = m_hitRect.x1 + width; + SetInvalid(); + } +} + +void CGUIControl::SetHeight(float height) +{ + if (m_height != height) + { + MarkDirtyRegion(); + m_height = height; + m_hitRect.y2 = m_hitRect.y1 + height; + SetInvalid(); + } +} + +void CGUIControl::SetVisible(bool bVisible, bool setVisState) +{ + if (bVisible && setVisState) + { //! @todo currently we only update m_visible from GUI_MSG_VISIBLE (SET_CONTROL_VISIBLE) + //! otherwise we just set m_forceHidden + GUIVISIBLE visible; + if (m_visibleCondition) + visible = m_visibleCondition->Get(INFO::DEFAULT_CONTEXT) ? VISIBLE : HIDDEN; + else + visible = VISIBLE; + if (visible != m_visible) + { + m_visible = visible; + SetInvalid(); + } + } + if (m_forceHidden == bVisible) + { + m_forceHidden = !bVisible; + SetInvalid(); + if (m_forceHidden) + MarkDirtyRegion(); + } + if (m_forceHidden) + { // reset any visible animations that are in process + if (IsAnimating(ANIM_TYPE_VISIBLE)) + { + // CLog::Log(LOGDEBUG, "Resetting visible animation on control {} (we are {})", m_controlID, m_visible ? "visible" : "hidden"); + CAnimation *visibleAnim = GetAnimation(ANIM_TYPE_VISIBLE); + if (visibleAnim) visibleAnim->ResetAnimation(); + } + } +} + +bool CGUIControl::HitTest(const CPoint &point) const +{ + return m_hitRect.PtInRect(point); +} + +EVENT_RESULT CGUIControl::SendMouseEvent(const CPoint &point, const CMouseEvent &event) +{ + CPoint childPoint(point); + m_transform.InverseTransformPosition(childPoint.x, childPoint.y); + if (!CanFocusFromPoint(childPoint)) + return EVENT_RESULT_UNHANDLED; + + bool handled = event.m_id != ACTION_MOUSE_MOVE || OnMouseOver(childPoint); + EVENT_RESULT ret = OnMouseEvent(childPoint, event); + if (ret) + return ret; + return (handled && (event.m_id == ACTION_MOUSE_MOVE)) ? EVENT_RESULT_HANDLED : EVENT_RESULT_UNHANDLED; +} + +// override this function to implement custom mouse behaviour +bool CGUIControl::OnMouseOver(const CPoint &point) +{ + if (CServiceBroker::GetInputManager().GetMouseState() != MOUSE_STATE_DRAG) + CServiceBroker::GetInputManager().SetMouseState(MOUSE_STATE_FOCUS); + if (!CanFocus()) return false; + if (!HasFocus()) + { + CGUIMessage msg(GUI_MSG_SETFOCUS, GetParentID(), GetID()); + OnMessage(msg); + } + return true; +} + +void CGUIControl::UpdateVisibility(const CGUIListItem *item) +{ + if (m_visibleCondition) + { + bool bWasVisible = m_visibleFromSkinCondition; + m_visibleFromSkinCondition = m_visibleCondition->Get(INFO::DEFAULT_CONTEXT, item); + if (!bWasVisible && m_visibleFromSkinCondition) + { // automatic change of visibility - queue the in effect + // CLog::Log(LOGDEBUG, "Visibility changed to visible for control id {}", m_controlID); + QueueAnimation(ANIM_TYPE_VISIBLE); + } + else if (bWasVisible && !m_visibleFromSkinCondition) + { // automatic change of visibility - do the out effect + // CLog::Log(LOGDEBUG, "Visibility changed to hidden for control id {}", m_controlID); + QueueAnimation(ANIM_TYPE_HIDDEN); + } + } + // check for conditional animations + for (unsigned int i = 0; i < m_animations.size(); i++) + { + CAnimation &anim = m_animations[i]; + if (anim.GetType() == ANIM_TYPE_CONDITIONAL) + anim.UpdateCondition(item); + } + // and check for conditional enabling - note this overrides SetEnabled() from the code currently + // this may need to be reviewed at a later date + bool enabled = m_enabled; + if (m_enableCondition) + m_enabled = m_enableCondition->Get(INFO::DEFAULT_CONTEXT, item); + + if (m_enabled != enabled) + MarkDirtyRegion(); + + m_allowHiddenFocus.Update(INFO::DEFAULT_CONTEXT, item); + if (UpdateColors(item)) + MarkDirtyRegion(); + // and finally, update our control information (if not pushed) + if (!m_pushedUpdates) + UpdateInfo(item); +} + +bool CGUIControl::UpdateColors(const CGUIListItem* item) +{ + return m_diffuseColor.Update(item); +} + +void CGUIControl::SetInitialVisibility() +{ + if (m_visibleCondition) + { + m_visibleFromSkinCondition = m_visibleCondition->Get(INFO::DEFAULT_CONTEXT); + m_visible = m_visibleFromSkinCondition ? VISIBLE : HIDDEN; + // CLog::Log(LOGDEBUG, "Set initial visibility for control {}: {}", m_controlID, m_visible == VISIBLE ? "visible" : "hidden"); + } + else if (m_visible == DELAYED) + m_visible = VISIBLE; + // and handle animation conditions as well + for (unsigned int i = 0; i < m_animations.size(); i++) + { + CAnimation &anim = m_animations[i]; + if (anim.GetType() == ANIM_TYPE_CONDITIONAL) + anim.SetInitialCondition(); + } + // and check for conditional enabling - note this overrides SetEnabled() from the code currently + // this may need to be reviewed at a later date + if (m_enableCondition) + m_enabled = m_enableCondition->Get(INFO::DEFAULT_CONTEXT); + m_allowHiddenFocus.Update(INFO::DEFAULT_CONTEXT); + UpdateColors(nullptr); + + MarkDirtyRegion(); +} + +void CGUIControl::SetVisibleCondition(const std::string &expression, const std::string &allowHiddenFocus) +{ + if (expression == "true") + m_visible = VISIBLE; + else if (expression == "false") + m_visible = HIDDEN; + else // register with the infomanager for updates + m_visibleCondition = CServiceBroker::GetGUI()->GetInfoManager().Register(expression, GetParentID()); + m_allowHiddenFocus.Parse(allowHiddenFocus, GetParentID()); +} + +void CGUIControl::SetAnimations(const std::vector<CAnimation> &animations) +{ + m_animations = animations; + MarkDirtyRegion(); +} + +void CGUIControl::ResetAnimation(ANIMATION_TYPE type) +{ + MarkDirtyRegion(); + + for (unsigned int i = 0; i < m_animations.size(); i++) + { + if (m_animations[i].GetType() == type) + m_animations[i].ResetAnimation(); + } +} + +void CGUIControl::ResetAnimations() +{ + MarkDirtyRegion(); + + for (unsigned int i = 0; i < m_animations.size(); i++) + m_animations[i].ResetAnimation(); + + MarkDirtyRegion(); +} + +bool CGUIControl::CheckAnimation(ANIMATION_TYPE animType) +{ + // rule out the animations we shouldn't perform + if (!IsVisible() || !HasProcessed()) + { // hidden or never processed - don't allow exit or entry animations for this control + if (animType == ANIM_TYPE_WINDOW_CLOSE) + { // could be animating a (delayed) window open anim, so reset it + ResetAnimation(ANIM_TYPE_WINDOW_OPEN); + return false; + } + } + if (!IsVisible()) + { // hidden - only allow hidden anims if we're animating a visible anim + if (animType == ANIM_TYPE_HIDDEN && !IsAnimating(ANIM_TYPE_VISIBLE)) + { + // update states to force it hidden + UpdateStates(animType, ANIM_PROCESS_NORMAL, ANIM_STATE_APPLIED); + return false; + } + if (animType == ANIM_TYPE_WINDOW_OPEN) + return false; + } + return true; +} + +void CGUIControl::QueueAnimation(ANIMATION_TYPE animType) +{ + if (!CheckAnimation(animType)) + return; + + MarkDirtyRegion(); + + CAnimation *reverseAnim = GetAnimation((ANIMATION_TYPE)-animType, false); + CAnimation *forwardAnim = GetAnimation(animType); + // we first check whether the reverse animation is in progress (and reverse it) + // then we check for the normal animation, and queue it + if (reverseAnim && reverseAnim->IsReversible() && (reverseAnim->GetState() == ANIM_STATE_IN_PROCESS || reverseAnim->GetState() == ANIM_STATE_DELAYED)) + { + reverseAnim->QueueAnimation(ANIM_PROCESS_REVERSE); + if (forwardAnim) forwardAnim->ResetAnimation(); + } + else if (forwardAnim) + { + forwardAnim->QueueAnimation(ANIM_PROCESS_NORMAL); + if (reverseAnim) reverseAnim->ResetAnimation(); + } + else + { // hidden and visible animations delay the change of state. If there is no animations + // to perform, then we should just change the state straightaway + if (reverseAnim) reverseAnim->ResetAnimation(); + UpdateStates(animType, ANIM_PROCESS_NORMAL, ANIM_STATE_APPLIED); + } +} + +CAnimation *CGUIControl::GetAnimation(ANIMATION_TYPE type, bool checkConditions /* = true */) +{ + for (unsigned int i = 0; i < m_animations.size(); i++) + { + CAnimation &anim = m_animations[i]; + if (anim.GetType() == type) + { + if (!checkConditions || anim.CheckCondition()) + return &anim; + } + } + return NULL; +} + +bool CGUIControl::HasAnimation(ANIMATION_TYPE type) +{ + return (NULL != GetAnimation(type, true)); +} + +void CGUIControl::UpdateStates(ANIMATION_TYPE type, ANIMATION_PROCESS currentProcess, ANIMATION_STATE currentState) +{ + // Make sure control is hidden or visible at the appropriate times + // while processing a visible or hidden animation it needs to be visible, + // but when finished a hidden operation it needs to be hidden + if (type == ANIM_TYPE_VISIBLE) + { + if (currentProcess == ANIM_PROCESS_REVERSE) + { + if (currentState == ANIM_STATE_APPLIED) + m_visible = HIDDEN; + } + else if (currentProcess == ANIM_PROCESS_NORMAL) + { + if (currentState == ANIM_STATE_DELAYED) + m_visible = DELAYED; + else + m_visible = m_visibleFromSkinCondition ? VISIBLE : HIDDEN; + } + } + else if (type == ANIM_TYPE_HIDDEN) + { + if (currentProcess == ANIM_PROCESS_NORMAL) // a hide animation + { + if (currentState == ANIM_STATE_APPLIED) + m_visible = HIDDEN; // finished + else + m_visible = VISIBLE; // have to be visible until we are finished + } + else if (currentProcess == ANIM_PROCESS_REVERSE) // a visible animation + { // no delay involved here - just make sure it's visible + m_visible = m_visibleFromSkinCondition ? VISIBLE : HIDDEN; + } + } + else if (type == ANIM_TYPE_WINDOW_OPEN) + { + if (currentProcess == ANIM_PROCESS_NORMAL) + { + if (currentState == ANIM_STATE_DELAYED) + m_visible = DELAYED; // delayed + else + m_visible = m_visibleFromSkinCondition ? VISIBLE : HIDDEN; + } + } + else if (type == ANIM_TYPE_FOCUS) + { + // call the focus function if we have finished a focus animation + // (buttons can "click" on focus) + if (currentProcess == ANIM_PROCESS_NORMAL && currentState == ANIM_STATE_APPLIED) + OnFocus(); + } + else if (type == ANIM_TYPE_UNFOCUS) + { + // call the unfocus function if we have finished a focus animation + // (buttons can "click" on focus) + if (currentProcess == ANIM_PROCESS_NORMAL && currentState == ANIM_STATE_APPLIED) + OnUnFocus(); + } +} + +bool CGUIControl::Animate(unsigned int currentTime) +{ + // check visible state outside the loop, as it could change + GUIVISIBLE visible = m_visible; + + m_transform.Reset(); + bool changed = false; + + CPoint center(GetXPosition() + GetWidth() * 0.5f, GetYPosition() + GetHeight() * 0.5f); + for (unsigned int i = 0; i < m_animations.size(); i++) + { + CAnimation &anim = m_animations[i]; + anim.Animate(currentTime, HasProcessed() || visible == DELAYED); + // Update the control states (such as visibility) + UpdateStates(anim.GetType(), anim.GetProcess(), anim.GetState()); + // and render the animation effect + changed |= (anim.GetProcess() != ANIM_PROCESS_NONE); + anim.RenderAnimation(m_transform, center); + + // debug stuff + //if (anim.GetProcess() != ANIM_PROCESS_NONE && IsVisible()) + //{ + // CLog::Log(LOGDEBUG, "Animating control {}", m_controlID); + //} + } + + return changed; +} + +bool CGUIControl::IsAnimating(ANIMATION_TYPE animType) +{ + for (unsigned int i = 0; i < m_animations.size(); i++) + { + CAnimation &anim = m_animations[i]; + if (anim.GetType() == animType) + { + if (anim.GetQueuedProcess() == ANIM_PROCESS_NORMAL) + return true; + if (anim.GetProcess() == ANIM_PROCESS_NORMAL) + return true; + } + else if (anim.GetType() == -animType) + { + if (anim.GetQueuedProcess() == ANIM_PROCESS_REVERSE) + return true; + if (anim.GetProcess() == ANIM_PROCESS_REVERSE) + return true; + } + } + return false; +} + +CGUIAction CGUIControl::GetAction(int actionID) const +{ + ActionMap::const_iterator i = m_actions.find(actionID); + if (i != m_actions.end()) + return i->second; + return CGUIAction(); +} + +bool CGUIControl::CanFocusFromPoint(const CPoint &point) const +{ + return CanFocus() && HitTest(point); +} + +void CGUIControl::UnfocusFromPoint(const CPoint &point) +{ + if (HasFocus()) + { + CPoint controlPoint(point); + m_transform.InverseTransformPosition(controlPoint.x, controlPoint.y); + if (!HitTest(controlPoint)) + { + SetFocus(false); + + // and tell our parent so it can unfocus + if (m_parentControl) + { + CGUIMessage msgLostFocus(GUI_MSG_LOSTFOCUS, GetID(), GetID()); + m_parentControl->OnMessage(msgLostFocus); + } + } + } +} + +void CGUIControl::SaveStates(std::vector<CControlState> &states) +{ + // empty for now - do nothing with the majority of controls +} + +CGUIControl *CGUIControl::GetControl(int iControl, std::vector<CGUIControl*> *idCollector) +{ + return (iControl == m_controlID) ? this : nullptr; +} + + +void CGUIControl::UpdateControlStats() +{ + if (m_controlStats) + { + ++m_controlStats->nCountTotal; + if (IsVisible() && IsVisibleFromSkin()) + ++m_controlStats->nCountVisible; + } +} + +void CGUIControl::SetHitRect(const CRect& rect, const UTILS::COLOR::Color& color) +{ + m_hitRect = rect; + m_hitColor = color; +} + +void CGUIControl::SetCamera(const CPoint &camera) +{ + m_camera = camera; + m_hasCamera = true; +} + +CPoint CGUIControl::GetRenderPosition() const +{ + float z = 0; + CPoint point(GetPosition()); + m_transform.TransformPosition(point.x, point.y, z); + if (m_parentControl) + point += m_parentControl->GetRenderPosition(); + return point; +} + +void CGUIControl::SetStereoFactor(const float &factor) +{ + m_stereo = factor; +} |