/xbmc/guilib/GUILabel.cpp

http://github.com/xbmc/xbmc · C++ · 238 lines · 188 code · 34 blank · 16 comment · 38 complexity · 067819fc92da0c3710baa77e529b90a2 MD5 · raw file

  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. #include "GUILabel.h"
  9. #include <limits>
  10. CGUILabel::CGUILabel(float posX, float posY, float width, float height, const CLabelInfo& labelInfo, CGUILabel::OVER_FLOW overflow)
  11. : m_label(labelInfo)
  12. , m_textLayout(labelInfo.font, overflow == OVER_FLOW_WRAP, height)
  13. , m_scrolling(overflow == OVER_FLOW_SCROLL)
  14. , m_overflowType(overflow)
  15. , m_scrollInfo(50, 0, labelInfo.scrollSpeed, labelInfo.scrollSuffix)
  16. , m_renderRect()
  17. , m_maxRect(posX, posY, posX + width, posY + height)
  18. , m_invalid(true)
  19. , m_color(COLOR_TEXT)
  20. {
  21. }
  22. CGUILabel::~CGUILabel(void) = default;
  23. bool CGUILabel::SetScrolling(bool scrolling)
  24. {
  25. bool changed = m_scrolling != scrolling;
  26. m_scrolling = scrolling;
  27. if (changed)
  28. m_scrollInfo.Reset();
  29. return changed;
  30. }
  31. bool CGUILabel::SetOverflow(OVER_FLOW overflow)
  32. {
  33. bool changed = m_overflowType != overflow;
  34. m_overflowType = overflow;
  35. return changed;
  36. }
  37. bool CGUILabel::SetColor(CGUILabel::COLOR color)
  38. {
  39. bool changed = m_color != color;
  40. m_color = color;
  41. return changed;
  42. }
  43. UTILS::Color CGUILabel::GetColor() const
  44. {
  45. switch (m_color)
  46. {
  47. case COLOR_SELECTED:
  48. return m_label.selectedColor;
  49. case COLOR_DISABLED:
  50. return m_label.disabledColor;
  51. case COLOR_FOCUSED:
  52. return m_label.focusedColor ? m_label.focusedColor : m_label.textColor;
  53. case COLOR_INVALID:
  54. return m_label.invalidColor ? m_label.invalidColor : m_label.textColor;
  55. default:
  56. break;
  57. }
  58. return m_label.textColor;
  59. }
  60. bool CGUILabel::Process(unsigned int currentTime)
  61. {
  62. //! @todo Add the correct processing
  63. bool overFlows = (m_renderRect.Width() + 0.5f < m_textLayout.GetTextWidth()); // 0.5f to deal with floating point rounding issues
  64. bool renderSolid = (m_color == COLOR_DISABLED);
  65. if (overFlows && m_scrolling && !renderSolid)
  66. {
  67. if (m_maxScrollLoops < m_scrollInfo.m_loopCount)
  68. SetScrolling(false);
  69. else
  70. return m_textLayout.UpdateScrollinfo(m_scrollInfo);
  71. }
  72. return false;
  73. }
  74. void CGUILabel::Render()
  75. {
  76. UTILS::Color color = GetColor();
  77. bool renderSolid = (m_color == COLOR_DISABLED);
  78. bool overFlows = (m_renderRect.Width() + 0.5f < m_textLayout.GetTextWidth()); // 0.5f to deal with floating point rounding issues
  79. if (overFlows && m_scrolling && !renderSolid)
  80. m_textLayout.RenderScrolling(m_renderRect.x1, m_renderRect.y1, m_label.angle, color, m_label.shadowColor, 0, m_renderRect.Width(), m_scrollInfo);
  81. else
  82. {
  83. float posX = m_renderRect.x1;
  84. float posY = m_renderRect.y1;
  85. uint32_t align = 0;
  86. if (!overFlows)
  87. { // hack for right and centered multiline text, as GUITextLayout::Render() treats posX as the right hand
  88. // or center edge of the text (see GUIFontTTF::DrawTextInternal), and this has already been taken care of
  89. // in UpdateRenderRect(), but we wish to still pass the horizontal alignment info through (so that multiline text
  90. // is aligned correctly), so we must undo the UpdateRenderRect() changes for horizontal alignment.
  91. if (m_label.align & XBFONT_RIGHT)
  92. posX += m_renderRect.Width();
  93. else if (m_label.align & XBFONT_CENTER_X)
  94. posX += m_renderRect.Width() * 0.5f;
  95. if (m_label.align & XBFONT_CENTER_Y) // need to pass a centered Y so that <angle> will rotate around the correct point.
  96. posY += m_renderRect.Height() * 0.5f;
  97. align = m_label.align;
  98. }
  99. else
  100. align |= XBFONT_TRUNCATED;
  101. 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);
  102. }
  103. }
  104. void CGUILabel::SetInvalid()
  105. {
  106. m_invalid = true;
  107. }
  108. bool CGUILabel::UpdateColors()
  109. {
  110. return m_label.UpdateColors();
  111. }
  112. bool CGUILabel::SetMaxRect(float x, float y, float w, float h)
  113. {
  114. CRect oldRect = m_maxRect;
  115. m_maxRect.SetRect(x, y, x + w, y + h);
  116. UpdateRenderRect();
  117. return oldRect != m_maxRect;
  118. }
  119. bool CGUILabel::SetAlign(uint32_t align)
  120. {
  121. bool changed = m_label.align != align;
  122. m_label.align = align;
  123. UpdateRenderRect();
  124. return changed;
  125. }
  126. bool CGUILabel::SetStyledText(const vecText &text, const std::vector<UTILS::Color> &colors)
  127. {
  128. m_textLayout.UpdateStyled(text, colors, m_maxRect.Width());
  129. m_invalid = false;
  130. return true;
  131. }
  132. bool CGUILabel::SetText(const std::string &label)
  133. {
  134. if (m_textLayout.Update(label, m_maxRect.Width(), m_invalid))
  135. { // needed an update - reset scrolling and update our text layout
  136. m_scrollInfo.Reset();
  137. UpdateRenderRect();
  138. m_invalid = false;
  139. return true;
  140. }
  141. else
  142. return false;
  143. }
  144. bool CGUILabel::SetTextW(const std::wstring &label)
  145. {
  146. if (m_textLayout.UpdateW(label, m_maxRect.Width(), m_invalid))
  147. {
  148. m_scrollInfo.Reset();
  149. UpdateRenderRect();
  150. m_invalid = false;
  151. return true;
  152. }
  153. else
  154. return false;
  155. }
  156. void CGUILabel::UpdateRenderRect()
  157. {
  158. // recalculate our text layout
  159. float width, height;
  160. m_textLayout.GetTextExtent(width, height);
  161. width = std::min(width, GetMaxWidth());
  162. if (m_label.align & XBFONT_CENTER_Y)
  163. m_renderRect.y1 = m_maxRect.y1 + (m_maxRect.Height() - height) * 0.5f;
  164. else
  165. m_renderRect.y1 = m_maxRect.y1 + m_label.offsetY;
  166. if (m_label.align & XBFONT_RIGHT)
  167. m_renderRect.x1 = m_maxRect.x2 - width - m_label.offsetX;
  168. else if (m_label.align & XBFONT_CENTER_X)
  169. m_renderRect.x1 = m_maxRect.x1 + (m_maxRect.Width() - width) * 0.5f;
  170. else
  171. m_renderRect.x1 = m_maxRect.x1 + m_label.offsetX;
  172. m_renderRect.x2 = m_renderRect.x1 + width;
  173. m_renderRect.y2 = m_renderRect.y1 + height;
  174. }
  175. float CGUILabel::GetMaxWidth() const
  176. {
  177. if (m_label.width) return m_label.width;
  178. return m_maxRect.Width() - 2*m_label.offsetX;
  179. }
  180. bool CGUILabel::CheckAndCorrectOverlap(CGUILabel &label1, CGUILabel &label2)
  181. {
  182. CRect rect(label1.m_renderRect);
  183. if (rect.Intersect(label2.m_renderRect).IsEmpty())
  184. return false; // nothing to do (though it could potentially encroach on the min_space requirement)
  185. // overlap vertically and horizontally - check alignment
  186. CGUILabel &left = label1.m_renderRect.x1 <= label2.m_renderRect.x1 ? label1 : label2;
  187. CGUILabel &right = label1.m_renderRect.x1 <= label2.m_renderRect.x1 ? label2 : label1;
  188. if ((left.m_label.align & 3) == 0 && right.m_label.align & XBFONT_RIGHT)
  189. {
  190. static const float min_space = 10;
  191. float chopPoint = (left.m_maxRect.x1 + left.GetMaxWidth() + right.m_maxRect.x2 - right.GetMaxWidth()) * 0.5f;
  192. // [1 [2...[2 1].|..........1] 2]
  193. // [1 [2.....[2 | 1]..1] 2]
  194. // [1 [2..........|.[2 1]..1] 2]
  195. if (right.m_renderRect.x1 > chopPoint)
  196. chopPoint = right.m_renderRect.x1 - min_space;
  197. else if (left.m_renderRect.x2 < chopPoint)
  198. chopPoint = left.m_renderRect.x2 + min_space;
  199. left.m_renderRect.x2 = chopPoint - min_space;
  200. right.m_renderRect.x1 = chopPoint + min_space;
  201. return true;
  202. }
  203. return false;
  204. }