The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
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.

660 lines
20KB

  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. #pragma once
  20. #include "jucer_ColouredElement.h"
  21. #include "../properties/jucer_FontPropertyComponent.h"
  22. #include "../properties/jucer_JustificationProperty.h"
  23. //==============================================================================
  24. class PaintElementText : public ColouredElement
  25. {
  26. public:
  27. PaintElementText (PaintRoutine* pr)
  28. : ColouredElement (pr, "Text", false, false),
  29. text ("Your text goes here"),
  30. font (15.0f),
  31. typefaceName (FontPropertyComponent::getDefaultFont()),
  32. justification (Justification::centred)
  33. {
  34. fillType.colour = Colours::black;
  35. position.rect.setWidth (200);
  36. position.rect.setHeight (30);
  37. }
  38. //==============================================================================
  39. void draw (Graphics& g, const ComponentLayout* layout, const Rectangle<int>& parentArea)
  40. {
  41. fillType.setFillType (g, getDocument(), parentArea);
  42. font = FontPropertyComponent::applyNameToFont (typefaceName, font);
  43. g.setFont (font);
  44. g.drawText (replaceStringTranslations (text, owner->getDocument()),
  45. position.getRectangle (parentArea, layout), justification, true);
  46. }
  47. static String replaceStringTranslations (String s, JucerDocument* document)
  48. {
  49. s = s.replace ("%%getName()%%", document->getComponentName());
  50. s = s.replace ("%%getButtonText()%%", document->getComponentName());
  51. return s;
  52. }
  53. void getEditableProperties (Array<PropertyComponent*>& props)
  54. {
  55. ColouredElement::getEditableProperties (props);
  56. props.add (new TextProperty (this));
  57. props.add (new FontNameProperty (this));
  58. props.add (new FontStyleProperty (this));
  59. props.add (new FontSizeProperty (this));
  60. props.add (new FontKerningProperty (this));
  61. props.add (new TextJustificationProperty (this));
  62. props.add (new TextToPathProperty (this));
  63. }
  64. void fillInGeneratedCode (GeneratedCode& code, String& paintMethodCode)
  65. {
  66. if (! fillType.isInvisible())
  67. {
  68. String r;
  69. fillType.fillInGeneratedCode (code, paintMethodCode);
  70. String x, y, w, h;
  71. positionToCode (position, code.document->getComponentLayout(), x, y, w, h);
  72. r << "g.setFont ("
  73. << FontPropertyComponent::getCompleteFontCode (font, typefaceName)
  74. << ");\ng.drawText ("
  75. << quotedString (text, code.shouldUseTransMacro())
  76. << ",\n "
  77. << x << ", " << y << ", " << w << ", " << h
  78. << ",\n "
  79. << CodeHelpers::justificationToCode (justification)
  80. << ", true);\n\n";
  81. paintMethodCode += r;
  82. }
  83. }
  84. static const char* getTagName() noexcept { return "TEXT"; }
  85. XmlElement* createXml() const
  86. {
  87. XmlElement* e = new XmlElement (getTagName());
  88. position.applyToXml (*e);
  89. addColourAttributes (e);
  90. e->setAttribute ("text", text);
  91. e->setAttribute ("fontname", typefaceName);
  92. e->setAttribute ("fontsize", roundToInt (font.getHeight() * 100.0) / 100.0);
  93. e->setAttribute ("kerning", roundToInt (font.getExtraKerningFactor() * 1000.0) / 1000.0);
  94. e->setAttribute ("bold", font.isBold());
  95. e->setAttribute ("italic", font.isItalic());
  96. e->setAttribute ("justification", justification.getFlags());
  97. if (font.getTypefaceStyle() != "Regular")
  98. {
  99. e->setAttribute ("typefaceStyle", font.getTypefaceStyle());
  100. }
  101. return e;
  102. }
  103. bool loadFromXml (const XmlElement& xml)
  104. {
  105. if (xml.hasTagName (getTagName()))
  106. {
  107. position.restoreFromXml (xml, position);
  108. loadColourAttributes (xml);
  109. text = xml.getStringAttribute ("text", "Hello World");
  110. typefaceName = xml.getStringAttribute ("fontname", FontPropertyComponent::getDefaultFont());
  111. font.setHeight ((float) xml.getDoubleAttribute ("fontsize", 15.0));
  112. font.setBold (xml.getBoolAttribute ("bold", false));
  113. font.setItalic (xml.getBoolAttribute ("italic", false));
  114. font.setExtraKerningFactor ((float) xml.getDoubleAttribute ("kerning", 0.0));
  115. justification = Justification (xml.getIntAttribute ("justification", Justification::centred));
  116. auto fontStyle = xml.getStringAttribute ("typefaceStyle");
  117. if (! fontStyle.isEmpty())
  118. font.setTypefaceStyle (fontStyle);
  119. return true;
  120. }
  121. jassertfalse;
  122. return false;
  123. }
  124. //==============================================================================
  125. const String& getText() const noexcept { return text; }
  126. class SetTextAction : public PaintElementUndoableAction <PaintElementText>
  127. {
  128. public:
  129. SetTextAction (PaintElementText* const element, const String& newText_)
  130. : PaintElementUndoableAction <PaintElementText> (element),
  131. newText (newText_),
  132. oldText (element->getText())
  133. {
  134. }
  135. bool perform()
  136. {
  137. showCorrectTab();
  138. getElement()->setText (newText, false);
  139. return true;
  140. }
  141. bool undo()
  142. {
  143. showCorrectTab();
  144. getElement()->setText (oldText, false);
  145. return true;
  146. }
  147. private:
  148. String newText, oldText;
  149. };
  150. void setText (const String& t, const bool undoable)
  151. {
  152. if (t != text)
  153. {
  154. if (undoable)
  155. {
  156. perform (new SetTextAction (this, t),
  157. "Change text element text");
  158. }
  159. else
  160. {
  161. text = t;
  162. changed();
  163. }
  164. }
  165. }
  166. //==============================================================================
  167. const Font& getFont() const { return font; }
  168. class SetFontAction : public PaintElementUndoableAction <PaintElementText>
  169. {
  170. public:
  171. SetFontAction (PaintElementText* const element, const Font& newFont_)
  172. : PaintElementUndoableAction <PaintElementText> (element),
  173. newFont (newFont_),
  174. oldFont (element->getFont())
  175. {
  176. }
  177. bool perform()
  178. {
  179. showCorrectTab();
  180. getElement()->setFont (newFont, false);
  181. return true;
  182. }
  183. bool undo()
  184. {
  185. showCorrectTab();
  186. getElement()->setFont (oldFont, false);
  187. return true;
  188. }
  189. private:
  190. Font newFont, oldFont;
  191. };
  192. void setFont (const Font& newFont, const bool undoable)
  193. {
  194. if (font != newFont)
  195. {
  196. if (undoable)
  197. {
  198. perform (new SetFontAction (this, newFont),
  199. "Change text element font");
  200. }
  201. else
  202. {
  203. font = newFont;
  204. changed();
  205. }
  206. }
  207. }
  208. //==============================================================================
  209. class SetTypefaceAction : public PaintElementUndoableAction <PaintElementText>
  210. {
  211. public:
  212. SetTypefaceAction (PaintElementText* const element, const String& newValue_)
  213. : PaintElementUndoableAction <PaintElementText> (element),
  214. newValue (newValue_),
  215. oldValue (element->getTypefaceName())
  216. {
  217. }
  218. bool perform()
  219. {
  220. showCorrectTab();
  221. getElement()->setTypefaceName (newValue, false);
  222. return true;
  223. }
  224. bool undo()
  225. {
  226. showCorrectTab();
  227. getElement()->setTypefaceName (oldValue, false);
  228. return true;
  229. }
  230. private:
  231. String newValue, oldValue;
  232. };
  233. void setTypefaceName (const String& newFontName, const bool undoable)
  234. {
  235. if (undoable)
  236. {
  237. perform (new SetTypefaceAction (this, newFontName),
  238. "Change text element typeface");
  239. }
  240. else
  241. {
  242. typefaceName = newFontName;
  243. changed();
  244. }
  245. }
  246. String getTypefaceName() const noexcept { return typefaceName; }
  247. //==============================================================================
  248. Justification getJustification() const noexcept { return justification; }
  249. class SetJustifyAction : public PaintElementUndoableAction <PaintElementText>
  250. {
  251. public:
  252. SetJustifyAction (PaintElementText* const element, Justification newValue_)
  253. : PaintElementUndoableAction <PaintElementText> (element),
  254. newValue (newValue_),
  255. oldValue (element->getJustification())
  256. {
  257. }
  258. bool perform()
  259. {
  260. showCorrectTab();
  261. getElement()->setJustification (newValue, false);
  262. return true;
  263. }
  264. bool undo()
  265. {
  266. showCorrectTab();
  267. getElement()->setJustification (oldValue, false);
  268. return true;
  269. }
  270. private:
  271. Justification newValue, oldValue;
  272. };
  273. void setJustification (Justification j, const bool undoable)
  274. {
  275. if (justification.getFlags() != j.getFlags())
  276. {
  277. if (undoable)
  278. {
  279. perform (new SetJustifyAction (this, j),
  280. "Change text element justification");
  281. }
  282. else
  283. {
  284. justification = j;
  285. changed();
  286. }
  287. }
  288. }
  289. void convertToPath()
  290. {
  291. if (PaintRoutineEditor* parent = dynamic_cast<PaintRoutineEditor*> (getParentComponent()))
  292. {
  293. font = FontPropertyComponent::applyNameToFont (typefaceName, font);
  294. const Rectangle<int> r =
  295. getCurrentBounds (parent->getComponentArea().withZeroOrigin());
  296. GlyphArrangement arr;
  297. arr.addCurtailedLineOfText (font, text,
  298. 0.0f, 0.0f, (float) r.getWidth(),
  299. true);
  300. arr.justifyGlyphs (0, arr.getNumGlyphs(),
  301. (float) r.getX(), (float) r.getY(),
  302. (float) r.getWidth(), (float) r.getHeight(),
  303. justification);
  304. Path path;
  305. arr.createPath (path);
  306. convertToNewPathElement (path);
  307. }
  308. else
  309. {
  310. jassertfalse;
  311. }
  312. }
  313. private:
  314. String text;
  315. Font font;
  316. String typefaceName;
  317. Justification justification;
  318. Array <Justification> justificationTypes;
  319. //==============================================================================
  320. class TextProperty : public TextPropertyComponent,
  321. public ChangeListener
  322. {
  323. public:
  324. TextProperty (PaintElementText* const e)
  325. : TextPropertyComponent ("text", 2048, false),
  326. element (e)
  327. {
  328. element->getDocument()->addChangeListener (this);
  329. }
  330. ~TextProperty()
  331. {
  332. element->getDocument()->removeChangeListener (this);
  333. }
  334. void setText (const String& newText) override { element->setText (newText, true); }
  335. String getText() const override { return element->getText(); }
  336. void changeListenerCallback (ChangeBroadcaster*) override { refresh(); }
  337. private:
  338. PaintElementText* const element;
  339. };
  340. //==============================================================================
  341. class FontNameProperty : public FontPropertyComponent,
  342. public ChangeListener
  343. {
  344. public:
  345. FontNameProperty (PaintElementText* const e)
  346. : FontPropertyComponent ("font"),
  347. element (e)
  348. {
  349. element->getDocument()->addChangeListener (this);
  350. }
  351. ~FontNameProperty()
  352. {
  353. element->getDocument()->removeChangeListener (this);
  354. }
  355. void setTypefaceName (const String& newFontName) { element->setTypefaceName (newFontName, true); }
  356. String getTypefaceName() const { return element->getTypefaceName(); }
  357. void changeListenerCallback (ChangeBroadcaster*) { refresh(); }
  358. private:
  359. PaintElementText* const element;
  360. };
  361. //==============================================================================
  362. class FontStyleProperty : public ChoicePropertyComponent,
  363. public ChangeListener
  364. {
  365. public:
  366. FontStyleProperty (PaintElementText* const e)
  367. : ChoicePropertyComponent ("style"),
  368. element (e)
  369. {
  370. element->getDocument()->addChangeListener (this);
  371. updateStylesList (element->getTypefaceName());
  372. }
  373. ~FontStyleProperty()
  374. {
  375. element->getDocument()->removeChangeListener (this);
  376. }
  377. void updateStylesList (const String& name)
  378. {
  379. if (getNumChildComponents() > 0)
  380. {
  381. if (auto cb = dynamic_cast<ComboBox*> (getChildComponent (0)))
  382. cb->clear();
  383. getChildComponent (0)->setVisible (false);
  384. removeAllChildren();
  385. }
  386. choices.clear();
  387. choices.add ("Regular");
  388. choices.add ("Bold");
  389. choices.add ("Italic");
  390. choices.add ("Bold Italic");
  391. choices.mergeArray (Font::findAllTypefaceStyles (name));
  392. refresh();
  393. }
  394. void setIndex (int newIndex)
  395. {
  396. Font f (element->getFont());
  397. if (f.getAvailableStyles().contains (choices[newIndex]))
  398. {
  399. f.setBold (false);
  400. f.setItalic (false);
  401. f.setTypefaceStyle (choices[newIndex]);
  402. }
  403. else
  404. {
  405. f.setTypefaceStyle ("Regular");
  406. f.setBold (newIndex == 1 || newIndex == 3);
  407. f.setItalic (newIndex == 2 || newIndex == 3);
  408. }
  409. element->setFont (f, true);
  410. }
  411. int getIndex() const
  412. {
  413. auto f = element->getFont();
  414. const auto typefaceIndex = choices.indexOf (f.getTypefaceStyle());
  415. if (typefaceIndex == -1)
  416. {
  417. if (f.isBold() && f.isItalic())
  418. return 3;
  419. else if (f.isBold())
  420. return 1;
  421. else if (f.isItalic())
  422. return 2;
  423. return 0;
  424. }
  425. return typefaceIndex;
  426. }
  427. void changeListenerCallback (ChangeBroadcaster*)
  428. {
  429. updateStylesList (element->getTypefaceName());
  430. }
  431. private:
  432. PaintElementText* const element;
  433. };
  434. //==============================================================================
  435. class FontSizeProperty : public SliderPropertyComponent,
  436. public ChangeListener
  437. {
  438. public:
  439. FontSizeProperty (PaintElementText* const e)
  440. : SliderPropertyComponent ("size", 1.0, 250.0, 0.1, 0.3),
  441. element (e)
  442. {
  443. element->getDocument()->addChangeListener (this);
  444. }
  445. ~FontSizeProperty()
  446. {
  447. element->getDocument()->removeChangeListener (this);
  448. }
  449. void setValue (double newValue)
  450. {
  451. element->getDocument()->getUndoManager().undoCurrentTransactionOnly();
  452. Font f (element->getFont());
  453. f.setHeight ((float) newValue);
  454. element->setFont (f, true);
  455. }
  456. double getValue() const
  457. {
  458. return element->getFont().getHeight();
  459. }
  460. void changeListenerCallback (ChangeBroadcaster*) { refresh(); }
  461. private:
  462. PaintElementText* const element;
  463. };
  464. //==============================================================================
  465. class FontKerningProperty : public SliderPropertyComponent,
  466. public ChangeListener
  467. {
  468. public:
  469. FontKerningProperty (PaintElementText* const e)
  470. : SliderPropertyComponent ("kerning", -0.5, 0.5, 0.001),
  471. element (e)
  472. {
  473. element->getDocument()->addChangeListener (this);
  474. }
  475. ~FontKerningProperty()
  476. {
  477. element->getDocument()->removeChangeListener (this);
  478. }
  479. void setValue (double newValue)
  480. {
  481. element->getDocument()->getUndoManager().undoCurrentTransactionOnly();
  482. Font f (element->getFont());
  483. f.setExtraKerningFactor ((float) newValue);
  484. element->setFont (f, true);
  485. }
  486. double getValue() const
  487. {
  488. return element->getFont().getExtraKerningFactor();
  489. }
  490. void changeListenerCallback (ChangeBroadcaster*)
  491. {
  492. refresh();
  493. }
  494. private:
  495. PaintElementText* const element;
  496. };
  497. //==============================================================================
  498. class TextJustificationProperty : public JustificationProperty,
  499. public ChangeListener
  500. {
  501. public:
  502. TextJustificationProperty (PaintElementText* const e)
  503. : JustificationProperty ("layout", false),
  504. element (e)
  505. {
  506. element->getDocument()->addChangeListener (this);
  507. }
  508. ~TextJustificationProperty()
  509. {
  510. element->getDocument()->removeChangeListener (this);
  511. }
  512. void setJustification (Justification newJustification)
  513. {
  514. element->setJustification (newJustification, true);
  515. }
  516. Justification getJustification() const
  517. {
  518. return element->getJustification();
  519. }
  520. void changeListenerCallback (ChangeBroadcaster*) { refresh(); }
  521. private:
  522. PaintElementText* const element;
  523. };
  524. //==============================================================================
  525. class TextToPathProperty : public ButtonPropertyComponent
  526. {
  527. public:
  528. TextToPathProperty (PaintElementText* const e)
  529. : ButtonPropertyComponent ("path", false),
  530. element (e)
  531. {
  532. }
  533. void buttonClicked()
  534. {
  535. element->convertToPath();
  536. }
  537. String getButtonText() const
  538. {
  539. return "convert text to a path";
  540. }
  541. private:
  542. PaintElementText* const element;
  543. };
  544. };