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_DrawableText.cpp 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  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. DrawableText::DrawableText()
  20. : colour (Colours::black),
  21. justification (Justification::centredLeft)
  22. {
  23. setBoundingBox (RelativeParallelogram (RelativePoint (0.0f, 0.0f),
  24. RelativePoint (50.0f, 0.0f),
  25. RelativePoint (0.0f, 20.0f)));
  26. setFont (Font (15.0f), true);
  27. }
  28. DrawableText::DrawableText (const DrawableText& other)
  29. : Drawable (other),
  30. bounds (other.bounds),
  31. fontHeight (other.fontHeight),
  32. fontHScale (other.fontHScale),
  33. font (other.font),
  34. text (other.text),
  35. colour (other.colour),
  36. justification (other.justification)
  37. {
  38. refreshBounds();
  39. }
  40. DrawableText::~DrawableText()
  41. {
  42. }
  43. //==============================================================================
  44. void DrawableText::setText (const String& newText)
  45. {
  46. if (text != newText)
  47. {
  48. text = newText;
  49. refreshBounds();
  50. }
  51. }
  52. void DrawableText::setColour (Colour newColour)
  53. {
  54. if (colour != newColour)
  55. {
  56. colour = newColour;
  57. repaint();
  58. }
  59. }
  60. void DrawableText::setFont (const Font& newFont, bool applySizeAndScale)
  61. {
  62. if (font != newFont)
  63. {
  64. font = newFont;
  65. if (applySizeAndScale)
  66. {
  67. fontHeight = font.getHeight();
  68. fontHScale = font.getHorizontalScale();
  69. }
  70. refreshBounds();
  71. }
  72. }
  73. void DrawableText::setJustification (Justification newJustification)
  74. {
  75. justification = newJustification;
  76. repaint();
  77. }
  78. void DrawableText::setBoundingBox (const RelativeParallelogram& newBounds)
  79. {
  80. if (bounds != newBounds)
  81. {
  82. bounds = newBounds;
  83. refreshBounds();
  84. }
  85. }
  86. void DrawableText::setFontHeight (const RelativeCoordinate& newHeight)
  87. {
  88. if (fontHeight != newHeight)
  89. {
  90. fontHeight = newHeight;
  91. refreshBounds();
  92. }
  93. }
  94. void DrawableText::setFontHorizontalScale (const RelativeCoordinate& newScale)
  95. {
  96. if (fontHScale != newScale)
  97. {
  98. fontHScale = newScale;
  99. refreshBounds();
  100. }
  101. }
  102. void DrawableText::refreshBounds()
  103. {
  104. if (bounds.isDynamic() || fontHeight.isDynamic() || fontHScale.isDynamic())
  105. {
  106. Drawable::Positioner<DrawableText>* const p = new Drawable::Positioner<DrawableText> (*this);
  107. setPositioner (p);
  108. p->apply();
  109. }
  110. else
  111. {
  112. setPositioner (0);
  113. recalculateCoordinates (0);
  114. }
  115. }
  116. bool DrawableText::registerCoordinates (RelativeCoordinatePositionerBase& pos)
  117. {
  118. bool ok = pos.addPoint (bounds.topLeft);
  119. ok = pos.addPoint (bounds.topRight) && ok;
  120. ok = pos.addPoint (bounds.bottomLeft) && ok;
  121. ok = pos.addCoordinate (fontHeight) && ok;
  122. return pos.addCoordinate (fontHScale) && ok;
  123. }
  124. void DrawableText::recalculateCoordinates (Expression::Scope* scope)
  125. {
  126. bounds.resolveThreePoints (resolvedPoints, scope);
  127. const float w = Line<float> (resolvedPoints[0], resolvedPoints[1]).getLength();
  128. const float h = Line<float> (resolvedPoints[0], resolvedPoints[2]).getLength();
  129. const float height = jlimit (0.01f, jmax (0.01f, h), (float) fontHeight.resolve (scope));
  130. const float hscale = jlimit (0.01f, jmax (0.01f, w), (float) fontHScale.resolve (scope));
  131. scaledFont = font;
  132. scaledFont.setHeight (height);
  133. scaledFont.setHorizontalScale (hscale);
  134. setBoundsToEnclose (getDrawableBounds());
  135. repaint();
  136. }
  137. //==============================================================================
  138. Rectangle<int> DrawableText::getTextArea (float w, float h) const
  139. {
  140. return Rectangle<float> (w, h).getSmallestIntegerContainer();
  141. }
  142. AffineTransform DrawableText::getTextTransform (float w, float h) const
  143. {
  144. return AffineTransform::fromTargetPoints (0, 0, resolvedPoints[0].x, resolvedPoints[0].y,
  145. w, 0, resolvedPoints[1].x, resolvedPoints[1].y,
  146. 0, h, resolvedPoints[2].x, resolvedPoints[2].y);
  147. }
  148. void DrawableText::paint (Graphics& g)
  149. {
  150. transformContextToCorrectOrigin (g);
  151. const float w = Line<float> (resolvedPoints[0], resolvedPoints[1]).getLength();
  152. const float h = Line<float> (resolvedPoints[0], resolvedPoints[2]).getLength();
  153. g.addTransform (getTextTransform (w, h));
  154. g.setFont (scaledFont);
  155. g.setColour (colour);
  156. g.drawFittedText (text, getTextArea (w, h), justification, 0x100000);
  157. }
  158. Rectangle<float> DrawableText::getDrawableBounds() const
  159. {
  160. return RelativeParallelogram::getBoundingBox (resolvedPoints);
  161. }
  162. Drawable* DrawableText::createCopy() const
  163. {
  164. return new DrawableText (*this);
  165. }
  166. //==============================================================================
  167. const Identifier DrawableText::valueTreeType ("Text");
  168. const Identifier DrawableText::ValueTreeWrapper::text ("text");
  169. const Identifier DrawableText::ValueTreeWrapper::colour ("colour");
  170. const Identifier DrawableText::ValueTreeWrapper::font ("font");
  171. const Identifier DrawableText::ValueTreeWrapper::justification ("justification");
  172. const Identifier DrawableText::ValueTreeWrapper::topLeft ("topLeft");
  173. const Identifier DrawableText::ValueTreeWrapper::topRight ("topRight");
  174. const Identifier DrawableText::ValueTreeWrapper::bottomLeft ("bottomLeft");
  175. const Identifier DrawableText::ValueTreeWrapper::fontHeight ("fontHeight");
  176. const Identifier DrawableText::ValueTreeWrapper::fontHScale ("fontHScale");
  177. //==============================================================================
  178. DrawableText::ValueTreeWrapper::ValueTreeWrapper (const ValueTree& state_)
  179. : ValueTreeWrapperBase (state_)
  180. {
  181. jassert (state.hasType (valueTreeType));
  182. }
  183. String DrawableText::ValueTreeWrapper::getText() const
  184. {
  185. return state [text].toString();
  186. }
  187. void DrawableText::ValueTreeWrapper::setText (const String& newText, UndoManager* undoManager)
  188. {
  189. state.setProperty (text, newText, undoManager);
  190. }
  191. Value DrawableText::ValueTreeWrapper::getTextValue (UndoManager* undoManager)
  192. {
  193. return state.getPropertyAsValue (text, undoManager);
  194. }
  195. Colour DrawableText::ValueTreeWrapper::getColour() const
  196. {
  197. return Colour::fromString (state [colour].toString());
  198. }
  199. void DrawableText::ValueTreeWrapper::setColour (Colour newColour, UndoManager* undoManager)
  200. {
  201. state.setProperty (colour, newColour.toString(), undoManager);
  202. }
  203. Justification DrawableText::ValueTreeWrapper::getJustification() const
  204. {
  205. return Justification ((int) state [justification]);
  206. }
  207. void DrawableText::ValueTreeWrapper::setJustification (Justification newJustification, UndoManager* undoManager)
  208. {
  209. state.setProperty (justification, newJustification.getFlags(), undoManager);
  210. }
  211. Font DrawableText::ValueTreeWrapper::getFont() const
  212. {
  213. return Font::fromString (state [font]);
  214. }
  215. void DrawableText::ValueTreeWrapper::setFont (const Font& newFont, UndoManager* undoManager)
  216. {
  217. state.setProperty (font, newFont.toString(), undoManager);
  218. }
  219. Value DrawableText::ValueTreeWrapper::getFontValue (UndoManager* undoManager)
  220. {
  221. return state.getPropertyAsValue (font, undoManager);
  222. }
  223. RelativeParallelogram DrawableText::ValueTreeWrapper::getBoundingBox() const
  224. {
  225. return RelativeParallelogram (state [topLeft].toString(), state [topRight].toString(), state [bottomLeft].toString());
  226. }
  227. void DrawableText::ValueTreeWrapper::setBoundingBox (const RelativeParallelogram& newBounds, UndoManager* undoManager)
  228. {
  229. state.setProperty (topLeft, newBounds.topLeft.toString(), undoManager);
  230. state.setProperty (topRight, newBounds.topRight.toString(), undoManager);
  231. state.setProperty (bottomLeft, newBounds.bottomLeft.toString(), undoManager);
  232. }
  233. RelativeCoordinate DrawableText::ValueTreeWrapper::getFontHeight() const
  234. {
  235. return state [fontHeight].toString();
  236. }
  237. void DrawableText::ValueTreeWrapper::setFontHeight (const RelativeCoordinate& coord, UndoManager* undoManager)
  238. {
  239. state.setProperty (fontHeight, coord.toString(), undoManager);
  240. }
  241. RelativeCoordinate DrawableText::ValueTreeWrapper::getFontHorizontalScale() const
  242. {
  243. return state [fontHScale].toString();
  244. }
  245. void DrawableText::ValueTreeWrapper::setFontHorizontalScale (const RelativeCoordinate& coord, UndoManager* undoManager)
  246. {
  247. state.setProperty (fontHScale, coord.toString(), undoManager);
  248. }
  249. //==============================================================================
  250. void DrawableText::refreshFromValueTree (const ValueTree& tree, ComponentBuilder&)
  251. {
  252. ValueTreeWrapper v (tree);
  253. setComponentID (v.getID());
  254. const RelativeParallelogram newBounds (v.getBoundingBox());
  255. const RelativeCoordinate newFontHeight (v.getFontHeight());
  256. const RelativeCoordinate newFontHScale (v.getFontHorizontalScale());
  257. const Colour newColour (v.getColour());
  258. const Justification newJustification (v.getJustification());
  259. const String newText (v.getText());
  260. const Font newFont (v.getFont());
  261. if (text != newText || font != newFont || justification != newJustification
  262. || colour != newColour || bounds != newBounds
  263. || newFontHeight != fontHeight || newFontHScale != fontHScale)
  264. {
  265. setBoundingBox (newBounds);
  266. setFontHeight (newFontHeight);
  267. setFontHorizontalScale (newFontHScale);
  268. setColour (newColour);
  269. setFont (newFont, false);
  270. setJustification (newJustification);
  271. setText (newText);
  272. }
  273. }
  274. ValueTree DrawableText::createValueTree (ComponentBuilder::ImageProvider*) const
  275. {
  276. ValueTree tree (valueTreeType);
  277. ValueTreeWrapper v (tree);
  278. v.setID (getComponentID());
  279. v.setText (text, nullptr);
  280. v.setFont (font, nullptr);
  281. v.setJustification (justification, nullptr);
  282. v.setColour (colour, nullptr);
  283. v.setBoundingBox (bounds, nullptr);
  284. v.setFontHeight (fontHeight, nullptr);
  285. v.setFontHorizontalScale (fontHScale, nullptr);
  286. return tree;
  287. }
  288. Path DrawableText::getOutlineAsPath() const
  289. {
  290. auto w = Line<float> (resolvedPoints[0], resolvedPoints[1]).getLength();
  291. auto h = Line<float> (resolvedPoints[0], resolvedPoints[2]).getLength();
  292. const auto area = getTextArea (w, h).toFloat();
  293. GlyphArrangement arr;
  294. arr.addFittedText (scaledFont, text,
  295. area.getX(), area.getY(),
  296. area.getWidth(), area.getHeight(),
  297. justification,
  298. 0x100000);
  299. Path pathOfAllGlyphs;
  300. for (int i = 0; i < arr.getNumGlyphs(); ++i)
  301. {
  302. Path gylphPath;
  303. arr.getGlyph (i).createPath (gylphPath);
  304. pathOfAllGlyphs.addPath (gylphPath);
  305. }
  306. pathOfAllGlyphs.applyTransform (getTextTransform (w, h)
  307. .followedBy (getTransform()));
  308. return pathOfAllGlyphs;
  309. }