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

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