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.

768 lines
26KB

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