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.

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