[guilib] fix labelcontrols with auto width always being marked as dirty if they speci...
[vuplus_xbmc] / xbmc / guilib / GUILabel.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #include "GUILabel.h"
22 #include <limits>
23
24 CGUILabel::CGUILabel(float posX, float posY, float width, float height, const CLabelInfo& labelInfo, CGUILabel::OVER_FLOW overflow)
25     : m_label(labelInfo)
26     , m_textLayout(labelInfo.font, overflow == OVER_FLOW_WRAP, height)
27     , m_scrolling(overflow == OVER_FLOW_SCROLL)
28     , m_overflowType(overflow)
29     , m_selected(false)
30     , m_scrollInfo(50, 0, labelInfo.scrollSpeed, labelInfo.scrollSuffix)
31     , m_renderRect()
32     , m_maxRect(posX, posY, posX + width, posY + height)
33     , m_invalid(true)
34     , m_color(COLOR_TEXT)
35 {
36 }
37
38 CGUILabel::~CGUILabel(void)
39 {
40 }
41
42 bool CGUILabel::SetScrolling(bool scrolling)
43 {
44   bool changed = m_scrolling != scrolling;
45
46   m_scrolling = scrolling;
47   if (!m_scrolling)
48     m_scrollInfo.Reset();
49
50   return changed;
51 }
52
53 bool CGUILabel::SetOverflow(OVER_FLOW overflow)
54 {
55   bool changed = m_overflowType != overflow;
56
57   m_overflowType = overflow;
58
59   return changed;
60 }
61
62 bool CGUILabel::SetColor(CGUILabel::COLOR color)
63 {
64   bool changed = m_color != color;
65
66   m_color = color;
67
68   return changed;
69 }
70
71 color_t CGUILabel::GetColor() const
72 {
73   switch (m_color)
74   {
75     case COLOR_SELECTED:
76       return m_label.selectedColor;
77     case COLOR_DISABLED:
78       return m_label.disabledColor;
79     case COLOR_FOCUSED:
80       return m_label.focusedColor ? m_label.focusedColor : m_label.textColor;
81     case COLOR_INVALID:
82       return m_label.invalidColor ? m_label.invalidColor : m_label.textColor;
83     default:
84       break;
85   }
86   return m_label.textColor;
87 }
88
89 bool CGUILabel::Process(unsigned int currentTime)
90 {
91   // TODO Add the correct processing
92
93   bool overFlows = (m_renderRect.Width() + 0.5f < m_textLayout.GetTextWidth()); // 0.5f to deal with floating point rounding issues
94   bool renderSolid = (m_color == COLOR_DISABLED);
95
96   if (overFlows && m_scrolling && !renderSolid)
97     return m_textLayout.UpdateScrollinfo(m_scrollInfo);
98
99   return false;
100 }
101
102 void CGUILabel::Render()
103 {
104   color_t color = GetColor();
105   bool renderSolid = (m_color == COLOR_DISABLED);
106   bool overFlows = (m_renderRect.Width() + 0.5f < m_textLayout.GetTextWidth()); // 0.5f to deal with floating point rounding issues
107   if (overFlows && m_scrolling && !renderSolid)
108     m_textLayout.RenderScrolling(m_renderRect.x1, m_renderRect.y1, m_label.angle, color, m_label.shadowColor, 0, m_renderRect.Width(), m_scrollInfo);
109   else
110   {
111     float posX = m_renderRect.x1;
112     float posY = m_renderRect.y1;
113     uint32_t align = 0;
114     if (!overFlows)
115     { // hack for right and centered multiline text, as GUITextLayout::Render() treats posX as the right hand
116       // or center edge of the text (see GUIFontTTF::DrawTextInternal), and this has already been taken care of
117       // in UpdateRenderRect(), but we wish to still pass the horizontal alignment info through (so that multiline text
118       // is aligned correctly), so we must undo the UpdateRenderRect() changes for horizontal alignment.
119       if (m_label.align & XBFONT_RIGHT)
120         posX += m_renderRect.Width();
121       else if (m_label.align & XBFONT_CENTER_X)
122         posX += m_renderRect.Width() * 0.5f;
123       if (m_label.align & XBFONT_CENTER_Y) // need to pass a centered Y so that <angle> will rotate around the correct point.
124         posY += m_renderRect.Height() * 0.5f;
125       align = m_label.align;
126     }
127     else
128       align |= XBFONT_TRUNCATED;
129     m_textLayout.Render(posX, posY, m_label.angle, color, m_label.shadowColor, align, m_overflowType == OVER_FLOW_CLIP ? m_textLayout.GetTextWidth() : m_renderRect.Width(), renderSolid);
130   }
131 }
132
133 void CGUILabel::SetInvalid()
134 {
135   m_invalid = true;
136 }
137
138 bool CGUILabel::UpdateColors()
139 {
140   return m_label.UpdateColors();
141 }
142
143 bool CGUILabel::SetMaxRect(float x, float y, float w, float h)
144 {
145   CRect oldRect = m_maxRect;
146
147   m_maxRect.SetRect(x, y, x + w, y + h);
148   UpdateRenderRect();
149
150   return oldRect != m_maxRect;
151 }
152
153 bool CGUILabel::SetAlign(uint32_t align)
154 {
155   bool changed = m_label.align != align;
156
157   m_label.align = align;
158   UpdateRenderRect();
159
160   return changed;
161 }
162
163 bool CGUILabel::SetStyledText(const vecText &text, const vecColors &colors)
164 {
165   m_textLayout.UpdateStyled(text, colors, m_maxRect.Width());
166   m_invalid = false;
167   return true;
168 }
169
170 bool CGUILabel::SetText(const CStdString &label)
171 {
172   if (m_textLayout.Update(label, m_maxRect.Width(), m_invalid))
173   { // needed an update - reset scrolling and update our text layout
174     m_scrollInfo.Reset();
175     UpdateRenderRect();
176     m_invalid = false;
177     return true;
178   }
179   else
180     return false;
181 }
182
183 bool CGUILabel::SetTextW(const CStdStringW &label)
184 {
185   if (m_textLayout.UpdateW(label, m_maxRect.Width(), m_invalid))
186   {
187     m_scrollInfo.Reset();
188     UpdateRenderRect();
189     m_invalid = false;
190     return true;
191   }
192   else
193     return false;
194 }
195
196 void CGUILabel::UpdateRenderRect()
197 {
198   // recalculate our text layout
199   float width, height;
200   m_textLayout.GetTextExtent(width, height);
201   width = std::min(width, GetMaxWidth());
202   if (m_label.align & XBFONT_CENTER_Y)
203     m_renderRect.y1 = m_maxRect.y1 + (m_maxRect.Height() - height) * 0.5f;
204   else
205     m_renderRect.y1 = m_maxRect.y1 + m_label.offsetY;
206   if (m_label.align & XBFONT_RIGHT)
207     m_renderRect.x1 = m_maxRect.x2 - width - m_label.offsetX;
208   else if (m_label.align & XBFONT_CENTER_X)
209     m_renderRect.x1 = m_maxRect.x1 + (m_maxRect.Width() - width) * 0.5f;
210   else
211     m_renderRect.x1 = m_maxRect.x1 + m_label.offsetX;
212   m_renderRect.x2 = m_renderRect.x1 + width;
213   m_renderRect.y2 = m_renderRect.y1 + height;
214 }
215
216 float CGUILabel::GetMaxWidth() const
217 {
218   if (m_label.width) return m_label.width;
219   return m_maxRect.Width() - 2*m_label.offsetX;
220 }
221
222 bool CGUILabel::CheckAndCorrectOverlap(CGUILabel &label1, CGUILabel &label2)
223 {
224   CRect rect(label1.m_renderRect);
225   if (rect.Intersect(label2.m_renderRect).IsEmpty())
226     return false; // nothing to do (though it could potentially encroach on the min_space requirement)
227   
228   // overlap vertically and horizontally - check alignment
229   CGUILabel &left = label1.m_renderRect.x1 <= label2.m_renderRect.x1 ? label1 : label2;
230   CGUILabel &right = label1.m_renderRect.x1 <= label2.m_renderRect.x1 ? label2 : label1;
231   if ((left.m_label.align & 3) == 0 && right.m_label.align & XBFONT_RIGHT)
232   {
233     static const float min_space = 10;
234     float chopPoint = (left.m_maxRect.x1 + left.GetMaxWidth() + right.m_maxRect.x2 - right.GetMaxWidth()) * 0.5f;
235     // [1       [2...[2  1].|..........1]         2]
236     // [1       [2.....[2   |      1]..1]         2]
237     // [1       [2..........|.[2   1]..1]         2]
238     if (right.m_renderRect.x1 > chopPoint)
239       chopPoint = right.m_renderRect.x1 - min_space;
240     else if (left.m_renderRect.x2 < chopPoint)
241       chopPoint = left.m_renderRect.x2 + min_space;
242     left.m_renderRect.x2 = chopPoint - min_space;
243     right.m_renderRect.x1 = chopPoint + min_space;
244     return true;
245   }
246   return false;
247 }