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 7.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2020 - 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 6 End-User License
  8. Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
  9. End User License Agreement: www.juce.com/juce-6-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. Range<int> splitAttributeRanges (Array<AttributedString::Attribute>& atts, Range<int> newRange)
  45. {
  46. newRange = newRange.getIntersectionWith ({ 0, getLength (atts) });
  47. if (! newRange.isEmpty())
  48. {
  49. splitAttributeRanges (atts, newRange.getStart());
  50. splitAttributeRanges (atts, newRange.getEnd());
  51. }
  52. return newRange;
  53. }
  54. void mergeAdjacentRanges (Array<AttributedString::Attribute>& atts)
  55. {
  56. for (int i = atts.size() - 1; --i >= 0;)
  57. {
  58. auto& a1 = atts.getReference (i);
  59. auto& a2 = atts.getReference (i + 1);
  60. if (a1.colour == a2.colour && a1.font == a2.font)
  61. {
  62. a1.range.setEnd (a2.range.getEnd());
  63. atts.remove (i + 1);
  64. if (i < atts.size() - 1)
  65. ++i;
  66. }
  67. }
  68. }
  69. void appendRange (Array<AttributedString::Attribute>& atts,
  70. int length, const Font* f, const Colour* c)
  71. {
  72. if (atts.size() == 0)
  73. {
  74. atts.add ({ Range<int> (0, length), f != nullptr ? *f : Font(), c != nullptr ? *c : Colour (0xff000000) });
  75. }
  76. else
  77. {
  78. auto start = getLength (atts);
  79. atts.add ({ Range<int> (start, start + length),
  80. f != nullptr ? *f : atts.getReference (atts.size() - 1).font,
  81. c != nullptr ? *c : atts.getReference (atts.size() - 1).colour });
  82. mergeAdjacentRanges (atts);
  83. }
  84. }
  85. void applyFontAndColour (Array<AttributedString::Attribute>& atts,
  86. Range<int> range, const Font* f, const Colour* c)
  87. {
  88. range = splitAttributeRanges (atts, range);
  89. for (auto& att : atts)
  90. {
  91. if (range.getStart() < att.range.getEnd())
  92. {
  93. if (range.getEnd() <= att.range.getStart())
  94. break;
  95. if (c != nullptr) att.colour = *c;
  96. if (f != nullptr) att.font = *f;
  97. }
  98. }
  99. mergeAdjacentRanges (atts);
  100. }
  101. void truncate (Array<AttributedString::Attribute>& atts, int newLength)
  102. {
  103. splitAttributeRanges (atts, newLength);
  104. for (int i = atts.size(); --i >= 0;)
  105. if (atts.getReference (i).range.getStart() >= newLength)
  106. atts.remove (i);
  107. }
  108. }
  109. //==============================================================================
  110. AttributedString::Attribute::Attribute (Range<int> r, const Font& f, Colour c) noexcept
  111. : range (r), font (f), colour (c)
  112. {
  113. }
  114. //==============================================================================
  115. void AttributedString::setText (const String& newText)
  116. {
  117. auto newLength = newText.length();
  118. auto oldLength = getLength (attributes);
  119. if (newLength > oldLength)
  120. appendRange (attributes, newLength - oldLength, nullptr, nullptr);
  121. else if (newLength < oldLength)
  122. truncate (attributes, newLength);
  123. text = newText;
  124. }
  125. void AttributedString::append (const String& textToAppend)
  126. {
  127. text += textToAppend;
  128. appendRange (attributes, textToAppend.length(), nullptr, nullptr);
  129. }
  130. void AttributedString::append (const String& textToAppend, const Font& font)
  131. {
  132. text += textToAppend;
  133. appendRange (attributes, textToAppend.length(), &font, nullptr);
  134. }
  135. void AttributedString::append (const String& textToAppend, Colour colour)
  136. {
  137. text += textToAppend;
  138. appendRange (attributes, textToAppend.length(), nullptr, &colour);
  139. }
  140. void AttributedString::append (const String& textToAppend, const Font& font, Colour colour)
  141. {
  142. text += textToAppend;
  143. appendRange (attributes, textToAppend.length(), &font, &colour);
  144. }
  145. void AttributedString::append (const AttributedString& other)
  146. {
  147. auto originalLength = getLength (attributes);
  148. auto originalNumAtts = attributes.size();
  149. text += other.text;
  150. attributes.addArray (other.attributes);
  151. for (auto i = originalNumAtts; i < attributes.size(); ++i)
  152. attributes.getReference (i).range += originalLength;
  153. mergeAdjacentRanges (attributes);
  154. }
  155. void AttributedString::clear()
  156. {
  157. text.clear();
  158. attributes.clear();
  159. }
  160. void AttributedString::setJustification (Justification newJustification) noexcept
  161. {
  162. justification = newJustification;
  163. }
  164. void AttributedString::setWordWrap (WordWrap newWordWrap) noexcept
  165. {
  166. wordWrap = newWordWrap;
  167. }
  168. void AttributedString::setReadingDirection (ReadingDirection newReadingDirection) noexcept
  169. {
  170. readingDirection = newReadingDirection;
  171. }
  172. void AttributedString::setLineSpacing (const float newLineSpacing) noexcept
  173. {
  174. lineSpacing = newLineSpacing;
  175. }
  176. void AttributedString::setColour (Range<int> range, Colour colour)
  177. {
  178. applyFontAndColour (attributes, range, nullptr, &colour);
  179. }
  180. void AttributedString::setFont (Range<int> range, const Font& font)
  181. {
  182. applyFontAndColour (attributes, range, &font, nullptr);
  183. }
  184. void AttributedString::setColour (Colour colour)
  185. {
  186. setColour ({ 0, getLength (attributes) }, colour);
  187. }
  188. void AttributedString::setFont (const Font& font)
  189. {
  190. setFont ({ 0, getLength (attributes) }, font);
  191. }
  192. void AttributedString::draw (Graphics& g, const Rectangle<float>& area) const
  193. {
  194. if (text.isNotEmpty() && g.clipRegionIntersects (area.getSmallestIntegerContainer()))
  195. {
  196. jassert (text.length() == getLength (attributes));
  197. if (! g.getInternalContext().drawTextLayout (*this, area))
  198. {
  199. TextLayout layout;
  200. layout.createLayout (*this, area.getWidth());
  201. layout.draw (g, area);
  202. }
  203. }
  204. }
  205. } // namespace juce