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.

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