The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

293 lines
8.6KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. namespace juce
  20. {
  21. namespace
  22. {
  23. int getLength (const Array<AttributedString::Attribute>& atts) noexcept
  24. {
  25. return atts.size() != 0 ? atts.getReference (atts.size() - 1).range.getEnd() : 0;
  26. }
  27. void splitAttributeRanges (Array<AttributedString::Attribute>& atts, int position)
  28. {
  29. for (int i = atts.size(); --i >= 0;)
  30. {
  31. const auto& att = atts.getUnchecked (i);
  32. auto offset = position - att.range.getStart();
  33. if (offset >= 0)
  34. {
  35. if (offset > 0 && position < att.range.getEnd())
  36. {
  37. atts.insert (i + 1, AttributedString::Attribute (att));
  38. atts.getReference (i).range.setEnd (position);
  39. atts.getReference (i + 1).range.setStart (position);
  40. }
  41. break;
  42. }
  43. }
  44. }
  45. Range<int> splitAttributeRanges (Array<AttributedString::Attribute>& atts, Range<int> newRange)
  46. {
  47. newRange = newRange.getIntersectionWith ({ 0, getLength (atts) });
  48. if (! newRange.isEmpty())
  49. {
  50. splitAttributeRanges (atts, newRange.getStart());
  51. splitAttributeRanges (atts, newRange.getEnd());
  52. }
  53. return newRange;
  54. }
  55. void mergeAdjacentRanges (Array<AttributedString::Attribute>& atts)
  56. {
  57. for (int i = atts.size() - 1; --i >= 0;)
  58. {
  59. auto& a1 = atts.getReference (i);
  60. auto& a2 = atts.getReference (i + 1);
  61. if (a1.colour == a2.colour && a1.font == a2.font)
  62. {
  63. a1.range.setEnd (a2.range.getEnd());
  64. atts.remove (i + 1);
  65. if (i < atts.size() - 1)
  66. ++i;
  67. }
  68. }
  69. }
  70. void appendRange (Array<AttributedString::Attribute>& atts,
  71. int length, const Font* f, const Colour* c)
  72. {
  73. if (atts.size() == 0)
  74. {
  75. atts.add ({ Range<int> (0, length), f != nullptr ? *f : Font(), c != nullptr ? *c : Colour (0xff000000) });
  76. }
  77. else
  78. {
  79. auto start = getLength (atts);
  80. atts.add ({ Range<int> (start, start + length),
  81. f != nullptr ? *f : atts.getReference (atts.size() - 1).font,
  82. c != nullptr ? *c : atts.getReference (atts.size() - 1).colour });
  83. mergeAdjacentRanges (atts);
  84. }
  85. }
  86. void applyFontAndColour (Array<AttributedString::Attribute>& atts,
  87. Range<int> range, const Font* f, const Colour* c)
  88. {
  89. range = splitAttributeRanges (atts, range);
  90. for (auto& att : atts)
  91. {
  92. if (range.getStart() < att.range.getEnd())
  93. {
  94. if (range.getEnd() <= att.range.getStart())
  95. break;
  96. if (c != nullptr) att.colour = *c;
  97. if (f != nullptr) att.font = *f;
  98. }
  99. }
  100. mergeAdjacentRanges (atts);
  101. }
  102. void truncate (Array<AttributedString::Attribute>& atts, int newLength)
  103. {
  104. splitAttributeRanges (atts, newLength);
  105. for (int i = atts.size(); --i >= 0;)
  106. if (atts.getReference (i).range.getStart() >= newLength)
  107. atts.remove (i);
  108. }
  109. }
  110. //==============================================================================
  111. AttributedString::Attribute::Attribute (Attribute&& other) noexcept
  112. : range (other.range),
  113. font (std::move (other.font)),
  114. colour (other.colour)
  115. {
  116. }
  117. AttributedString::Attribute& AttributedString::Attribute::operator= (Attribute&& other) noexcept
  118. {
  119. range = other.range;
  120. font = std::move (other.font);
  121. colour = other.colour;
  122. return *this;
  123. }
  124. AttributedString::Attribute::Attribute (Range<int> r, const Font& f, Colour c) noexcept
  125. : range (r), font (f), colour (c)
  126. {
  127. }
  128. //==============================================================================
  129. AttributedString::AttributedString (AttributedString&& other) noexcept
  130. : text (std::move (other.text)),
  131. lineSpacing (other.lineSpacing),
  132. justification (other.justification),
  133. wordWrap (other.wordWrap),
  134. readingDirection (other.readingDirection),
  135. attributes (std::move (other.attributes))
  136. {
  137. }
  138. AttributedString& AttributedString::operator= (AttributedString&& other) noexcept
  139. {
  140. text = std::move (other.text);
  141. lineSpacing = other.lineSpacing;
  142. justification = other.justification;
  143. wordWrap = other.wordWrap;
  144. readingDirection = other.readingDirection;
  145. attributes = std::move (other.attributes);
  146. return *this;
  147. }
  148. void AttributedString::setText (const String& newText)
  149. {
  150. auto newLength = newText.length();
  151. auto oldLength = getLength (attributes);
  152. if (newLength > oldLength)
  153. appendRange (attributes, newLength - oldLength, nullptr, nullptr);
  154. else if (newLength < oldLength)
  155. truncate (attributes, newLength);
  156. text = newText;
  157. }
  158. void AttributedString::append (const String& textToAppend)
  159. {
  160. text += textToAppend;
  161. appendRange (attributes, textToAppend.length(), nullptr, nullptr);
  162. }
  163. void AttributedString::append (const String& textToAppend, const Font& font)
  164. {
  165. text += textToAppend;
  166. appendRange (attributes, textToAppend.length(), &font, nullptr);
  167. }
  168. void AttributedString::append (const String& textToAppend, Colour colour)
  169. {
  170. text += textToAppend;
  171. appendRange (attributes, textToAppend.length(), nullptr, &colour);
  172. }
  173. void AttributedString::append (const String& textToAppend, const Font& font, Colour colour)
  174. {
  175. text += textToAppend;
  176. appendRange (attributes, textToAppend.length(), &font, &colour);
  177. }
  178. void AttributedString::append (const AttributedString& other)
  179. {
  180. auto originalLength = getLength (attributes);
  181. auto originalNumAtts = attributes.size();
  182. text += other.text;
  183. attributes.addArray (other.attributes);
  184. for (auto i = originalNumAtts; i < attributes.size(); ++i)
  185. attributes.getReference (i).range += originalLength;
  186. mergeAdjacentRanges (attributes);
  187. }
  188. void AttributedString::clear()
  189. {
  190. text.clear();
  191. attributes.clear();
  192. }
  193. void AttributedString::setJustification (Justification newJustification) noexcept
  194. {
  195. justification = newJustification;
  196. }
  197. void AttributedString::setWordWrap (WordWrap newWordWrap) noexcept
  198. {
  199. wordWrap = newWordWrap;
  200. }
  201. void AttributedString::setReadingDirection (ReadingDirection newReadingDirection) noexcept
  202. {
  203. readingDirection = newReadingDirection;
  204. }
  205. void AttributedString::setLineSpacing (const float newLineSpacing) noexcept
  206. {
  207. lineSpacing = newLineSpacing;
  208. }
  209. void AttributedString::setColour (Range<int> range, Colour colour)
  210. {
  211. applyFontAndColour (attributes, range, nullptr, &colour);
  212. }
  213. void AttributedString::setFont (Range<int> range, const Font& font)
  214. {
  215. applyFontAndColour (attributes, range, &font, nullptr);
  216. }
  217. void AttributedString::setColour (Colour colour)
  218. {
  219. setColour ({ 0, getLength (attributes) }, colour);
  220. }
  221. void AttributedString::setFont (const Font& font)
  222. {
  223. setFont ({ 0, getLength (attributes) }, font);
  224. }
  225. void AttributedString::draw (Graphics& g, const Rectangle<float>& area) const
  226. {
  227. if (text.isNotEmpty() && g.clipRegionIntersects (area.getSmallestIntegerContainer()))
  228. {
  229. jassert (text.length() == getLength (attributes));
  230. if (! g.getInternalContext().drawTextLayout (*this, area))
  231. {
  232. TextLayout layout;
  233. layout.createLayout (*this, area.getWidth());
  234. layout.draw (g, area);
  235. }
  236. }
  237. }
  238. } // namespace juce