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.

673 lines
21KB

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