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.

666 lines
21KB

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