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.

658 lines
20KB

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