From c04dcc2e7d834218ef2d4194331e383402495ae1 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 10 Apr 2024 20:07:22 +0200 Subject: Adding upstream version 2:20.4+dfsg. Signed-off-by: Daniel Baumann --- xbmc/guilib/GUITextBox.cpp | 451 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 451 insertions(+) create mode 100644 xbmc/guilib/GUITextBox.cpp (limited to 'xbmc/guilib/GUITextBox.cpp') diff --git a/xbmc/guilib/GUITextBox.cpp b/xbmc/guilib/GUITextBox.cpp new file mode 100644 index 0000000..dcfbb8e --- /dev/null +++ b/xbmc/guilib/GUITextBox.cpp @@ -0,0 +1,451 @@ +/* + * 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 "GUITextBox.h" + +#include "GUIInfoManager.h" +#include "GUIMessage.h" +#include "guilib/GUIComponent.h" +#include "guilib/guiinfo/GUIInfoLabels.h" +#include "utils/MathUtils.h" +#include "utils/StringUtils.h" +#include "utils/XBMCTinyXML.h" + +#include + +using namespace KODI::GUILIB; + +CGUITextBox::CGUITextBox(int parentID, int controlID, float posX, float posY, float width, float height, + const CLabelInfo& labelInfo, int scrollTime, + const CLabelInfo* labelInfoMono) + : CGUIControl(parentID, controlID, posX, posY, width, height) + , CGUITextLayout(labelInfo.font, true) + , m_label(labelInfo) +{ + m_offset = 0; + m_scrollOffset = 0; + m_scrollSpeed = 0; + m_itemsPerPage = 10; + m_itemHeight = 10; + ControlType = GUICONTROL_TEXTBOX; + m_pageControl = 0; + m_lastRenderTime = 0; + m_scrollTime = scrollTime; + m_autoScrollTime = 0; + m_autoScrollDelay = 3000; + m_autoScrollDelayTime = 0; + m_autoScrollRepeatAnim = NULL; + m_minHeight = 0; + m_renderHeight = height; + if (labelInfoMono) + SetMonoFont(labelInfoMono->font); +} + +CGUITextBox::CGUITextBox(const CGUITextBox& from) + : CGUIControl(from), CGUITextLayout(from), m_autoScrollCondition(from.m_autoScrollCondition) +{ + m_pageControl = from.m_pageControl; + m_scrollTime = from.m_scrollTime; + m_autoScrollTime = from.m_autoScrollTime; + m_autoScrollDelay = from.m_autoScrollDelay; + m_minHeight = from.m_minHeight; + m_renderHeight = from.m_renderHeight; + m_autoScrollRepeatAnim = NULL; + if (from.m_autoScrollRepeatAnim) + m_autoScrollRepeatAnim = new CAnimation(*from.m_autoScrollRepeatAnim); + m_label = from.m_label; + m_info = from.m_info; + // defaults + m_offset = 0; + m_scrollOffset = 0; + m_scrollSpeed = 0; + m_itemsPerPage = 10; + m_itemHeight = 10; + m_lastRenderTime = 0; + m_autoScrollDelayTime = 0; + ControlType = GUICONTROL_TEXTBOX; +} + +CGUITextBox::~CGUITextBox(void) +{ + delete m_autoScrollRepeatAnim; + m_autoScrollRepeatAnim = NULL; +} + +bool CGUITextBox::UpdateColors(const CGUIListItem* item) +{ + bool changed = CGUIControl::UpdateColors(nullptr); + changed |= m_label.UpdateColors(); + + return changed; +} + +#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) + +void CGUITextBox::UpdateInfo(const CGUIListItem *item) +{ + m_textColor = m_label.textColor; + if (!CGUITextLayout::Update(item ? m_info.GetItemLabel(item) : m_info.GetLabel(m_parentID), m_width)) + return; // nothing changed + + // needed update, so reset to the top of the textbox and update our sizing/page control + SetInvalid(); + m_offset = 0; + m_scrollOffset = 0; + ResetAutoScrolling(); + + m_itemHeight = m_font ? m_font->GetLineHeight() : 10; + float textHeight = m_font ? m_font->GetTextHeight(m_lines.size()) : m_itemHeight * m_lines.size(); + float maxHeight = m_height ? m_height : textHeight; + m_renderHeight = m_minHeight ? CLAMP(textHeight, m_minHeight, maxHeight) : m_height; + m_itemsPerPage = (unsigned int)(m_renderHeight / m_itemHeight); + + UpdatePageControl(); +} + +void CGUITextBox::DoProcess(unsigned int currentTime, CDirtyRegionList &dirtyregions) +{ + CGUIControl::DoProcess(currentTime, dirtyregions); + + // if not visible, we reset the autoscroll timer and positioning + if (!IsVisible() && m_autoScrollTime) + { + ResetAutoScrolling(); + m_lastRenderTime = 0; + m_offset = 0; + m_scrollOffset = 0; + m_scrollSpeed = 0; + } +} + +void CGUITextBox::Process(unsigned int currentTime, CDirtyRegionList &dirtyregions) +{ + // update our auto-scrolling as necessary + if (m_autoScrollTime && m_lines.size() > m_itemsPerPage) + { + if (!m_autoScrollCondition || m_autoScrollCondition->Get(INFO::DEFAULT_CONTEXT)) + { + if (m_lastRenderTime) + m_autoScrollDelayTime += currentTime - m_lastRenderTime; + if (m_autoScrollDelayTime > (unsigned int)m_autoScrollDelay && m_scrollSpeed == 0) + { // delay is finished - start scrolling + MarkDirtyRegion(); + if (m_offset < (int)m_lines.size() - m_itemsPerPage) + ScrollToOffset(m_offset + 1, true); + else + { // at the end, run a delay and restart + if (m_autoScrollRepeatAnim) + { + if (m_autoScrollRepeatAnim->GetState() == ANIM_STATE_NONE) + m_autoScrollRepeatAnim->QueueAnimation(ANIM_PROCESS_NORMAL); + else if (m_autoScrollRepeatAnim->GetState() == ANIM_STATE_APPLIED) + { // reset to the start of the list and start the scrolling again + m_offset = 0; + m_scrollOffset = 0; + ResetAutoScrolling(); + } + } + } + } + } + else if (m_autoScrollCondition) + ResetAutoScrolling(); // conditional is false, so reset the autoscrolling + } + + // render the repeat anim as appropriate + if (m_autoScrollRepeatAnim) + { + if (m_autoScrollRepeatAnim->GetProcess() != ANIM_PROCESS_NONE) + MarkDirtyRegion(); + m_autoScrollRepeatAnim->Animate(currentTime, true); + TransformMatrix matrix; + m_autoScrollRepeatAnim->RenderAnimation(matrix); + m_cachedTextMatrix = CServiceBroker::GetWinSystem()->GetGfxContext().AddTransform(matrix); + } + + // update our scroll position as necessary + if (m_scrollSpeed != 0) + MarkDirtyRegion(); + + if (m_lastRenderTime) + m_scrollOffset += m_scrollSpeed * (currentTime - m_lastRenderTime); + if ((m_scrollSpeed < 0 && m_scrollOffset < m_offset * m_itemHeight) || + (m_scrollSpeed > 0 && m_scrollOffset > m_offset * m_itemHeight)) + { + m_scrollOffset = m_offset * m_itemHeight; + m_scrollSpeed = 0; + } + m_lastRenderTime = currentTime; + + if (m_pageControl) + { + CGUIMessage msg(GUI_MSG_ITEM_SELECT, GetID(), m_pageControl, + MathUtils::round_int(static_cast(m_scrollOffset / m_itemHeight))); + SendWindowMessage(msg); + } + + CGUIControl::Process(currentTime, dirtyregions); + + if (m_autoScrollRepeatAnim) + CServiceBroker::GetWinSystem()->GetGfxContext().RemoveTransform(); +} + +void CGUITextBox::Render() +{ + // render the repeat anim as appropriate + if (m_autoScrollRepeatAnim) + CServiceBroker::GetWinSystem()->GetGfxContext().SetTransform(m_cachedTextMatrix); + + if (CServiceBroker::GetWinSystem()->GetGfxContext().SetClipRegion(m_posX, m_posY, m_width, m_renderHeight)) + { + // we offset our draw position to take into account scrolling and whether or not our focused + // item is offscreen "above" the list. + int offset = (int)(m_scrollOffset / m_itemHeight); + float posX = m_posX; + float posY = m_posY + offset * m_itemHeight - m_scrollOffset; + + uint32_t alignment = m_label.align; + + if (alignment & XBFONT_CENTER_Y) + { + if (m_font) + { + float textHeight = m_font->GetTextHeight(std::min((unsigned int)m_lines.size(), m_itemsPerPage)); + + if (textHeight <= m_renderHeight) + posY += (m_renderHeight - textHeight) * 0.5f; + } + + alignment &= ~XBFONT_CENTER_Y; + } + + // alignment correction + if (alignment & XBFONT_CENTER_X) + posX += m_width * 0.5f; + if (alignment & XBFONT_RIGHT) + posX += m_width; + + if (m_font) + { + m_font->Begin(); + int current = offset; + + // set the main text color + if (m_colors.size()) + m_colors[0] = m_label.textColor; + + while (posY < m_posY + m_renderHeight && current < (int)m_lines.size()) + { + uint32_t align = alignment; + if (m_lines[current].m_text.size() && m_lines[current].m_carriageReturn) + align &= ~XBFONT_JUSTIFIED; // last line of a paragraph shouldn't be justified + m_font->DrawText(posX, posY, m_colors, m_label.shadowColor, m_lines[current].m_text, align, m_width); + posY += m_itemHeight; + current++; + } + m_font->End(); + } + + CServiceBroker::GetWinSystem()->GetGfxContext().RestoreClipRegion(); + } + if (m_autoScrollRepeatAnim) + CServiceBroker::GetWinSystem()->GetGfxContext().RemoveTransform(); + CGUIControl::Render(); +} + +bool CGUITextBox::OnMessage(CGUIMessage& message) +{ + if (message.GetControlId() == GetID()) + { + if (message.GetMessage() == GUI_MSG_LABEL_SET) + { + m_offset = 0; + m_scrollOffset = 0; + ResetAutoScrolling(); + CGUITextLayout::Reset(); + m_info.SetLabel(message.GetLabel(), "", GetParentID()); + } + + if (message.GetMessage() == GUI_MSG_LABEL_RESET) + { + m_offset = 0; + m_scrollOffset = 0; + ResetAutoScrolling(); + CGUITextLayout::Reset(); + UpdatePageControl(); + SetInvalid(); + } + + if (message.GetMessage() == GUI_MSG_PAGE_CHANGE) + { + if (message.GetSenderId() == m_pageControl) + { // update our page + Scroll(message.GetParam1()); + return true; + } + } + + if (message.GetMessage() == GUI_MSG_SET_TYPE) + { + UseMonoFont(message.GetParam1() == 1 ? true : false); + return true; + } + } + + return CGUIControl::OnMessage(message); +} + +float CGUITextBox::GetHeight() const +{ + return m_renderHeight; +} + +void CGUITextBox::SetMinHeight(float minHeight) +{ + if (m_minHeight != minHeight) + SetInvalid(); + + m_minHeight = minHeight; +} + +void CGUITextBox::UpdatePageControl() +{ + if (m_pageControl) + { + CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), m_pageControl, m_itemsPerPage, m_lines.size()); + SendWindowMessage(msg); + } +} + +bool CGUITextBox::CanFocus() const +{ + return false; +} + +void CGUITextBox::SetPageControl(int pageControl) +{ + m_pageControl = pageControl; +} + +void CGUITextBox::SetInfo(const GUIINFO::CGUIInfoLabel &infoLabel) +{ + m_info = infoLabel; +} + +void CGUITextBox::Scroll(unsigned int offset) +{ + ResetAutoScrolling(); + if (m_lines.size() <= m_itemsPerPage) + return; // no need to scroll + if (offset > m_lines.size() - m_itemsPerPage) + offset = m_lines.size() - m_itemsPerPage; // on last page + ScrollToOffset(offset); +} + +void CGUITextBox::ScrollToOffset(int offset, bool autoScroll) +{ + m_scrollOffset = m_offset * m_itemHeight; + int timeToScroll = autoScroll ? m_autoScrollTime : m_scrollTime; + m_scrollSpeed = (offset * m_itemHeight - m_scrollOffset) / timeToScroll; + m_offset = offset; +} + +void CGUITextBox::SetAutoScrolling(const TiXmlNode *node) +{ + if (!node) return; + const TiXmlElement *scroll = node->FirstChildElement("autoscroll"); + if (scroll) + { + scroll->Attribute("delay", &m_autoScrollDelay); + scroll->Attribute("time", &m_autoScrollTime); + if (scroll->FirstChild()) + m_autoScrollCondition = CServiceBroker::GetGUI()->GetInfoManager().Register(scroll->FirstChild()->ValueStr(), GetParentID()); + int repeatTime; + if (scroll->Attribute("repeat", &repeatTime)) + m_autoScrollRepeatAnim = new CAnimation(CAnimation::CreateFader(100, 0, repeatTime, 1000)); + } +} + +void CGUITextBox::SetAutoScrolling(int delay, int time, int repeatTime, const std::string &condition /* = "" */) +{ + m_autoScrollDelay = delay; + m_autoScrollTime = time; + if (!condition.empty()) + m_autoScrollCondition = CServiceBroker::GetGUI()->GetInfoManager().Register(condition, GetParentID()); + m_autoScrollRepeatAnim = new CAnimation(CAnimation::CreateFader(100, 0, repeatTime, 1000)); +} + +void CGUITextBox::ResetAutoScrolling() +{ + m_autoScrollDelayTime = 0; + if (m_autoScrollRepeatAnim) + m_autoScrollRepeatAnim->ResetAnimation(); +} + +unsigned int CGUITextBox::GetRows() const +{ + return m_lines.size(); +} + +int CGUITextBox::GetNumPages() const +{ + return m_itemsPerPage > 0 ? (GetRows() + m_itemsPerPage - 1) / m_itemsPerPage : 0; +} + +int CGUITextBox::GetCurrentPage() const +{ + if (m_offset + m_itemsPerPage >= GetRows()) // last page + return GetNumPages(); + return m_offset / m_itemsPerPage + 1; +} + +std::string CGUITextBox::GetLabel(int info) const +{ + std::string label; + switch (info) + { + case CONTAINER_NUM_PAGES: + label = std::to_string(GetNumPages()); + break; + case CONTAINER_CURRENT_PAGE: + label = std::to_string(GetCurrentPage()); + break; + default: + break; + } + return label; +} + +bool CGUITextBox::GetCondition(int condition, int data) const +{ + switch (condition) + { + case CONTAINER_HAS_NEXT: + return (GetCurrentPage() < GetNumPages()); + case CONTAINER_HAS_PREVIOUS: + return (GetCurrentPage() > 1); + default: + return false; + } +} + +std::string CGUITextBox::GetDescription() const +{ + return GetText(); +} + +void CGUITextBox::UpdateVisibility(const CGUIListItem *item) +{ + // we have to update the page control when we become visible + // as another control may be sharing the same page control when we're + // not visible + bool wasVisible = IsVisible(); + CGUIControl::UpdateVisibility(item); + if (IsVisible() && !wasVisible) + UpdatePageControl(); +} -- cgit v1.2.3