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.

671 lines
21KB

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