Audio plugin host https://kx.studio/carla
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.

juce_AttributedString.cpp 8.6KB

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