juce_AttributedString.cpp 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  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 (Range<int> r, const Font& f, Colour c) noexcept
  112. : range (r), font (f), colour (c)
  113. {
  114. }
  115. //==============================================================================
  116. void AttributedString::setText (const String& newText)
  117. {
  118. auto newLength = newText.length();
  119. auto oldLength = getLength (attributes);
  120. if (newLength > oldLength)
  121. appendRange (attributes, newLength - oldLength, nullptr, nullptr);
  122. else if (newLength < oldLength)
  123. truncate (attributes, newLength);
  124. text = newText;
  125. }
  126. void AttributedString::append (const String& textToAppend)
  127. {
  128. text += textToAppend;
  129. appendRange (attributes, textToAppend.length(), nullptr, nullptr);
  130. }
  131. void AttributedString::append (const String& textToAppend, const Font& font)
  132. {
  133. text += textToAppend;
  134. appendRange (attributes, textToAppend.length(), &font, nullptr);
  135. }
  136. void AttributedString::append (const String& textToAppend, Colour colour)
  137. {
  138. text += textToAppend;
  139. appendRange (attributes, textToAppend.length(), nullptr, &colour);
  140. }
  141. void AttributedString::append (const String& textToAppend, const Font& font, Colour colour)
  142. {
  143. text += textToAppend;
  144. appendRange (attributes, textToAppend.length(), &font, &colour);
  145. }
  146. void AttributedString::append (const AttributedString& other)
  147. {
  148. auto originalLength = getLength (attributes);
  149. auto originalNumAtts = attributes.size();
  150. text += other.text;
  151. attributes.addArray (other.attributes);
  152. for (auto i = originalNumAtts; i < attributes.size(); ++i)
  153. attributes.getReference (i).range += originalLength;
  154. mergeAdjacentRanges (attributes);
  155. }
  156. void AttributedString::clear()
  157. {
  158. text.clear();
  159. attributes.clear();
  160. }
  161. void AttributedString::setJustification (Justification newJustification) noexcept
  162. {
  163. justification = newJustification;
  164. }
  165. void AttributedString::setWordWrap (WordWrap newWordWrap) noexcept
  166. {
  167. wordWrap = newWordWrap;
  168. }
  169. void AttributedString::setReadingDirection (ReadingDirection newReadingDirection) noexcept
  170. {
  171. readingDirection = newReadingDirection;
  172. }
  173. void AttributedString::setLineSpacing (const float newLineSpacing) noexcept
  174. {
  175. lineSpacing = newLineSpacing;
  176. }
  177. void AttributedString::setColour (Range<int> range, Colour colour)
  178. {
  179. applyFontAndColour (attributes, range, nullptr, &colour);
  180. }
  181. void AttributedString::setFont (Range<int> range, const Font& font)
  182. {
  183. applyFontAndColour (attributes, range, &font, nullptr);
  184. }
  185. void AttributedString::setColour (Colour colour)
  186. {
  187. setColour ({ 0, getLength (attributes) }, colour);
  188. }
  189. void AttributedString::setFont (const Font& font)
  190. {
  191. setFont ({ 0, getLength (attributes) }, font);
  192. }
  193. void AttributedString::draw (Graphics& g, const Rectangle<float>& area) const
  194. {
  195. if (text.isNotEmpty() && g.clipRegionIntersects (area.getSmallestIntegerContainer()))
  196. {
  197. jassert (text.length() == getLength (attributes));
  198. if (! g.getInternalContext().drawTextLayout (*this, area))
  199. {
  200. TextLayout layout;
  201. layout.createLayout (*this, area.getWidth());
  202. layout.draw (g, area);
  203. }
  204. }
  205. }
  206. } // namespace juce