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.

764 lines
26KB

  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. class LabelHandler : public ComponentTypeHandler
  18. {
  19. public:
  20. LabelHandler()
  21. : ComponentTypeHandler ("Label", "Label", typeid (Label), 150, 24)
  22. {
  23. registerColour (Label::backgroundColourId, "background", "bkgCol");
  24. registerColour (Label::textColourId, "text", "textCol");
  25. registerColour (Label::outlineColourId, "outline", "outlineCol");
  26. registerColour (TextEditor::textColourId, "editor text", "edTextCol");
  27. registerColour (TextEditor::backgroundColourId, "editor bkg", "edBkgCol");
  28. registerColour (TextEditor::highlightColourId, "highlight", "hiliteCol");
  29. }
  30. Component* createNewComponent (JucerDocument*)
  31. {
  32. return new Label ("new label", "label text");
  33. }
  34. XmlElement* createXmlFor (Component* comp, const ComponentLayout* layout)
  35. {
  36. Label* const l = dynamic_cast<Label*> (comp);
  37. XmlElement* e = ComponentTypeHandler::createXmlFor (comp, layout);
  38. e->setAttribute ("labelText", l->getText());
  39. e->setAttribute ("editableSingleClick", l->isEditableOnSingleClick());
  40. e->setAttribute ("editableDoubleClick", l->isEditableOnDoubleClick());
  41. e->setAttribute ("focusDiscardsChanges", l->doesLossOfFocusDiscardChanges());
  42. e->setAttribute ("fontname", l->getProperties().getWithDefault ("typefaceName", FontPropertyComponent::getDefaultFont()).toString());
  43. e->setAttribute ("fontsize", roundToInt (l->getFont().getHeight() * 100.0) / 100.0);
  44. e->setAttribute ("kerning", roundToInt (l->getFont().getExtraKerningFactor() * 1000.0) / 1000.0);
  45. e->setAttribute ("bold", l->getFont().isBold());
  46. e->setAttribute ("italic", l->getFont().isItalic());
  47. e->setAttribute ("justification", l->getJustificationType().getFlags());
  48. if (l->getFont().getTypefaceStyle() != "Regular")
  49. {
  50. e->setAttribute ("typefaceStyle", l->getFont().getTypefaceStyle());
  51. }
  52. return e;
  53. }
  54. bool restoreFromXml (const XmlElement& xml, Component* comp, const ComponentLayout* layout)
  55. {
  56. Label* const l = dynamic_cast<Label*> (comp);
  57. if (! ComponentTypeHandler::restoreFromXml (xml, comp, layout))
  58. return false;
  59. Label defaultLabel;
  60. Font font;
  61. font.setHeight ((float) xml.getDoubleAttribute ("fontsize", 15.0));
  62. font.setBold (xml.getBoolAttribute ("bold", false));
  63. font.setItalic (xml.getBoolAttribute ("italic", false));
  64. font.setExtraKerningFactor ((float) xml.getDoubleAttribute ("kerning", 0.0));
  65. auto fontStyle = xml.getStringAttribute ("typefaceStyle");
  66. if (! fontStyle.isEmpty())
  67. font.setTypefaceStyle (fontStyle);
  68. l->setFont (font);
  69. l->getProperties().set ("typefaceName", xml.getStringAttribute ("fontname", FontPropertyComponent::getDefaultFont()));
  70. updateLabelFont (l);
  71. l->setJustificationType (Justification (xml.getIntAttribute ("justification", Justification::centred)));
  72. l->setText (xml.getStringAttribute ("labelText", "Label Text"), dontSendNotification);
  73. l->setEditable (xml.getBoolAttribute ("editableSingleClick", defaultLabel.isEditableOnSingleClick()),
  74. xml.getBoolAttribute ("editableDoubleClick", defaultLabel.isEditableOnDoubleClick()),
  75. xml.getBoolAttribute ("focusDiscardsChanges", defaultLabel.doesLossOfFocusDiscardChanges()));
  76. return true;
  77. }
  78. static void updateLabelFont (Label* label)
  79. {
  80. Font f (label->getFont());
  81. f = FontPropertyComponent::applyNameToFont (label->getProperties().getWithDefault ("typefaceName", FontPropertyComponent::getDefaultFont()), f);
  82. label->setFont (f);
  83. }
  84. String getCreationParameters (GeneratedCode& code, Component* component)
  85. {
  86. Label* const l = dynamic_cast<Label*> (component);
  87. return quotedString (component->getName(), false)
  88. + ",\n"
  89. + quotedString (l->getText(), code.shouldUseTransMacro());
  90. }
  91. void fillInCreationCode (GeneratedCode& code, Component* component, const String& memberVariableName)
  92. {
  93. ComponentTypeHandler::fillInCreationCode (code, component, memberVariableName);
  94. Label* const l = dynamic_cast<Label*> (component);
  95. String s;
  96. s << memberVariableName << "->setFont ("
  97. << FontPropertyComponent::getCompleteFontCode (l->getFont(), l->getProperties().getWithDefault ("typefaceName", FontPropertyComponent::getDefaultFont()))
  98. << ");\n"
  99. << memberVariableName << "->setJustificationType ("
  100. << CodeHelpers::justificationToCode (l->getJustificationType())
  101. << ");\n"
  102. << memberVariableName << "->setEditable ("
  103. << CodeHelpers::boolLiteral (l->isEditableOnSingleClick()) << ", "
  104. << CodeHelpers::boolLiteral (l->isEditableOnDoubleClick()) << ", "
  105. << CodeHelpers::boolLiteral (l->doesLossOfFocusDiscardChanges()) << ");\n"
  106. << getColourIntialisationCode (component, memberVariableName);
  107. if (needsCallback (component))
  108. s << memberVariableName << "->addListener (this);\n";
  109. s << '\n';
  110. code.constructorCode += s;
  111. }
  112. void fillInGeneratedCode (Component* component, GeneratedCode& code)
  113. {
  114. ComponentTypeHandler::fillInGeneratedCode (component, code);
  115. if (needsCallback (component))
  116. {
  117. String& callback = code.getCallbackCode ("public LabelListener",
  118. "void",
  119. "labelTextChanged (Label* labelThatHasChanged)",
  120. true);
  121. if (callback.trim().isNotEmpty())
  122. callback << "else ";
  123. const String memberVariableName (code.document->getComponentLayout()->getComponentMemberVariableName (component));
  124. const String userCodeComment ("UserLabelCode_" + memberVariableName);
  125. callback
  126. << "if (labelThatHasChanged == " << memberVariableName
  127. << ")\n{\n //[" << userCodeComment << "] -- add your label text handling code here..\n //[/" << userCodeComment << "]\n}\n";
  128. }
  129. }
  130. void getEditableProperties (Component* component, JucerDocument& document, Array<PropertyComponent*>& props)
  131. {
  132. ComponentTypeHandler::getEditableProperties (component, document, props);
  133. Label* const l = dynamic_cast<Label*> (component);
  134. props.add (new LabelTextProperty (l, document));
  135. props.add (new LabelJustificationProperty (l, document));
  136. props.add (new FontNameProperty (l, document));
  137. props.add (new FontStyleProperty (l, document));
  138. props.add (new FontSizeProperty (l, document));
  139. props.add (new FontKerningProperty (l, document));
  140. addColourProperties (component, document, props);
  141. props.add (new LabelEditableProperty (l, document));
  142. if (l->isEditableOnDoubleClick() || l->isEditableOnSingleClick())
  143. props.add (new LabelLossOfFocusProperty (l, document));
  144. }
  145. static bool needsCallback (Component* label)
  146. {
  147. return ((Label*) label)->isEditableOnSingleClick()
  148. || ((Label*) label)->isEditableOnDoubleClick(); // xxx should be configurable
  149. }
  150. private:
  151. //==============================================================================
  152. class LabelTextProperty : public ComponentTextProperty <Label>
  153. {
  154. public:
  155. LabelTextProperty (Label* comp, JucerDocument& doc)
  156. : ComponentTextProperty <Label> ("text", 10000, true, comp, doc)
  157. {}
  158. void setText (const String& newText) override
  159. {
  160. document.perform (new LabelTextChangeAction (component, *document.getComponentLayout(), newText),
  161. "Change Label text");
  162. }
  163. String getText() const override
  164. {
  165. return component->getText();
  166. }
  167. private:
  168. class LabelTextChangeAction : public ComponentUndoableAction <Label>
  169. {
  170. public:
  171. LabelTextChangeAction (Label* const comp, ComponentLayout& l, const String& newState_)
  172. : ComponentUndoableAction <Label> (comp, l),
  173. newState (newState_)
  174. {
  175. oldState = comp->getText();
  176. }
  177. bool perform()
  178. {
  179. showCorrectTab();
  180. getComponent()->setText (newState, dontSendNotification);
  181. changed();
  182. return true;
  183. }
  184. bool undo()
  185. {
  186. showCorrectTab();
  187. getComponent()->setText (oldState, dontSendNotification);
  188. changed();
  189. return true;
  190. }
  191. String newState, oldState;
  192. };
  193. };
  194. //==============================================================================
  195. class LabelEditableProperty : public ComponentChoiceProperty <Label>
  196. {
  197. public:
  198. LabelEditableProperty (Label* comp, JucerDocument& doc)
  199. : ComponentChoiceProperty <Label> ("editing", comp, doc)
  200. {
  201. choices.add ("read-only");
  202. choices.add ("edit on single-click");
  203. choices.add ("edit on double-click");
  204. }
  205. void setIndex (int newIndex)
  206. {
  207. document.perform (new LabelEditableChangeAction (component, *document.getComponentLayout(), newIndex),
  208. "Change Label editability");
  209. }
  210. int getIndex() const
  211. {
  212. return component->isEditableOnSingleClick()
  213. ? 1
  214. : (component->isEditableOnDoubleClick() ? 2 : 0);
  215. }
  216. private:
  217. class LabelEditableChangeAction : public ComponentUndoableAction <Label>
  218. {
  219. public:
  220. LabelEditableChangeAction (Label* const comp, ComponentLayout& l, const int newState_)
  221. : ComponentUndoableAction <Label> (comp, l),
  222. newState (newState_)
  223. {
  224. oldState = comp->isEditableOnSingleClick()
  225. ? 1
  226. : (comp->isEditableOnDoubleClick() ? 2 : 0);
  227. }
  228. bool perform()
  229. {
  230. showCorrectTab();
  231. getComponent()->setEditable (newState == 1, newState >= 1, getComponent()->doesLossOfFocusDiscardChanges());
  232. changed();
  233. layout.getSelectedSet().changed();
  234. return true;
  235. }
  236. bool undo()
  237. {
  238. showCorrectTab();
  239. getComponent()->setEditable (oldState == 1, oldState >= 1, getComponent()->doesLossOfFocusDiscardChanges());
  240. changed();
  241. layout.getSelectedSet().changed();
  242. return true;
  243. }
  244. int newState, oldState;
  245. };
  246. };
  247. //==============================================================================
  248. class LabelLossOfFocusProperty : public ComponentChoiceProperty <Label>
  249. {
  250. public:
  251. LabelLossOfFocusProperty (Label* comp, JucerDocument& doc)
  252. : ComponentChoiceProperty <Label> ("focus", comp, doc)
  253. {
  254. choices.add ("loss of focus discards changes");
  255. choices.add ("loss of focus commits changes");
  256. }
  257. void setIndex (int newIndex)
  258. {
  259. document.perform (new LabelFocusLossChangeAction (component, *document.getComponentLayout(), newIndex == 0),
  260. "Change Label focus behaviour");
  261. }
  262. int getIndex() const
  263. {
  264. return component->doesLossOfFocusDiscardChanges() ? 0 : 1;
  265. }
  266. private:
  267. class LabelFocusLossChangeAction : public ComponentUndoableAction <Label>
  268. {
  269. public:
  270. LabelFocusLossChangeAction (Label* const comp, ComponentLayout& l, const bool newState_)
  271. : ComponentUndoableAction <Label> (comp, l),
  272. newState (newState_)
  273. {
  274. oldState = comp->doesLossOfFocusDiscardChanges();
  275. }
  276. bool perform()
  277. {
  278. showCorrectTab();
  279. getComponent()->setEditable (getComponent()->isEditableOnSingleClick(),
  280. getComponent()->isEditableOnDoubleClick(),
  281. newState);
  282. changed();
  283. return true;
  284. }
  285. bool undo()
  286. {
  287. showCorrectTab();
  288. getComponent()->setEditable (getComponent()->isEditableOnSingleClick(),
  289. getComponent()->isEditableOnDoubleClick(),
  290. oldState);
  291. changed();
  292. return true;
  293. }
  294. bool newState, oldState;
  295. };
  296. };
  297. //==============================================================================
  298. class LabelJustificationProperty : public JustificationProperty,
  299. public ChangeListener
  300. {
  301. public:
  302. LabelJustificationProperty (Label* const label_, JucerDocument& doc)
  303. : JustificationProperty ("layout", false),
  304. label (label_),
  305. document (doc)
  306. {
  307. document.addChangeListener (this);
  308. }
  309. ~LabelJustificationProperty()
  310. {
  311. document.removeChangeListener (this);
  312. }
  313. void setJustification (Justification newJustification)
  314. {
  315. document.perform (new LabelJustifyChangeAction (label, *document.getComponentLayout(), newJustification),
  316. "Change Label justification");
  317. }
  318. Justification getJustification() const
  319. {
  320. return label->getJustificationType();
  321. }
  322. void changeListenerCallback (ChangeBroadcaster*) { refresh(); }
  323. private:
  324. Label* const label;
  325. JucerDocument& document;
  326. class LabelJustifyChangeAction : public ComponentUndoableAction <Label>
  327. {
  328. public:
  329. LabelJustifyChangeAction (Label* const comp, ComponentLayout& l, Justification newState_)
  330. : ComponentUndoableAction <Label> (comp, l),
  331. newState (newState_),
  332. oldState (comp->getJustificationType())
  333. {
  334. }
  335. bool perform()
  336. {
  337. showCorrectTab();
  338. getComponent()->setJustificationType (newState);
  339. changed();
  340. return true;
  341. }
  342. bool undo()
  343. {
  344. showCorrectTab();
  345. getComponent()->setJustificationType (oldState);
  346. changed();
  347. return true;
  348. }
  349. Justification newState, oldState;
  350. };
  351. };
  352. //==============================================================================
  353. class FontNameProperty : public FontPropertyComponent,
  354. public ChangeListener
  355. {
  356. public:
  357. FontNameProperty (Label* const label_, JucerDocument& doc)
  358. : FontPropertyComponent ("font"),
  359. label (label_),
  360. document (doc)
  361. {
  362. document.addChangeListener (this);
  363. }
  364. ~FontNameProperty()
  365. {
  366. document.removeChangeListener (this);
  367. }
  368. void setTypefaceName (const String& newFontName)
  369. {
  370. document.perform (new FontNameChangeAction (label, *document.getComponentLayout(), newFontName),
  371. "Change Label typeface");
  372. }
  373. String getTypefaceName() const
  374. {
  375. return label->getProperties().getWithDefault ("typefaceName", FontPropertyComponent::getDefaultFont());
  376. }
  377. void changeListenerCallback (ChangeBroadcaster*) { refresh(); }
  378. private:
  379. Label* const label;
  380. JucerDocument& document;
  381. class FontNameChangeAction : public ComponentUndoableAction <Label>
  382. {
  383. public:
  384. FontNameChangeAction (Label* const comp, ComponentLayout& l, const String& newState_)
  385. : ComponentUndoableAction <Label> (comp, l),
  386. newState (newState_)
  387. {
  388. oldState = comp->getProperties().getWithDefault ("typefaceName", FontPropertyComponent::getDefaultFont());
  389. }
  390. bool perform()
  391. {
  392. showCorrectTab();
  393. getComponent()->getProperties().set ("typefaceName", newState);
  394. LabelHandler::updateLabelFont (getComponent());
  395. changed();
  396. return true;
  397. }
  398. bool undo()
  399. {
  400. showCorrectTab();
  401. getComponent()->getProperties().set ("typefaceName", oldState);
  402. LabelHandler::updateLabelFont (getComponent());
  403. changed();
  404. return true;
  405. }
  406. String newState, oldState;
  407. };
  408. };
  409. //==============================================================================
  410. class FontSizeProperty : public SliderPropertyComponent,
  411. public ChangeListener
  412. {
  413. public:
  414. FontSizeProperty (Label* const label_, JucerDocument& doc)
  415. : SliderPropertyComponent ("size", 1.0, 250.0, 0.1, 0.3),
  416. label (label_),
  417. document (doc)
  418. {
  419. document.addChangeListener (this);
  420. }
  421. ~FontSizeProperty()
  422. {
  423. document.removeChangeListener (this);
  424. }
  425. void setValue (double newValue)
  426. {
  427. document.getUndoManager().undoCurrentTransactionOnly();
  428. document.perform (new FontSizeChangeAction (label, *document.getComponentLayout(), (float) newValue),
  429. "Change Label font size");
  430. }
  431. double getValue() const
  432. {
  433. return label->getFont().getHeight();
  434. }
  435. void changeListenerCallback (ChangeBroadcaster*) { refresh(); }
  436. private:
  437. Label* const label;
  438. JucerDocument& document;
  439. class FontSizeChangeAction : public ComponentUndoableAction <Label>
  440. {
  441. public:
  442. FontSizeChangeAction (Label* const comp, ComponentLayout& l, const float newState_)
  443. : ComponentUndoableAction <Label> (comp, l),
  444. newState (newState_)
  445. {
  446. oldState = comp->getFont().getHeight();
  447. }
  448. bool perform()
  449. {
  450. showCorrectTab();
  451. Font f (getComponent()->getFont());
  452. f.setHeight ((float) newState);
  453. getComponent()->setFont (f);
  454. changed();
  455. return true;
  456. }
  457. bool undo()
  458. {
  459. showCorrectTab();
  460. Font f (getComponent()->getFont());
  461. f.setHeight ((float) oldState);
  462. getComponent()->setFont (f);
  463. changed();
  464. return true;
  465. }
  466. float newState, oldState;
  467. };
  468. };
  469. //==============================================================================
  470. class FontStyleProperty : public ChoicePropertyComponent,
  471. public ChangeListener
  472. {
  473. public:
  474. FontStyleProperty (Label* const label_, JucerDocument& doc)
  475. : ChoicePropertyComponent ("style"),
  476. label (label_),
  477. document (doc)
  478. {
  479. document.addChangeListener (this);
  480. updateStylesList (label->getFont());
  481. }
  482. ~FontStyleProperty()
  483. {
  484. document.removeChangeListener (this);
  485. }
  486. void updateStylesList (const Font& newFont)
  487. {
  488. if (getNumChildComponents() > 0)
  489. {
  490. if (auto cb = dynamic_cast<ComboBox*> (getChildComponent (0)))
  491. cb->clear();
  492. getChildComponent (0)->setVisible (false);
  493. removeAllChildren();
  494. }
  495. choices.clear();
  496. choices.add ("Regular");
  497. choices.add ("Bold");
  498. choices.add ("Italic");
  499. choices.add ("Bold Italic");
  500. choices.mergeArray (newFont.getAvailableStyles());
  501. refresh();
  502. }
  503. void setIndex (int newIndex)
  504. {
  505. Font f (label->getFont());
  506. if (f.getAvailableStyles().contains (choices[newIndex]))
  507. {
  508. f.setBold (false);
  509. f.setItalic (false);
  510. f.setTypefaceStyle (choices[newIndex]);
  511. }
  512. else
  513. {
  514. f.setTypefaceStyle ("Regular");
  515. f.setBold (newIndex == 1 || newIndex == 3);
  516. f.setItalic (newIndex == 2 || newIndex == 3);
  517. }
  518. document.perform (new FontStyleChangeAction (label, *document.getComponentLayout(), f),
  519. "Change Label font style");
  520. }
  521. int getIndex() const
  522. {
  523. auto f = label->getFont();
  524. const auto typefaceIndex = choices.indexOf (f.getTypefaceStyle());
  525. if (typefaceIndex == -1)
  526. {
  527. if (f.isBold() && f.isItalic())
  528. return 3;
  529. else if (f.isBold())
  530. return 1;
  531. else if (f.isItalic())
  532. return 2;
  533. return 0;
  534. }
  535. return typefaceIndex;
  536. }
  537. void changeListenerCallback (ChangeBroadcaster*)
  538. {
  539. updateStylesList (label->getFont());
  540. }
  541. private:
  542. Label* const label;
  543. JucerDocument& document;
  544. class FontStyleChangeAction : public ComponentUndoableAction <Label>
  545. {
  546. public:
  547. FontStyleChangeAction (Label* const comp, ComponentLayout& l, const Font& newState_)
  548. : ComponentUndoableAction <Label> (comp, l),
  549. newState (newState_)
  550. {
  551. oldState = comp->getFont();
  552. }
  553. bool perform()
  554. {
  555. showCorrectTab();
  556. getComponent()->setFont (newState);
  557. changed();
  558. return true;
  559. }
  560. bool undo()
  561. {
  562. showCorrectTab();
  563. getComponent()->setFont (oldState);
  564. changed();
  565. return true;
  566. }
  567. Font newState, oldState;
  568. };
  569. };
  570. //==============================================================================
  571. class FontKerningProperty : public SliderPropertyComponent,
  572. public ChangeListener
  573. {
  574. public:
  575. FontKerningProperty (Label* const label_, JucerDocument& doc)
  576. : SliderPropertyComponent ("kerning", -0.5, 0.5, 0.001),
  577. label (label_),
  578. document (doc)
  579. {
  580. document.addChangeListener (this);
  581. }
  582. ~FontKerningProperty()
  583. {
  584. document.removeChangeListener (this);
  585. }
  586. void setValue (double newValue)
  587. {
  588. document.getUndoManager().undoCurrentTransactionOnly();
  589. document.perform (new FontKerningChangeAction (label, *document.getComponentLayout(), (float) newValue),
  590. "Change Label font kerning");
  591. }
  592. double getValue() const
  593. {
  594. return label->getFont().getExtraKerningFactor();
  595. }
  596. void changeListenerCallback (ChangeBroadcaster*)
  597. {
  598. refresh();
  599. }
  600. private:
  601. Label* const label;
  602. JucerDocument& document;
  603. class FontKerningChangeAction : public ComponentUndoableAction <Label>
  604. {
  605. public:
  606. FontKerningChangeAction (Label* const comp, ComponentLayout& l, const float newState_)
  607. : ComponentUndoableAction <Label> (comp, l),
  608. newState (newState_)
  609. {
  610. oldState = comp->getFont().getExtraKerningFactor();
  611. }
  612. bool perform()
  613. {
  614. showCorrectTab();
  615. Font f (getComponent()->getFont());
  616. f.setExtraKerningFactor ((float) newState);
  617. getComponent()->setFont (f);
  618. changed();
  619. return true;
  620. }
  621. bool undo()
  622. {
  623. showCorrectTab();
  624. Font f (getComponent()->getFont());
  625. f.setExtraKerningFactor ((float) oldState);
  626. getComponent()->setFont (f);
  627. changed();
  628. return true;
  629. }
  630. float newState, oldState;
  631. };
  632. };
  633. };