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 "GUIFadeLabelControl.h"
10 
11 #include "GUIMessage.h"
12 #include "utils/Random.h"
13 
14 using namespace KODI::GUILIB;
15 
CGUIFadeLabelControl(int parentID,int controlID,float posX,float posY,float width,float height,const CLabelInfo & labelInfo,bool scrollOut,unsigned int timeToDelayAtEnd,bool resetOnLabelChange,bool randomized)16 CGUIFadeLabelControl::CGUIFadeLabelControl(int parentID, int controlID, float posX, float posY, float width, float height, const CLabelInfo& labelInfo, bool scrollOut, unsigned int timeToDelayAtEnd, bool resetOnLabelChange, bool randomized)
17     : CGUIControl(parentID, controlID, posX, posY, width, height), m_label(labelInfo), m_scrollInfo(50, labelInfo.offsetX, labelInfo.scrollSpeed)
18     , m_textLayout(labelInfo.font, false)
19     , m_fadeAnim(CAnimation::CreateFader(100, 0, timeToDelayAtEnd, 200))
20 {
21   m_currentLabel = 0;
22   ControlType = GUICONTROL_FADELABEL;
23   m_scrollOut = scrollOut;
24   m_fadeAnim.ApplyAnimation();
25   m_lastLabel = -1;
26   m_scrollSpeed = labelInfo.scrollSpeed;  // save it for later
27   m_resetOnLabelChange = resetOnLabelChange;
28   m_shortText = true;
29   m_scroll = true;
30   m_randomized = randomized;
31 }
32 
CGUIFadeLabelControl(const CGUIFadeLabelControl & from)33 CGUIFadeLabelControl::CGUIFadeLabelControl(const CGUIFadeLabelControl &from)
34 : CGUIControl(from), m_infoLabels(from.m_infoLabels), m_label(from.m_label), m_scrollInfo(from.m_scrollInfo), m_textLayout(from.m_textLayout),
35   m_fadeAnim(from.m_fadeAnim)
36 {
37   m_scrollOut = from.m_scrollOut;
38   m_scrollSpeed = from.m_scrollSpeed;
39   m_resetOnLabelChange = from.m_resetOnLabelChange;
40 
41   m_fadeAnim.ApplyAnimation();
42   m_currentLabel = 0;
43   m_lastLabel = -1;
44   ControlType = GUICONTROL_FADELABEL;
45   m_shortText = from.m_shortText;
46   m_scroll = from.m_scroll;
47   m_randomized = from.m_randomized;
48   m_allLabelsShown = from.m_allLabelsShown;
49 }
50 
51 CGUIFadeLabelControl::~CGUIFadeLabelControl(void) = default;
52 
SetInfo(const std::vector<GUIINFO::CGUIInfoLabel> & infoLabels)53 void CGUIFadeLabelControl::SetInfo(const std::vector<GUIINFO::CGUIInfoLabel> &infoLabels)
54 {
55   m_lastLabel = -1;
56   m_infoLabels = infoLabels;
57   m_allLabelsShown = m_infoLabels.empty();
58   if (m_randomized)
59     KODI::UTILS::RandomShuffle(m_infoLabels.begin(), m_infoLabels.end());
60 }
61 
AddLabel(const std::string & label)62 void CGUIFadeLabelControl::AddLabel(const std::string &label)
63 {
64   m_infoLabels.emplace_back(label, "", GetParentID());
65   m_allLabelsShown = false;
66 }
67 
Process(unsigned int currentTime,CDirtyRegionList & dirtyregions)68 void CGUIFadeLabelControl::Process(unsigned int currentTime, CDirtyRegionList &dirtyregions)
69 {
70   if (m_infoLabels.empty() || !m_label.font)
71   {
72     CGUIControl::Process(currentTime, dirtyregions);
73     return;
74   }
75 
76   if (m_currentLabel >= m_infoLabels.size() )
77     m_currentLabel = 0;
78 
79   if (m_textLayout.Update(GetLabel()))
80   { // changed label - update our suffix based on length of available text
81     float width, height;
82     m_textLayout.GetTextExtent(width, height);
83     float spaceWidth = m_label.font->GetCharWidth(L' ');
84     unsigned int numSpaces = (unsigned int)(m_width / spaceWidth) + 1;
85     if (width < m_width) // append spaces for scrolling
86       numSpaces += (unsigned int)((m_width - width) / spaceWidth) + 1;
87     m_shortText = (width + m_label.offsetX) < m_width;
88     m_scrollInfo.suffix.assign(numSpaces, ' ');
89     if (m_resetOnLabelChange)
90     {
91       m_scrollInfo.Reset();
92       m_fadeAnim.ResetAnimation();
93     }
94     MarkDirtyRegion();
95   }
96 
97   if (m_shortText && m_infoLabels.size() == 1)
98     m_allLabelsShown = true;
99 
100   if (m_currentLabel != m_lastLabel)
101   { // new label - reset scrolling
102     m_scrollInfo.Reset();
103     m_fadeAnim.QueueAnimation(ANIM_PROCESS_REVERSE);
104     m_lastLabel = m_currentLabel;
105     MarkDirtyRegion();
106   }
107 
108   if (m_infoLabels.size() > 1 || !m_shortText)
109   { // have scrolling text
110     bool moveToNextLabel = false;
111     if (!m_scrollOut)
112     {
113       if (m_scrollInfo.pixelPos + m_width > m_scrollInfo.m_textWidth)
114       {
115         if (m_fadeAnim.GetProcess() != ANIM_PROCESS_NORMAL)
116           m_fadeAnim.QueueAnimation(ANIM_PROCESS_NORMAL);
117         moveToNextLabel = true;
118       }
119     }
120     else if (m_scrollInfo.pixelPos > m_scrollInfo.m_textWidth)
121       moveToNextLabel = true;
122 
123     if(m_scrollInfo.pixelSpeed || m_fadeAnim.GetState() == ANIM_STATE_IN_PROCESS)
124       MarkDirtyRegion();
125 
126     // apply the fading animation
127     TransformMatrix matrix;
128     m_fadeAnim.Animate(currentTime, true);
129     m_fadeAnim.RenderAnimation(matrix);
130     m_fadeMatrix = CServiceBroker::GetWinSystem()->GetGfxContext().AddTransform(matrix);
131 
132     if (m_fadeAnim.GetState() == ANIM_STATE_APPLIED)
133       m_fadeAnim.ResetAnimation();
134 
135     m_scrollInfo.SetSpeed((m_fadeAnim.GetProcess() == ANIM_PROCESS_NONE) ? m_scrollSpeed : 0);
136 
137     if (moveToNextLabel)
138     { // increment the label and reset scrolling
139       if (m_fadeAnim.GetProcess() != ANIM_PROCESS_NORMAL)
140       {
141         if (++m_currentLabel >= m_infoLabels.size())
142         {
143           m_currentLabel = 0;
144           m_allLabelsShown = true;
145         }
146         m_scrollInfo.Reset();
147         m_fadeAnim.QueueAnimation(ANIM_PROCESS_REVERSE);
148       }
149     }
150 
151     if (m_scroll)
152     {
153       m_textLayout.UpdateScrollinfo(m_scrollInfo);
154       MarkDirtyRegion();
155     }
156 
157     CServiceBroker::GetWinSystem()->GetGfxContext().RemoveTransform();
158   }
159 
160   CGUIControl::Process(currentTime, dirtyregions);
161 }
162 
UpdateColors()163 bool CGUIFadeLabelControl::UpdateColors()
164 {
165   bool changed = CGUIControl::UpdateColors();
166   changed |= m_label.UpdateColors();
167 
168   return changed;
169 }
170 
Render()171 void CGUIFadeLabelControl::Render()
172 {
173   if (!m_label.font)
174   { // nothing to render
175     CGUIControl::Render();
176     return ;
177   }
178 
179   float posY = m_posY;
180   if (m_label.align & XBFONT_CENTER_Y)
181     posY += m_height * 0.5f;
182   if (m_infoLabels.size() == 1 && m_shortText)
183   { // single label set and no scrolling required - just display
184     float posX = m_posX + m_label.offsetX;
185     if (m_label.align & XBFONT_CENTER_X)
186       posX = m_posX + m_width * 0.5f;
187     else if (m_label.align & XBFONT_RIGHT)
188       posX = m_posX + m_width;
189     m_textLayout.Render(posX, posY, m_label.angle, m_label.textColor, m_label.shadowColor, m_label.align, m_width - m_label.offsetX);
190     CGUIControl::Render();
191     return;
192   }
193 
194   // render the scrolling text
195   CServiceBroker::GetWinSystem()->GetGfxContext().SetTransform(m_fadeMatrix);
196   if (!m_scroll || (!m_scrollOut && m_shortText))
197   {
198     float posX = m_posX + m_label.offsetX;
199     if (m_label.align & XBFONT_CENTER_X)
200       posX = m_posX + m_width * 0.5f;
201     else if (m_label.align & XBFONT_RIGHT)
202       posX = m_posX + m_width;
203     m_textLayout.Render(posX, posY, 0, m_label.textColor, m_label.shadowColor, m_label.align, m_width);
204   }
205   else
206     m_textLayout.RenderScrolling(m_posX, posY, 0, m_label.textColor, m_label.shadowColor, (m_label.align & ~3), m_width, m_scrollInfo);
207   CServiceBroker::GetWinSystem()->GetGfxContext().RemoveTransform();
208   CGUIControl::Render();
209 }
210 
211 
CanFocus() const212 bool CGUIFadeLabelControl::CanFocus() const
213 {
214   return false;
215 }
216 
217 
OnMessage(CGUIMessage & message)218 bool CGUIFadeLabelControl::OnMessage(CGUIMessage& message)
219 {
220   if ( message.GetControlId() == GetID() )
221   {
222     if (message.GetMessage() == GUI_MSG_LABEL_ADD)
223     {
224       AddLabel(message.GetLabel());
225       return true;
226     }
227     if (message.GetMessage() == GUI_MSG_LABEL_RESET)
228     {
229       m_lastLabel = -1;
230       m_infoLabels.clear();
231       m_allLabelsShown = true;
232       m_scrollInfo.Reset();
233       return true;
234     }
235     if (message.GetMessage() == GUI_MSG_LABEL_SET)
236     {
237       m_lastLabel = -1;
238       m_infoLabels.clear();
239       m_allLabelsShown = true;
240       m_scrollInfo.Reset();
241       AddLabel(message.GetLabel());
242       return true;
243     }
244   }
245   return CGUIControl::OnMessage(message);
246 }
247 
GetDescription() const248 std::string CGUIFadeLabelControl::GetDescription() const
249 {
250   return (m_currentLabel < m_infoLabels.size()) ?  m_infoLabels[m_currentLabel].GetLabel(m_parentID) : "";
251 }
252 
GetLabel()253 std::string CGUIFadeLabelControl::GetLabel()
254 {
255   if (m_currentLabel > m_infoLabels.size())
256     m_currentLabel = 0;
257 
258   unsigned int numTries = 0;
259   std::string label(m_infoLabels[m_currentLabel].GetLabel(m_parentID));
260   while (label.empty() && ++numTries < m_infoLabels.size())
261   {
262     if (++m_currentLabel >= m_infoLabels.size())
263       m_currentLabel = 0;
264     label = m_infoLabels[m_currentLabel].GetLabel(m_parentID);
265   }
266   return label;
267 }
268