/* * 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 "GUISpinControl.h" #include "GUIMessage.h" #include "input/Key.h" #include "utils/StringUtils.h" #include #define SPIN_BUTTON_DOWN 1 #define SPIN_BUTTON_UP 2 namespace { // Additional space between text and spin buttons constexpr float TEXT_SPACE = 5.0f; } // unnamed namespace CGUISpinControl::CGUISpinControl(int parentID, int controlID, float posX, float posY, float width, float height, const CTextureInfo& textureUp, const CTextureInfo& textureDown, const CTextureInfo& textureUpFocus, const CTextureInfo& textureDownFocus, const CTextureInfo& textureUpDisabled, const CTextureInfo& textureDownDisabled, const CLabelInfo& labelInfo, int iType) : CGUIControl(parentID, controlID, posX, posY, width, height), m_imgspinUp(CGUITexture::CreateTexture(posX, posY, width, height, textureUp)), m_imgspinDown(CGUITexture::CreateTexture(posX, posY, width, height, textureDown)), m_imgspinUpFocus(CGUITexture::CreateTexture(posX, posY, width, height, textureUpFocus)), m_imgspinDownFocus(CGUITexture::CreateTexture(posX, posY, width, height, textureDownFocus)), m_imgspinUpDisabled(CGUITexture::CreateTexture(posX, posY, width, height, textureUpDisabled)), m_imgspinDownDisabled( CGUITexture::CreateTexture(posX, posY, width, height, textureDownDisabled)), m_label(posX, posY, width, height, labelInfo) { m_bReverse = false; m_iStart = 0; m_iEnd = 100; m_fStart = 0.0f; m_fEnd = 1.0f; m_fInterval = 0.1f; m_iValue = 0; m_fValue = 0.0; m_iType = iType; m_iSelect = SPIN_BUTTON_DOWN; m_bShowRange = false; m_iTypedPos = 0; strcpy(m_szTyped, ""); ControlType = GUICONTROL_SPIN; m_currentItem = 0; m_numItems = 10; m_itemsPerPage = 10; m_showOnePage = true; } CGUISpinControl::CGUISpinControl(const CGUISpinControl& control) : CGUIControl(control), m_iStart(control.m_iStart), m_iEnd(control.m_iEnd), m_fStart(control.m_fStart), m_fEnd(control.m_fEnd), m_iValue(control.m_iValue), m_fValue(control.m_fValue), m_iType(control.m_iType), m_iSelect(control.m_iSelect), m_bReverse(control.m_bReverse), m_fInterval(control.m_fInterval), m_vecLabels(control.m_vecLabels), m_vecValues(control.m_vecValues), m_vecStrValues(control.m_vecStrValues), m_imgspinUp(control.m_imgspinUp->Clone()), m_imgspinDown(control.m_imgspinDown->Clone()), m_imgspinUpFocus(control.m_imgspinUpFocus->Clone()), m_imgspinDownFocus(control.m_imgspinDownFocus->Clone()), m_imgspinUpDisabled(control.m_imgspinUpDisabled->Clone()), m_imgspinDownDisabled(control.m_imgspinDownDisabled->Clone()), m_label(control.m_label), m_bShowRange(control.m_bShowRange), m_iTypedPos(control.m_iTypedPos), m_currentItem(control.m_currentItem), m_itemsPerPage(control.m_itemsPerPage), m_numItems(control.m_numItems), m_showOnePage(control.m_showOnePage) { std::strcpy(m_szTyped, control.m_szTyped); } bool CGUISpinControl::OnAction(const CAction &action) { switch (action.GetID()) { case REMOTE_0: case REMOTE_1: case REMOTE_2: case REMOTE_3: case REMOTE_4: case REMOTE_5: case REMOTE_6: case REMOTE_7: case REMOTE_8: case REMOTE_9: { if (strlen(m_szTyped) >= 3) { m_iTypedPos = 0; strcpy(m_szTyped, ""); } int iNumber = action.GetID() - REMOTE_0; m_szTyped[m_iTypedPos] = iNumber + '0'; m_iTypedPos++; m_szTyped[m_iTypedPos] = 0; int iValue; sscanf(m_szTyped, "%i", &iValue); switch (m_iType) { case SPIN_CONTROL_TYPE_INT: { if (iValue < m_iStart || iValue > m_iEnd) { m_iTypedPos = 0; m_szTyped[m_iTypedPos] = iNumber + '0'; m_iTypedPos++; m_szTyped[m_iTypedPos] = 0; sscanf(m_szTyped, "%i", &iValue); if (iValue < m_iStart || iValue > m_iEnd) { m_iTypedPos = 0; strcpy(m_szTyped, ""); return true; } } m_iValue = iValue; CGUIMessage msg(GUI_MSG_CLICKED, GetID(), GetParentID()); SendWindowMessage(msg); } break; case SPIN_CONTROL_TYPE_TEXT: { if (iValue < 0 || iValue >= (int)m_vecLabels.size()) { m_iTypedPos = 0; m_szTyped[m_iTypedPos] = iNumber + '0'; m_iTypedPos++; m_szTyped[m_iTypedPos] = 0; sscanf(m_szTyped, "%i", &iValue); if (iValue < 0 || iValue >= (int)m_vecLabels.size()) { m_iTypedPos = 0; strcpy(m_szTyped, ""); return true; } } m_iValue = iValue; CGUIMessage msg(GUI_MSG_CLICKED, GetID(), GetParentID()); SendWindowMessage(msg); } break; } return true; } break; case ACTION_PAGE_UP: if (!m_bReverse) PageDown(); else PageUp(); return true; break; case ACTION_PAGE_DOWN: if (!m_bReverse) PageUp(); else PageDown(); return true; break; case ACTION_SELECT_ITEM: if (m_iSelect == SPIN_BUTTON_UP) { MoveUp(); return true; } if (m_iSelect == SPIN_BUTTON_DOWN) { MoveDown(); return true; } break; } /* static float m_fSmoothScrollOffset = 0.0f; if (action.GetID() == ACTION_SCROLL_UP) { m_fSmoothScrollOffset += action.GetAmount() * action.GetAmount(); bool handled = false; while (m_fSmoothScrollOffset > 0.4) { handled = true; m_fSmoothScrollOffset -= 0.4f; MoveDown(); } return handled; }*/ return CGUIControl::OnAction(action); } void CGUISpinControl::OnLeft() { if (m_iSelect == SPIN_BUTTON_UP) { // select the down button m_iSelect = SPIN_BUTTON_DOWN; MarkDirtyRegion(); } else { // base class CGUIControl::OnLeft(); } } void CGUISpinControl::OnRight() { if (m_iSelect == SPIN_BUTTON_DOWN) { // select the up button m_iSelect = SPIN_BUTTON_UP; MarkDirtyRegion(); } else { // base class CGUIControl::OnRight(); } } void CGUISpinControl::Clear() { m_vecLabels.clear(); m_vecValues.clear(); m_vecStrValues.clear(); SetValue(0); } bool CGUISpinControl::OnMessage(CGUIMessage& message) { if (CGUIControl::OnMessage(message) ) return true; if (message.GetControlId() == GetID() ) { switch (message.GetMessage()) { case GUI_MSG_ITEM_SELECT: if (SPIN_CONTROL_TYPE_PAGE == m_iType) { m_currentItem = message.GetParam1(); return true; } SetValue( message.GetParam1()); if (message.GetParam2() == SPIN_BUTTON_DOWN || message.GetParam2() == SPIN_BUTTON_UP) m_iSelect = message.GetParam2(); return true; break; case GUI_MSG_LABEL_RESET: if (SPIN_CONTROL_TYPE_PAGE == m_iType) { m_itemsPerPage = message.GetParam1(); m_numItems = message.GetParam2(); return true; } { Clear(); return true; } break; case GUI_MSG_SHOWRANGE: if (message.GetParam1() ) m_bShowRange = true; else m_bShowRange = false; break; case GUI_MSG_SET_LABELS: if (message.GetPointer()) { auto labels = static_cast>*>(message.GetPointer()); Clear(); for (const auto& i : *labels) AddLabel(i.first, i.second); SetValue( message.GetParam1()); } break; case GUI_MSG_LABEL_ADD: { AddLabel(message.GetLabel(), message.GetParam1()); return true; } break; case GUI_MSG_ITEM_SELECTED: { message.SetParam1( GetValue() ); message.SetParam2(m_iSelect); if (m_iType == SPIN_CONTROL_TYPE_TEXT) { if ( m_iValue >= 0 && m_iValue < (int)m_vecLabels.size() ) message.SetLabel( m_vecLabels[m_iValue]); } return true; } case GUI_MSG_PAGE_UP: if (CanMoveUp()) MoveUp(); return true; case GUI_MSG_PAGE_DOWN: if (CanMoveDown()) MoveDown(); return true; case GUI_MSG_MOVE_OFFSET: { int count = message.GetParam1(); while (count < 0) { MoveUp(); count++; } while (count > 0) { MoveDown(); count--; } return true; } } } return false; } void CGUISpinControl::AllocResources() { CGUIControl::AllocResources(); m_imgspinUp->AllocResources(); m_imgspinUpFocus->AllocResources(); m_imgspinDown->AllocResources(); m_imgspinDownFocus->AllocResources(); m_imgspinUpDisabled->AllocResources(); m_imgspinDownDisabled->AllocResources(); m_imgspinDownFocus->SetPosition(m_posX, m_posY); m_imgspinDown->SetPosition(m_posX, m_posY); m_imgspinDownDisabled->SetPosition(m_posX, m_posY); m_imgspinUp->SetPosition(m_posX + m_imgspinDown->GetWidth(), m_posY); m_imgspinUpFocus->SetPosition(m_posX + m_imgspinDownFocus->GetWidth(), m_posY); m_imgspinUpDisabled->SetPosition(m_posX + m_imgspinDownDisabled->GetWidth(), m_posY); } void CGUISpinControl::FreeResources(bool immediately) { CGUIControl::FreeResources(immediately); m_imgspinUp->FreeResources(immediately); m_imgspinUpFocus->FreeResources(immediately); m_imgspinDown->FreeResources(immediately); m_imgspinDownFocus->FreeResources(immediately); m_imgspinUpDisabled->FreeResources(immediately); m_imgspinDownDisabled->FreeResources(immediately); m_iTypedPos = 0; strcpy(m_szTyped, ""); } void CGUISpinControl::DynamicResourceAlloc(bool bOnOff) { CGUIControl::DynamicResourceAlloc(bOnOff); m_imgspinUp->DynamicResourceAlloc(bOnOff); m_imgspinUpFocus->DynamicResourceAlloc(bOnOff); m_imgspinDown->DynamicResourceAlloc(bOnOff); m_imgspinDownFocus->DynamicResourceAlloc(bOnOff); m_imgspinUpDisabled->DynamicResourceAlloc(bOnOff); m_imgspinDownDisabled->DynamicResourceAlloc(bOnOff); } void CGUISpinControl::SetInvalid() { CGUIControl::SetInvalid(); m_label.SetInvalid(); m_imgspinUp->SetInvalid(); m_imgspinUpFocus->SetInvalid(); m_imgspinDown->SetInvalid(); m_imgspinDownFocus->SetInvalid(); m_imgspinUpDisabled->SetInvalid(); m_imgspinDownDisabled->SetInvalid(); } void CGUISpinControl::Process(unsigned int currentTime, CDirtyRegionList &dirtyregions) { bool changed = false; if (!HasFocus()) { m_iTypedPos = 0; strcpy(m_szTyped, ""); } std::string text; if (m_iType == SPIN_CONTROL_TYPE_INT) { if (m_bShowRange) { text = StringUtils::Format("{}/{}", m_iValue, m_iEnd); } else { text = std::to_string(m_iValue); } } else if (m_iType == SPIN_CONTROL_TYPE_PAGE) { // work out number of pages and current page int numPages = (m_numItems + m_itemsPerPage - 1) / m_itemsPerPage; int currentPage = m_currentItem / m_itemsPerPage + 1; if (m_currentItem >= m_numItems - m_itemsPerPage) currentPage = numPages; text = StringUtils::Format("{}/{}", currentPage, numPages); } else if (m_iType == SPIN_CONTROL_TYPE_FLOAT) { if (m_bShowRange) { text = StringUtils::Format("{:02.2f}/{:02.2f}", m_fValue, m_fEnd); } else { text = StringUtils::Format("{:02.2f}", m_fValue); } } else { if (m_iValue >= 0 && m_iValue < (int)m_vecLabels.size() ) { if (m_bShowRange) { text = StringUtils::Format("({}/{}) {}", m_iValue + 1, (int)m_vecLabels.size(), m_vecLabels[m_iValue]); } else { text = m_vecLabels[m_iValue]; } } else text = StringUtils::Format("?{}?", m_iValue); } changed |= m_label.SetText(text); float textWidth = m_label.GetTextWidth() + 2 * m_label.GetLabelInfo().offsetX; // Position the arrows bool arrowsOnRight(0 != (m_label.GetLabelInfo().align & (XBFONT_RIGHT | XBFONT_CENTER_X))); if (!arrowsOnRight) { changed |= m_imgspinDownFocus->SetPosition(m_posX + textWidth + TEXT_SPACE, m_posY); changed |= m_imgspinDown->SetPosition(m_posX + textWidth + TEXT_SPACE, m_posY); changed |= m_imgspinDownDisabled->SetPosition(m_posX + textWidth + TEXT_SPACE, m_posY); changed |= m_imgspinUpFocus->SetPosition( m_posX + textWidth + TEXT_SPACE + m_imgspinDown->GetWidth(), m_posY); changed |= m_imgspinUp->SetPosition(m_posX + textWidth + TEXT_SPACE + m_imgspinDown->GetWidth(), m_posY); changed |= m_imgspinUpDisabled->SetPosition( m_posX + textWidth + TEXT_SPACE + m_imgspinDownDisabled->GetWidth(), m_posY); } changed |= m_imgspinDownFocus->Process(currentTime); changed |= m_imgspinDown->Process(currentTime); changed |= m_imgspinUp->Process(currentTime); changed |= m_imgspinUpFocus->Process(currentTime); changed |= m_imgspinUpDisabled->Process(currentTime); changed |= m_imgspinDownDisabled->Process(currentTime); changed |= m_label.Process(currentTime); if (changed) MarkDirtyRegion(); CGUIControl::Process(currentTime, dirtyregions); } void CGUISpinControl::Render() { if (m_label.GetLabelInfo().font) { float textWidth = m_label.GetTextWidth() + 2 * m_label.GetLabelInfo().offsetX; // Position the arrows bool arrowsOnRight(0 != (m_label.GetLabelInfo().align & (XBFONT_RIGHT | XBFONT_CENTER_X))); if (arrowsOnRight) RenderText(m_posX - TEXT_SPACE - textWidth, m_posY, textWidth, m_height); else RenderText(m_posX + m_imgspinDown->GetWidth() + m_imgspinUp->GetWidth() + TEXT_SPACE, m_posY, textWidth, m_height); // set our hit rectangle for MouseOver events m_hitRect = m_label.GetRenderRect(); } if (HasFocus()) { if (m_iSelect == SPIN_BUTTON_UP) m_imgspinUpFocus->Render(); else m_imgspinUp->Render(); if (m_iSelect == SPIN_BUTTON_DOWN) m_imgspinDownFocus->Render(); else m_imgspinDown->Render(); } else if (!HasFocus() && !IsDisabled()) { m_imgspinUp->Render(); m_imgspinDown->Render(); } else { m_imgspinUpDisabled->Render(); m_imgspinDownDisabled->Render(); } CGUIControl::Render(); } void CGUISpinControl::RenderText(float posX, float posY, float width, float height) { m_label.SetMaxRect(posX, posY, width, height); m_label.SetColor(GetTextColor()); m_label.Render(); } CGUILabel::COLOR CGUISpinControl::GetTextColor() const { if (IsDisabled()) return CGUILabel::COLOR_DISABLED; else if (HasFocus()) return CGUILabel::COLOR_FOCUSED; return CGUILabel::COLOR_TEXT; } void CGUISpinControl::SetRange(int iStart, int iEnd) { m_iStart = iStart; m_iEnd = iEnd; } void CGUISpinControl::SetFloatRange(float fStart, float fEnd) { m_fStart = fStart; m_fEnd = fEnd; } void CGUISpinControl::SetValueFromLabel(const std::string &label) { if (m_iType == SPIN_CONTROL_TYPE_TEXT) { m_iValue = 0; for (unsigned int i = 0; i < m_vecLabels.size(); i++) if (label == m_vecLabels[i]) m_iValue = i; } else m_iValue = atoi(label.c_str()); MarkDirtyRegion(); SetInvalid(); } void CGUISpinControl::SetValue(int iValue) { if (m_iType == SPIN_CONTROL_TYPE_TEXT) { m_iValue = 0; for (unsigned int i = 0; i < m_vecValues.size(); i++) if (iValue == m_vecValues[i]) m_iValue = i; } else m_iValue = iValue; MarkDirtyRegion(); SetInvalid(); } void CGUISpinControl::SetFloatValue(float fValue) { m_fValue = fValue; } void CGUISpinControl::SetStringValue(const std::string& strValue) { if (m_iType == SPIN_CONTROL_TYPE_TEXT) { m_iValue = 0; for (unsigned int i = 0; i < m_vecStrValues.size(); i++) if (strValue == m_vecStrValues[i]) m_iValue = i; } SetInvalid(); } int CGUISpinControl::GetValue() const { if (m_iType == SPIN_CONTROL_TYPE_TEXT) { if (m_iValue >= 0 && m_iValue < (int)m_vecValues.size()) return m_vecValues[m_iValue]; } return m_iValue; } float CGUISpinControl::GetFloatValue() const { return m_fValue; } std::string CGUISpinControl::GetStringValue() const { if (m_iType == SPIN_CONTROL_TYPE_TEXT && m_iValue >= 0 && m_iValue < (int)m_vecLabels.size()) { if (m_iValue < (int)m_vecStrValues.size()) return m_vecStrValues[m_iValue]; return m_vecLabels[m_iValue]; } return ""; } void CGUISpinControl::AddLabel(const std::string& strLabel, int iValue) { m_vecLabels.push_back(strLabel); m_vecValues.push_back(iValue); } void CGUISpinControl::AddLabel(const std::string& strLabel, const std::string& strValue) { m_vecLabels.push_back(strLabel); m_vecStrValues.push_back(strValue); } const std::string CGUISpinControl::GetLabel() const { if (m_iValue >= 0 && m_iValue < (int)m_vecLabels.size()) { return m_vecLabels[m_iValue]; } return ""; } void CGUISpinControl::SetPosition(float posX, float posY) { CGUIControl::SetPosition(posX, posY); m_imgspinDownFocus->SetPosition(posX, posY); m_imgspinDown->SetPosition(posX, posY); m_imgspinDownDisabled->SetPosition(posX, posY); m_imgspinUp->SetPosition(m_posX + m_imgspinDown->GetWidth(), m_posY); m_imgspinUpFocus->SetPosition(m_posX + m_imgspinDownFocus->GetWidth(), m_posY); m_imgspinUpDisabled->SetPosition(m_posX + m_imgspinDownDisabled->GetWidth(), m_posY); } float CGUISpinControl::GetWidth() const { return m_imgspinDown->GetWidth() * 2; } bool CGUISpinControl::CanMoveUp(bool bTestReverse) { // test for reverse... if (bTestReverse && m_bReverse) return CanMoveDown(false); switch (m_iType) { case SPIN_CONTROL_TYPE_PAGE: return m_currentItem > 0; case SPIN_CONTROL_TYPE_INT: { if (m_iValue - 1 >= m_iStart) return true; return false; } break; case SPIN_CONTROL_TYPE_FLOAT: { if (m_fValue - m_fInterval >= m_fStart) return true; return false; } break; case SPIN_CONTROL_TYPE_TEXT: { if (m_iValue - 1 >= 0) return true; return false; } break; } return false; } bool CGUISpinControl::CanMoveDown(bool bTestReverse) { // test for reverse... if (bTestReverse && m_bReverse) return CanMoveUp(false); switch (m_iType) { case SPIN_CONTROL_TYPE_PAGE: return m_currentItem < m_numItems; case SPIN_CONTROL_TYPE_INT: { if (m_iValue + 1 <= m_iEnd) return true; return false; } break; case SPIN_CONTROL_TYPE_FLOAT: { if (m_fValue + m_fInterval <= m_fEnd) return true; return false; } break; case SPIN_CONTROL_TYPE_TEXT: { if (m_iValue + 1 < (int)m_vecLabels.size()) return true; return false; } break; } return false; } void CGUISpinControl::PageUp() { switch (m_iType) { case SPIN_CONTROL_TYPE_INT: { if (m_iValue - 10 >= m_iStart) m_iValue -= 10; else m_iValue = m_iStart; CGUIMessage msg(GUI_MSG_CLICKED, GetID(), GetParentID()); SendWindowMessage(msg); return ; } break; case SPIN_CONTROL_TYPE_PAGE: ChangePage(-10); break; case SPIN_CONTROL_TYPE_TEXT: { if (m_iValue - 10 >= 0) m_iValue -= 10; else m_iValue = 0; CGUIMessage msg(GUI_MSG_CLICKED, GetID(), GetParentID()); SendWindowMessage(msg); return ; } break; } } void CGUISpinControl::PageDown() { switch (m_iType) { case SPIN_CONTROL_TYPE_INT: { if (m_iValue + 10 <= m_iEnd) m_iValue += 10; else m_iValue = m_iEnd; CGUIMessage msg(GUI_MSG_CLICKED, GetID(), GetParentID()); SendWindowMessage(msg); return ; } break; case SPIN_CONTROL_TYPE_PAGE: ChangePage(10); break; case SPIN_CONTROL_TYPE_TEXT: { if (m_iValue + 10 < (int)m_vecLabels.size() ) m_iValue += 10; CGUIMessage msg(GUI_MSG_CLICKED, GetID(), GetParentID()); SendWindowMessage(msg); } break; } } void CGUISpinControl::MoveUp(bool bTestReverse) { if (bTestReverse && m_bReverse) { // actually should move down. MoveDown(false); return ; } switch (m_iType) { case SPIN_CONTROL_TYPE_INT: { if (m_iValue - 1 >= m_iStart) m_iValue--; else if (m_iValue == m_iStart) m_iValue = m_iEnd; CGUIMessage msg(GUI_MSG_CLICKED, GetID(), GetParentID()); SendWindowMessage(msg); return ; } break; case SPIN_CONTROL_TYPE_PAGE: ChangePage(-1); break; case SPIN_CONTROL_TYPE_FLOAT: { if (m_fValue - m_fInterval >= m_fStart) m_fValue -= m_fInterval; else m_fValue = m_fEnd; CGUIMessage msg(GUI_MSG_CLICKED, GetID(), GetParentID()); SendWindowMessage(msg); return ; } break; case SPIN_CONTROL_TYPE_TEXT: { if (m_iValue - 1 >= 0) m_iValue--; else if (m_iValue == 0) m_iValue = (int)m_vecLabels.size() - 1; CGUIMessage msg(GUI_MSG_CLICKED, GetID(), GetParentID()); SendWindowMessage(msg); return ; } break; } } void CGUISpinControl::MoveDown(bool bTestReverse) { if (bTestReverse && m_bReverse) { // actually should move up. MoveUp(false); return ; } switch (m_iType) { case SPIN_CONTROL_TYPE_INT: { if (m_iValue + 1 <= m_iEnd) m_iValue++; else if (m_iValue == m_iEnd) m_iValue = m_iStart; CGUIMessage msg(GUI_MSG_CLICKED, GetID(), GetParentID()); SendWindowMessage(msg); return ; } break; case SPIN_CONTROL_TYPE_PAGE: ChangePage(1); break; case SPIN_CONTROL_TYPE_FLOAT: { if (m_fValue + m_fInterval <= m_fEnd) m_fValue += m_fInterval; else m_fValue = m_fStart; CGUIMessage msg(GUI_MSG_CLICKED, GetID(), GetParentID()); SendWindowMessage(msg); return ; } break; case SPIN_CONTROL_TYPE_TEXT: { if (m_iValue + 1 < (int)m_vecLabels.size() ) m_iValue++; else if (m_iValue == (int)m_vecLabels.size() - 1) m_iValue = 0; CGUIMessage msg(GUI_MSG_CLICKED, GetID(), GetParentID()); SendWindowMessage(msg); return ; } break; } } void CGUISpinControl::SetReverse(bool bReverse) { m_bReverse = bReverse; } void CGUISpinControl::SetFloatInterval(float fInterval) { m_fInterval = fInterval; } void CGUISpinControl::SetShowRange(bool bOnoff) { m_bShowRange = bOnoff; } int CGUISpinControl::GetMinimum() const { switch (m_iType) { case SPIN_CONTROL_TYPE_PAGE: return 0; case SPIN_CONTROL_TYPE_INT: return m_iStart; break; case SPIN_CONTROL_TYPE_TEXT: return 1; break; case SPIN_CONTROL_TYPE_FLOAT: return (int)(m_fStart*10.0f); break; } return 0; } int CGUISpinControl::GetMaximum() const { switch (m_iType) { case SPIN_CONTROL_TYPE_PAGE: return m_numItems; case SPIN_CONTROL_TYPE_INT: return m_iEnd; break; case SPIN_CONTROL_TYPE_TEXT: return (int)m_vecLabels.size(); break; case SPIN_CONTROL_TYPE_FLOAT: return (int)(m_fEnd*10.0f); break; } return 100; } bool CGUISpinControl::HitTest(const CPoint &point) const { if (m_imgspinUpFocus->HitTest(point) || m_imgspinDownFocus->HitTest(point)) return true; return CGUIControl::HitTest(point); } bool CGUISpinControl::OnMouseOver(const CPoint &point) { int select = m_iSelect; if (m_imgspinDownFocus->HitTest(point)) m_iSelect = SPIN_BUTTON_DOWN; else m_iSelect = SPIN_BUTTON_UP; if (select != m_iSelect) MarkDirtyRegion(); return CGUIControl::OnMouseOver(point); } EVENT_RESULT CGUISpinControl::OnMouseEvent(const CPoint &point, const CMouseEvent &event) { if (event.m_id == ACTION_MOUSE_LEFT_CLICK) { if (m_imgspinUpFocus->HitTest(point)) MoveUp(); else if (m_imgspinDownFocus->HitTest(point)) MoveDown(); return EVENT_RESULT_HANDLED; } else if (event.m_id == ACTION_MOUSE_WHEEL_UP) { if (m_imgspinUpFocus->HitTest(point) || m_imgspinDownFocus->HitTest(point)) { MoveUp(); return EVENT_RESULT_HANDLED; } } else if (event.m_id == ACTION_MOUSE_WHEEL_DOWN) { if (m_imgspinUpFocus->HitTest(point) || m_imgspinDownFocus->HitTest(point)) { MoveDown(); return EVENT_RESULT_HANDLED; } } return EVENT_RESULT_UNHANDLED; } std::string CGUISpinControl::GetDescription() const { return StringUtils::Format("{}/{}", 1 + GetValue(), GetMaximum()); } bool CGUISpinControl::IsFocusedOnUp() const { return (m_iSelect == SPIN_BUTTON_UP); } void CGUISpinControl::ChangePage(int amount) { m_currentItem += amount * m_itemsPerPage; if (m_currentItem > m_numItems - m_itemsPerPage) m_currentItem = m_numItems - m_itemsPerPage; if (m_currentItem < 0) m_currentItem = 0; CGUIMessage message(GUI_MSG_NOTIFY_ALL, GetParentID(), GetID(), GUI_MSG_PAGE_CHANGE, m_currentItem); SendWindowMessage(message); } bool CGUISpinControl::UpdateColors(const CGUIListItem* item) { bool changed = CGUIControl::UpdateColors(nullptr); changed |= m_label.UpdateColors(); changed |= m_imgspinDownFocus->SetDiffuseColor(m_diffuseColor); changed |= m_imgspinDown->SetDiffuseColor(m_diffuseColor); changed |= m_imgspinUp->SetDiffuseColor(m_diffuseColor); changed |= m_imgspinUpFocus->SetDiffuseColor(m_diffuseColor); changed |= m_imgspinUpDisabled->SetDiffuseColor(m_diffuseColor); changed |= m_imgspinDownDisabled->SetDiffuseColor(m_diffuseColor); return changed; } bool CGUISpinControl::IsVisible() const { // page controls can be optionally disabled if the number of pages is 1 if (m_iType == SPIN_CONTROL_TYPE_PAGE && m_numItems <= m_itemsPerPage && !m_showOnePage) return false; return CGUIControl::IsVisible(); }