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.

772 lines
27KB

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