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.

766 lines
26KB

  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*)
  33. {
  34. return new Label ("new label", "label text");
  35. }
  36. XmlElement* createXmlFor (Component* comp, const ComponentLayout* layout)
  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)
  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)
  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)
  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)
  115. {
  116. ComponentTypeHandler::fillInGeneratedCode (component, code);
  117. if (needsCallback (component))
  118. {
  119. String& callback = code.getCallbackCode ("public LabelListener",
  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, Array<PropertyComponent*>& props)
  133. {
  134. ComponentTypeHandler::getEditableProperties (component, document, props);
  135. Label* const l = dynamic_cast<Label*> (component);
  136. props.add (new LabelTextProperty (l, document));
  137. props.add (new LabelJustificationProperty (l, document));
  138. props.add (new FontNameProperty (l, document));
  139. props.add (new FontStyleProperty (l, document));
  140. props.add (new FontSizeProperty (l, document));
  141. props.add (new FontKerningProperty (l, document));
  142. addColourProperties (component, document, props);
  143. props.add (new LabelEditableProperty (l, document));
  144. if (l->isEditableOnDoubleClick() || l->isEditableOnSingleClick())
  145. props.add (new LabelLossOfFocusProperty (l, document));
  146. }
  147. static bool needsCallback (Component* label)
  148. {
  149. return ((Label*) label)->isEditableOnSingleClick()
  150. || ((Label*) label)->isEditableOnDoubleClick(); // xxx should be configurable
  151. }
  152. private:
  153. //==============================================================================
  154. class LabelTextProperty : public ComponentTextProperty <Label>
  155. {
  156. public:
  157. LabelTextProperty (Label* comp, JucerDocument& doc)
  158. : ComponentTextProperty <Label> ("text", 10000, true, comp, doc)
  159. {}
  160. void setText (const String& newText) override
  161. {
  162. document.perform (new LabelTextChangeAction (component, *document.getComponentLayout(), newText),
  163. "Change Label text");
  164. }
  165. String getText() const override
  166. {
  167. return component->getText();
  168. }
  169. private:
  170. class LabelTextChangeAction : public ComponentUndoableAction <Label>
  171. {
  172. public:
  173. LabelTextChangeAction (Label* const comp, ComponentLayout& l, const String& newState_)
  174. : ComponentUndoableAction <Label> (comp, l),
  175. newState (newState_)
  176. {
  177. oldState = comp->getText();
  178. }
  179. bool perform()
  180. {
  181. showCorrectTab();
  182. getComponent()->setText (newState, dontSendNotification);
  183. changed();
  184. return true;
  185. }
  186. bool undo()
  187. {
  188. showCorrectTab();
  189. getComponent()->setText (oldState, dontSendNotification);
  190. changed();
  191. return true;
  192. }
  193. String newState, oldState;
  194. };
  195. };
  196. //==============================================================================
  197. class LabelEditableProperty : public ComponentChoiceProperty <Label>
  198. {
  199. public:
  200. LabelEditableProperty (Label* comp, JucerDocument& doc)
  201. : ComponentChoiceProperty <Label> ("editing", comp, doc)
  202. {
  203. choices.add ("read-only");
  204. choices.add ("edit on single-click");
  205. choices.add ("edit on double-click");
  206. }
  207. void setIndex (int newIndex)
  208. {
  209. document.perform (new LabelEditableChangeAction (component, *document.getComponentLayout(), newIndex),
  210. "Change Label editability");
  211. }
  212. int getIndex() const
  213. {
  214. return component->isEditableOnSingleClick()
  215. ? 1
  216. : (component->isEditableOnDoubleClick() ? 2 : 0);
  217. }
  218. private:
  219. class LabelEditableChangeAction : public ComponentUndoableAction <Label>
  220. {
  221. public:
  222. LabelEditableChangeAction (Label* const comp, ComponentLayout& l, const int newState_)
  223. : ComponentUndoableAction <Label> (comp, l),
  224. newState (newState_)
  225. {
  226. oldState = comp->isEditableOnSingleClick()
  227. ? 1
  228. : (comp->isEditableOnDoubleClick() ? 2 : 0);
  229. }
  230. bool perform()
  231. {
  232. showCorrectTab();
  233. getComponent()->setEditable (newState == 1, newState >= 1, getComponent()->doesLossOfFocusDiscardChanges());
  234. changed();
  235. layout.getSelectedSet().changed();
  236. return true;
  237. }
  238. bool undo()
  239. {
  240. showCorrectTab();
  241. getComponent()->setEditable (oldState == 1, oldState >= 1, getComponent()->doesLossOfFocusDiscardChanges());
  242. changed();
  243. layout.getSelectedSet().changed();
  244. return true;
  245. }
  246. int newState, oldState;
  247. };
  248. };
  249. //==============================================================================
  250. class LabelLossOfFocusProperty : public ComponentChoiceProperty <Label>
  251. {
  252. public:
  253. LabelLossOfFocusProperty (Label* comp, JucerDocument& doc)
  254. : ComponentChoiceProperty <Label> ("focus", comp, doc)
  255. {
  256. choices.add ("loss of focus discards changes");
  257. choices.add ("loss of focus commits changes");
  258. }
  259. void setIndex (int newIndex)
  260. {
  261. document.perform (new LabelFocusLossChangeAction (component, *document.getComponentLayout(), newIndex == 0),
  262. "Change Label focus behaviour");
  263. }
  264. int getIndex() const
  265. {
  266. return component->doesLossOfFocusDiscardChanges() ? 0 : 1;
  267. }
  268. private:
  269. class LabelFocusLossChangeAction : public ComponentUndoableAction <Label>
  270. {
  271. public:
  272. LabelFocusLossChangeAction (Label* const comp, ComponentLayout& l, const bool newState_)
  273. : ComponentUndoableAction <Label> (comp, l),
  274. newState (newState_)
  275. {
  276. oldState = comp->doesLossOfFocusDiscardChanges();
  277. }
  278. bool perform()
  279. {
  280. showCorrectTab();
  281. getComponent()->setEditable (getComponent()->isEditableOnSingleClick(),
  282. getComponent()->isEditableOnDoubleClick(),
  283. newState);
  284. changed();
  285. return true;
  286. }
  287. bool undo()
  288. {
  289. showCorrectTab();
  290. getComponent()->setEditable (getComponent()->isEditableOnSingleClick(),
  291. getComponent()->isEditableOnDoubleClick(),
  292. oldState);
  293. changed();
  294. return true;
  295. }
  296. bool newState, oldState;
  297. };
  298. };
  299. //==============================================================================
  300. class LabelJustificationProperty : public JustificationProperty,
  301. public ChangeListener
  302. {
  303. public:
  304. LabelJustificationProperty (Label* const label_, JucerDocument& doc)
  305. : JustificationProperty ("layout", false),
  306. label (label_),
  307. document (doc)
  308. {
  309. document.addChangeListener (this);
  310. }
  311. ~LabelJustificationProperty()
  312. {
  313. document.removeChangeListener (this);
  314. }
  315. void setJustification (Justification newJustification)
  316. {
  317. document.perform (new LabelJustifyChangeAction (label, *document.getComponentLayout(), newJustification),
  318. "Change Label justification");
  319. }
  320. Justification getJustification() const
  321. {
  322. return label->getJustificationType();
  323. }
  324. void changeListenerCallback (ChangeBroadcaster*) { refresh(); }
  325. private:
  326. Label* const label;
  327. JucerDocument& document;
  328. class LabelJustifyChangeAction : public ComponentUndoableAction <Label>
  329. {
  330. public:
  331. LabelJustifyChangeAction (Label* const comp, ComponentLayout& l, Justification newState_)
  332. : ComponentUndoableAction <Label> (comp, l),
  333. newState (newState_),
  334. oldState (comp->getJustificationType())
  335. {
  336. }
  337. bool perform()
  338. {
  339. showCorrectTab();
  340. getComponent()->setJustificationType (newState);
  341. changed();
  342. return true;
  343. }
  344. bool undo()
  345. {
  346. showCorrectTab();
  347. getComponent()->setJustificationType (oldState);
  348. changed();
  349. return true;
  350. }
  351. Justification newState, oldState;
  352. };
  353. };
  354. //==============================================================================
  355. class FontNameProperty : public FontPropertyComponent,
  356. public ChangeListener
  357. {
  358. public:
  359. FontNameProperty (Label* const label_, JucerDocument& doc)
  360. : FontPropertyComponent ("font"),
  361. label (label_),
  362. document (doc)
  363. {
  364. document.addChangeListener (this);
  365. }
  366. ~FontNameProperty()
  367. {
  368. document.removeChangeListener (this);
  369. }
  370. void setTypefaceName (const String& newFontName)
  371. {
  372. document.perform (new FontNameChangeAction (label, *document.getComponentLayout(), newFontName),
  373. "Change Label typeface");
  374. }
  375. String getTypefaceName() const
  376. {
  377. return label->getProperties().getWithDefault ("typefaceName", FontPropertyComponent::getDefaultFont());
  378. }
  379. void changeListenerCallback (ChangeBroadcaster*) { refresh(); }
  380. private:
  381. Label* const label;
  382. JucerDocument& document;
  383. class FontNameChangeAction : public ComponentUndoableAction <Label>
  384. {
  385. public:
  386. FontNameChangeAction (Label* const comp, ComponentLayout& l, const String& newState_)
  387. : ComponentUndoableAction <Label> (comp, l),
  388. newState (newState_)
  389. {
  390. oldState = comp->getProperties().getWithDefault ("typefaceName", FontPropertyComponent::getDefaultFont());
  391. }
  392. bool perform()
  393. {
  394. showCorrectTab();
  395. getComponent()->getProperties().set ("typefaceName", newState);
  396. LabelHandler::updateLabelFont (getComponent());
  397. changed();
  398. return true;
  399. }
  400. bool undo()
  401. {
  402. showCorrectTab();
  403. getComponent()->getProperties().set ("typefaceName", oldState);
  404. LabelHandler::updateLabelFont (getComponent());
  405. changed();
  406. return true;
  407. }
  408. String newState, oldState;
  409. };
  410. };
  411. //==============================================================================
  412. class FontSizeProperty : public SliderPropertyComponent,
  413. public ChangeListener
  414. {
  415. public:
  416. FontSizeProperty (Label* const label_, JucerDocument& doc)
  417. : SliderPropertyComponent ("size", 1.0, 250.0, 0.1, 0.3),
  418. label (label_),
  419. document (doc)
  420. {
  421. document.addChangeListener (this);
  422. }
  423. ~FontSizeProperty()
  424. {
  425. document.removeChangeListener (this);
  426. }
  427. void setValue (double newValue)
  428. {
  429. document.getUndoManager().undoCurrentTransactionOnly();
  430. document.perform (new FontSizeChangeAction (label, *document.getComponentLayout(), (float) newValue),
  431. "Change Label font size");
  432. }
  433. double getValue() const
  434. {
  435. return label->getFont().getHeight();
  436. }
  437. void changeListenerCallback (ChangeBroadcaster*) { refresh(); }
  438. private:
  439. Label* const label;
  440. JucerDocument& document;
  441. class FontSizeChangeAction : public ComponentUndoableAction <Label>
  442. {
  443. public:
  444. FontSizeChangeAction (Label* const comp, ComponentLayout& l, const float newState_)
  445. : ComponentUndoableAction <Label> (comp, l),
  446. newState (newState_)
  447. {
  448. oldState = comp->getFont().getHeight();
  449. }
  450. bool perform()
  451. {
  452. showCorrectTab();
  453. Font f (getComponent()->getFont());
  454. f.setHeight ((float) newState);
  455. getComponent()->setFont (f);
  456. changed();
  457. return true;
  458. }
  459. bool undo()
  460. {
  461. showCorrectTab();
  462. Font f (getComponent()->getFont());
  463. f.setHeight ((float) oldState);
  464. getComponent()->setFont (f);
  465. changed();
  466. return true;
  467. }
  468. float newState, oldState;
  469. };
  470. };
  471. //==============================================================================
  472. class FontStyleProperty : public ChoicePropertyComponent,
  473. public ChangeListener
  474. {
  475. public:
  476. FontStyleProperty (Label* const label_, JucerDocument& doc)
  477. : ChoicePropertyComponent ("style"),
  478. label (label_),
  479. document (doc)
  480. {
  481. document.addChangeListener (this);
  482. updateStylesList (label->getFont());
  483. }
  484. ~FontStyleProperty()
  485. {
  486. document.removeChangeListener (this);
  487. }
  488. void updateStylesList (const Font& newFont)
  489. {
  490. if (getNumChildComponents() > 0)
  491. {
  492. if (auto cb = dynamic_cast<ComboBox*> (getChildComponent (0)))
  493. cb->clear();
  494. getChildComponent (0)->setVisible (false);
  495. removeAllChildren();
  496. }
  497. choices.clear();
  498. choices.add ("Regular");
  499. choices.add ("Bold");
  500. choices.add ("Italic");
  501. choices.add ("Bold Italic");
  502. choices.mergeArray (newFont.getAvailableStyles());
  503. refresh();
  504. }
  505. void setIndex (int newIndex)
  506. {
  507. Font f (label->getFont());
  508. if (f.getAvailableStyles().contains (choices[newIndex]))
  509. {
  510. f.setBold (false);
  511. f.setItalic (false);
  512. f.setTypefaceStyle (choices[newIndex]);
  513. }
  514. else
  515. {
  516. f.setTypefaceStyle ("Regular");
  517. f.setBold (newIndex == 1 || newIndex == 3);
  518. f.setItalic (newIndex == 2 || newIndex == 3);
  519. }
  520. document.perform (new FontStyleChangeAction (label, *document.getComponentLayout(), f),
  521. "Change Label font style");
  522. }
  523. int getIndex() const
  524. {
  525. auto f = label->getFont();
  526. const auto typefaceIndex = choices.indexOf (f.getTypefaceStyle());
  527. if (typefaceIndex == -1)
  528. {
  529. if (f.isBold() && f.isItalic())
  530. return 3;
  531. else if (f.isBold())
  532. return 1;
  533. else if (f.isItalic())
  534. return 2;
  535. return 0;
  536. }
  537. return typefaceIndex;
  538. }
  539. void changeListenerCallback (ChangeBroadcaster*)
  540. {
  541. updateStylesList (label->getFont());
  542. }
  543. private:
  544. Label* const label;
  545. JucerDocument& document;
  546. class FontStyleChangeAction : public ComponentUndoableAction <Label>
  547. {
  548. public:
  549. FontStyleChangeAction (Label* const comp, ComponentLayout& l, const Font& newState_)
  550. : ComponentUndoableAction <Label> (comp, l),
  551. newState (newState_)
  552. {
  553. oldState = comp->getFont();
  554. }
  555. bool perform()
  556. {
  557. showCorrectTab();
  558. getComponent()->setFont (newState);
  559. changed();
  560. return true;
  561. }
  562. bool undo()
  563. {
  564. showCorrectTab();
  565. getComponent()->setFont (oldState);
  566. changed();
  567. return true;
  568. }
  569. Font newState, oldState;
  570. };
  571. };
  572. //==============================================================================
  573. class FontKerningProperty : public SliderPropertyComponent,
  574. public ChangeListener
  575. {
  576. public:
  577. FontKerningProperty (Label* const label_, JucerDocument& doc)
  578. : SliderPropertyComponent ("kerning", -0.5, 0.5, 0.001),
  579. label (label_),
  580. document (doc)
  581. {
  582. document.addChangeListener (this);
  583. }
  584. ~FontKerningProperty()
  585. {
  586. document.removeChangeListener (this);
  587. }
  588. void setValue (double newValue)
  589. {
  590. document.getUndoManager().undoCurrentTransactionOnly();
  591. document.perform (new FontKerningChangeAction (label, *document.getComponentLayout(), (float) newValue),
  592. "Change Label font kerning");
  593. }
  594. double getValue() const
  595. {
  596. return label->getFont().getExtraKerningFactor();
  597. }
  598. void changeListenerCallback (ChangeBroadcaster*)
  599. {
  600. refresh();
  601. }
  602. private:
  603. Label* const label;
  604. JucerDocument& document;
  605. class FontKerningChangeAction : public ComponentUndoableAction <Label>
  606. {
  607. public:
  608. FontKerningChangeAction (Label* const comp, ComponentLayout& l, const float newState_)
  609. : ComponentUndoableAction <Label> (comp, l),
  610. newState (newState_)
  611. {
  612. oldState = comp->getFont().getExtraKerningFactor();
  613. }
  614. bool perform()
  615. {
  616. showCorrectTab();
  617. Font f (getComponent()->getFont());
  618. f.setExtraKerningFactor ((float) newState);
  619. getComponent()->setFont (f);
  620. changed();
  621. return true;
  622. }
  623. bool undo()
  624. {
  625. showCorrectTab();
  626. Font f (getComponent()->getFont());
  627. f.setExtraKerningFactor ((float) oldState);
  628. getComponent()->setFont (f);
  629. changed();
  630. return true;
  631. }
  632. float newState, oldState;
  633. };
  634. };
  635. };