/* * 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 "GUIButtonControl.h" #include "GUIFontManager.h" #include "input/Key.h" CGUIButtonControl::CGUIButtonControl(int parentID, int controlID, float posX, float posY, float width, float height, const CTextureInfo& textureFocus, const CTextureInfo& textureNoFocus, const CLabelInfo& labelInfo, bool wrapMultiline) : CGUIControl(parentID, controlID, posX, posY, width, height), m_imgFocus(CGUITexture::CreateTexture(posX, posY, width, height, textureFocus)), m_imgNoFocus(CGUITexture::CreateTexture(posX, posY, width, height, textureNoFocus)), m_label(posX, posY, width, height, labelInfo, wrapMultiline ? CGUILabel::OVER_FLOW_WRAP : CGUILabel::OVER_FLOW_TRUNCATE), m_label2(posX, posY, width, height, labelInfo) { m_bSelected = false; m_alpha = 255; m_focusCounter = 0; m_minWidth = 0; m_maxWidth = width; ControlType = GUICONTROL_BUTTON; } CGUIButtonControl::CGUIButtonControl(const CGUIButtonControl& control) : CGUIControl(control), m_imgFocus(control.m_imgFocus->Clone()), m_imgNoFocus(control.m_imgNoFocus->Clone()), m_focusCounter(control.m_focusCounter), m_alpha(control.m_alpha), m_minWidth(control.m_minWidth), m_maxWidth(control.m_maxWidth), m_info(control.m_info), m_info2(control.m_info2), m_label(control.m_label), m_label2(control.m_label2), m_clickActions(control.m_clickActions), m_focusActions(control.m_focusActions), m_unfocusActions(control.m_unfocusActions), m_bSelected(control.m_bSelected) { } void CGUIButtonControl::Process(unsigned int currentTime, CDirtyRegionList &dirtyregions) { ProcessText(currentTime); if (m_bInvalidated) { m_imgFocus->SetWidth(GetWidth()); m_imgFocus->SetHeight(m_height); m_imgNoFocus->SetWidth(GetWidth()); m_imgNoFocus->SetHeight(m_height); } if (HasFocus()) { unsigned int alphaChannel = m_alpha; if (m_pulseOnSelect) { unsigned int alphaCounter = m_focusCounter + 2; if ((alphaCounter % 128) >= 64) alphaChannel = alphaCounter % 64; else alphaChannel = 63 - (alphaCounter % 64); alphaChannel += 192; alphaChannel = (unsigned int)((float)m_alpha * (float)alphaChannel / 255.0f); } if (m_imgFocus->SetAlpha((unsigned char)alphaChannel)) MarkDirtyRegion(); m_imgFocus->SetVisible(true); m_imgNoFocus->SetVisible(false); m_focusCounter++; } else { m_imgFocus->SetVisible(false); m_imgNoFocus->SetVisible(true); } m_imgFocus->Process(currentTime); m_imgNoFocus->Process(currentTime); CGUIControl::Process(currentTime, dirtyregions); } void CGUIButtonControl::Render() { m_imgFocus->Render(); m_imgNoFocus->Render(); RenderText(); CGUIControl::Render(); } void CGUIButtonControl::RenderText() { m_label.Render(); m_label2.Render(); } CGUILabel::COLOR CGUIButtonControl::GetTextColor() const { if (IsDisabled()) return CGUILabel::COLOR_DISABLED; if (HasFocus()) return CGUILabel::COLOR_FOCUSED; return CGUILabel::COLOR_TEXT; } #define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) float CGUIButtonControl::GetWidth() const { if (m_minWidth && m_minWidth != m_width) { float txtWidth = m_label.GetTextWidth() + 2 * m_label.GetLabelInfo().offsetX; if (m_label2.GetTextWidth()) { static const float min_space = 10; txtWidth += m_label2.GetTextWidth() + 2 * m_label2.GetLabelInfo().offsetX + min_space; } float maxWidth = m_maxWidth ? m_maxWidth : txtWidth; return CLAMP(txtWidth, m_minWidth, maxWidth); } return m_width; } void CGUIButtonControl::SetMinWidth(float minWidth) { if (m_minWidth != minWidth) MarkDirtyRegion(); m_minWidth = minWidth; } void CGUIButtonControl::ProcessText(unsigned int currentTime) { CRect labelRenderRect = m_label.GetRenderRect(); CRect label2RenderRect = m_label2.GetRenderRect(); float renderWidth = GetWidth(); float renderTextWidth = renderWidth; if (m_labelMaxWidth > 0 && m_labelMaxWidth < renderWidth) renderTextWidth = m_labelMaxWidth; bool changed = m_label.SetMaxRect(m_posX, m_posY, renderTextWidth, m_height); changed |= m_label.SetText(m_info.GetLabel(m_parentID)); changed |= m_label.SetScrolling(HasFocus()); changed |= m_label2.SetMaxRect(m_posX, m_posY, renderTextWidth, m_height); changed |= m_label2.SetText(m_info2.GetLabel(m_parentID)); // text changed - images need resizing if (m_minWidth && (m_label.GetRenderRect() != labelRenderRect)) SetInvalid(); // auto-width - adjust hitrect if (m_minWidth && m_width != renderWidth) { CRect rect{m_posX, m_posY, m_posX + renderWidth, m_posY + m_height}; SetHitRect(rect, m_hitColor); } // render the second label if it exists if (!m_info2.GetLabel(m_parentID).empty()) { changed |= m_label2.SetAlign(XBFONT_RIGHT | (m_label.GetLabelInfo().align & XBFONT_CENTER_Y) | XBFONT_TRUNCATED); changed |= m_label2.SetScrolling(HasFocus()); // If overlapping was corrected - compare render rects to determine // if they changed since last frame. if (CGUILabel::CheckAndCorrectOverlap(m_label, m_label2)) changed |= (m_label.GetRenderRect() != labelRenderRect || m_label2.GetRenderRect() != label2RenderRect); changed |= m_label2.SetColor(GetTextColor()); changed |= m_label2.Process(currentTime); } changed |= m_label.SetColor(GetTextColor()); changed |= m_label.Process(currentTime); if (changed) MarkDirtyRegion(); } bool CGUIButtonControl::OnAction(const CAction &action) { if (action.GetID() == ACTION_SELECT_ITEM) { OnClick(); return true; } return CGUIControl::OnAction(action); } bool CGUIButtonControl::OnMessage(CGUIMessage& message) { if (message.GetControlId() == GetID()) { if (message.GetMessage() == GUI_MSG_LABEL_SET) { SetLabel(message.GetLabel()); return true; } if (message.GetMessage() == GUI_MSG_LABEL2_SET) { SetLabel2(message.GetLabel()); return true; } if (message.GetMessage() == GUI_MSG_IS_SELECTED) { message.SetParam1(m_bSelected ? 1 : 0); return true; } if (message.GetMessage() == GUI_MSG_SET_SELECTED) { if (!m_bSelected) SetInvalid(); m_bSelected = true; return true; } if (message.GetMessage() == GUI_MSG_SET_DESELECTED) { if (m_bSelected) SetInvalid(); m_bSelected = false; return true; } } return CGUIControl::OnMessage(message); } void CGUIButtonControl::AllocResources() { CGUIControl::AllocResources(); m_focusCounter = 0; m_imgFocus->AllocResources(); m_imgNoFocus->AllocResources(); if (!m_width) m_width = m_imgFocus->GetWidth(); if (!m_height) m_height = m_imgFocus->GetHeight(); } void CGUIButtonControl::FreeResources(bool immediately) { CGUIControl::FreeResources(immediately); m_imgFocus->FreeResources(immediately); m_imgNoFocus->FreeResources(immediately); } void CGUIButtonControl::DynamicResourceAlloc(bool bOnOff) { CGUIControl::DynamicResourceAlloc(bOnOff); m_imgFocus->DynamicResourceAlloc(bOnOff); m_imgNoFocus->DynamicResourceAlloc(bOnOff); } void CGUIButtonControl::SetInvalid() { CGUIControl::SetInvalid(); m_label.SetInvalid(); m_label2.SetInvalid(); m_imgFocus->SetInvalid(); m_imgNoFocus->SetInvalid(); } void CGUIButtonControl::SetLabel(const std::string &label) { // NOTE: No fallback for buttons at this point if (m_info.GetLabel(GetParentID(), false) != label) { m_info.SetLabel(label, "", GetParentID()); SetInvalid(); } } void CGUIButtonControl::SetLabel2(const std::string &label2) { // NOTE: No fallback for buttons at this point if (m_info2.GetLabel(GetParentID(), false) != label2) { m_info2.SetLabel(label2, "", GetParentID()); SetInvalid(); } } void CGUIButtonControl::SetPosition(float posX, float posY) { CGUIControl::SetPosition(posX, posY); m_imgFocus->SetPosition(posX, posY); m_imgNoFocus->SetPosition(posX, posY); } void CGUIButtonControl::SetAlpha(unsigned char alpha) { if (m_alpha != alpha) MarkDirtyRegion(); m_alpha = alpha; } bool CGUIButtonControl::UpdateColors(const CGUIListItem* item) { bool changed = CGUIControl::UpdateColors(nullptr); changed |= m_label.UpdateColors(); changed |= m_label2.UpdateColors(); changed |= m_imgFocus->SetDiffuseColor(m_diffuseColor); changed |= m_imgNoFocus->SetDiffuseColor(m_diffuseColor); return changed; } CRect CGUIButtonControl::CalcRenderRegion() const { CRect buttonRect = CGUIControl::CalcRenderRegion(); CRect textRect = m_label.GetRenderRect(); buttonRect.Union(textRect); return buttonRect; } EVENT_RESULT CGUIButtonControl::OnMouseEvent(const CPoint &point, const CMouseEvent &event) { if (event.m_id == ACTION_MOUSE_LEFT_CLICK) { OnAction(CAction(ACTION_SELECT_ITEM)); return EVENT_RESULT_HANDLED; } return EVENT_RESULT_UNHANDLED; } std::string CGUIButtonControl::GetDescription() const { std::string strLabel(m_info.GetLabel(m_parentID)); return strLabel; } std::string CGUIButtonControl::GetLabel2() const { std::string strLabel(m_info2.GetLabel(m_parentID)); return strLabel; } void CGUIButtonControl::PythonSetLabel(const std::string& strFont, const std::string& strText, UTILS::COLOR::Color textColor, UTILS::COLOR::Color shadowColor, UTILS::COLOR::Color focusedColor) { m_label.GetLabelInfo().font = g_fontManager.GetFont(strFont); m_label.GetLabelInfo().textColor = textColor; m_label.GetLabelInfo().focusedColor = focusedColor; m_label.GetLabelInfo().shadowColor = shadowColor; SetLabel(strText); } void CGUIButtonControl::PythonSetDisabledColor(UTILS::COLOR::Color disabledColor) { m_label.GetLabelInfo().disabledColor = disabledColor; } void CGUIButtonControl::OnClick() { // Save values, as the click message may deactivate the window int controlID = GetID(); int parentID = GetParentID(); CGUIAction clickActions = m_clickActions; // button selected, send a message CGUIMessage msg(GUI_MSG_CLICKED, controlID, parentID, 0); SendWindowMessage(msg); clickActions.ExecuteActions(controlID, parentID); } void CGUIButtonControl::OnFocus() { m_focusActions.ExecuteActions(GetID(), GetParentID()); } void CGUIButtonControl::OnUnFocus() { m_unfocusActions.ExecuteActions(GetID(), GetParentID()); } void CGUIButtonControl::SetSelected(bool bSelected) { if (m_bSelected != bSelected) { m_bSelected = bSelected; SetInvalid(); } }