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.

1187 lines
41KB

  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 TabbedComponentHandler : public ComponentTypeHandler
  16. {
  17. public:
  18. TabbedComponentHandler()
  19. : ComponentTypeHandler ("Tabbed Component", "juce::TabbedComponent", typeid (TabbedComponent), 200, 150)
  20. {}
  21. Component* createNewComponent (JucerDocument*) override
  22. {
  23. TabbedComponent* const t = new TabbedComponent (TabbedButtonBar::TabsAtTop);
  24. t->setName ("new tabbed component");
  25. for (int i = 3; --i >= 0;)
  26. addNewTab (t);
  27. return t;
  28. }
  29. XmlElement* createXmlFor (Component* comp, const ComponentLayout* layout) override
  30. {
  31. TabbedComponent* const t = dynamic_cast<TabbedComponent*> (comp);
  32. XmlElement* const e = ComponentTypeHandler::createXmlFor (comp, layout);
  33. if (t->getOrientation() == TabbedButtonBar::TabsAtTop) e->setAttribute ("orientation", "top");
  34. else if (t->getOrientation() == TabbedButtonBar::TabsAtBottom) e->setAttribute ("orientation", "bottom");
  35. else if (t->getOrientation() == TabbedButtonBar::TabsAtLeft) e->setAttribute ("orientation", "left");
  36. else if (t->getOrientation() == TabbedButtonBar::TabsAtRight) e->setAttribute ("orientation", "right");
  37. e->setAttribute ("tabBarDepth", t->getTabBarDepth());
  38. e->setAttribute ("initialTab", t->getCurrentTabIndex());
  39. for (int i = 0; i < t->getNumTabs(); ++i)
  40. e->addChildElement (getTabState (t, i).release());
  41. return e;
  42. }
  43. bool restoreFromXml (const XmlElement& xml, Component* comp, const ComponentLayout* layout) override
  44. {
  45. if (! ComponentTypeHandler::restoreFromXml (xml, comp, layout))
  46. return false;
  47. TabbedComponent* const t = dynamic_cast<TabbedComponent*> (comp);
  48. if (xml.getStringAttribute ("orientation") == "top") t->setOrientation (TabbedButtonBar::TabsAtTop);
  49. else if (xml.getStringAttribute ("orientation") == "bottom") t->setOrientation (TabbedButtonBar::TabsAtBottom);
  50. else if (xml.getStringAttribute ("orientation") == "left") t->setOrientation (TabbedButtonBar::TabsAtLeft);
  51. else if (xml.getStringAttribute ("orientation") == "right") t->setOrientation (TabbedButtonBar::TabsAtRight);
  52. TabbedComponent defaultTabComp (TabbedButtonBar::TabsAtTop);
  53. t->setTabBarDepth (xml.getIntAttribute ("tabBarDepth", defaultTabComp.getTabBarDepth()));
  54. t->clearTabs();
  55. forEachXmlChildElement (xml, e)
  56. {
  57. addNewTab (t);
  58. restoreTabState (t, t->getNumTabs() - 1, *e);
  59. }
  60. t->setCurrentTabIndex (xml.getIntAttribute ("initialTab", 0));
  61. return true;
  62. }
  63. void getEditableProperties (Component* component, JucerDocument& doc,
  64. Array<PropertyComponent*>& props, bool multipleSelected) override
  65. {
  66. ComponentTypeHandler::getEditableProperties (component, doc, props, multipleSelected);
  67. if (multipleSelected)
  68. return;
  69. if (auto* t = dynamic_cast<TabbedComponent*> (component))
  70. {
  71. props.add (new TabOrientationProperty (t, doc));
  72. props.add (new TabDepthProperty (t, doc));
  73. if (t->getNumTabs() > 0)
  74. props.add (new TabInitialTabProperty (t, doc));
  75. props.add (new TabAddTabProperty (t, doc));
  76. if (t->getNumTabs() > 0)
  77. props.add (new TabRemoveTabProperty (t, doc));
  78. }
  79. }
  80. void addPropertiesToPropertyPanel (Component* comp, JucerDocument& doc,
  81. PropertyPanel& panel, bool multipleSelected) override
  82. {
  83. ComponentTypeHandler::addPropertiesToPropertyPanel (comp, doc, panel, multipleSelected);
  84. TabbedComponent* const t = dynamic_cast<TabbedComponent*> (comp);
  85. for (int i = 0; i < t->getNumTabs(); ++i)
  86. {
  87. Array<PropertyComponent*> properties;
  88. properties.add (new TabNameProperty (t, doc, i));
  89. properties.add (new TabColourProperty (t, doc, i));
  90. properties.add (new TabContentTypeProperty (t, doc, i));
  91. if (isTabUsingJucerComp (t, i))
  92. properties.add (new TabJucerFileProperty (t, doc, i));
  93. else
  94. properties.add (new TabContentClassProperty (t, doc, i));
  95. properties.add (new TabContentConstructorParamsProperty (t, doc, i));
  96. properties.add (new TabMoveProperty (t, doc, i, t->getNumTabs()));
  97. panel.addSection ("Tab " + String (i), properties);
  98. }
  99. }
  100. String getCreationParameters (GeneratedCode&, Component* comp) override
  101. {
  102. TabbedComponent* const t = dynamic_cast<TabbedComponent*> (comp);
  103. switch (t->getOrientation())
  104. {
  105. case TabbedButtonBar::TabsAtTop: return "juce::TabbedButtonBar::TabsAtTop";
  106. case TabbedButtonBar::TabsAtBottom: return "juce::TabbedButtonBar::TabsAtBottom";
  107. case TabbedButtonBar::TabsAtLeft: return "juce::TabbedButtonBar::TabsAtLeft";
  108. case TabbedButtonBar::TabsAtRight: return "juce::TabbedButtonBar::TabsAtRight";
  109. default: jassertfalse; break;
  110. }
  111. return {};
  112. }
  113. void fillInCreationCode (GeneratedCode& code, Component* component, const String& memberVariableName) override
  114. {
  115. TabbedComponent* const t = dynamic_cast<TabbedComponent*> (component);
  116. ComponentTypeHandler::fillInCreationCode (code, component, memberVariableName);
  117. code.constructorCode
  118. << memberVariableName << "->setTabBarDepth (" << t->getTabBarDepth() << ");\n";
  119. for (int i = 0; i < t->getNumTabs(); ++i)
  120. {
  121. String contentClassName;
  122. if (isTabUsingJucerComp (t, i))
  123. {
  124. File jucerCpp = code.document->getCppFile().getSiblingFile (getTabJucerFile (t, i));
  125. std::unique_ptr<JucerDocument> doc (JucerDocument::createForCppFile (nullptr, jucerCpp));
  126. if (doc != nullptr)
  127. {
  128. code.includeFilesCPP.add (jucerCpp.withFileExtension (".h"));
  129. contentClassName = doc->getClassName();
  130. }
  131. }
  132. else
  133. {
  134. contentClassName = getTabClassName (t, i);
  135. }
  136. code.constructorCode
  137. << memberVariableName
  138. << "->addTab ("
  139. << quotedString (t->getTabNames() [i], code.shouldUseTransMacro())
  140. << ", "
  141. << CodeHelpers::colourToCode (t->getTabBackgroundColour (i));
  142. if (contentClassName.isNotEmpty())
  143. {
  144. code.constructorCode << ", new " << contentClassName;
  145. if (getTabConstructorParams (t, i).trim().isNotEmpty())
  146. code.constructorCode << " ";
  147. code.constructorCode << "(" << getTabConstructorParams (t, i).trim() << "), true);\n";
  148. }
  149. else
  150. {
  151. code.constructorCode << ", 0, false);\n";
  152. }
  153. }
  154. code.constructorCode
  155. << memberVariableName << "->setCurrentTabIndex (" << t->getCurrentTabIndex() << ");\n";
  156. code.constructorCode << "\n";
  157. }
  158. //==============================================================================
  159. static void addNewTab (TabbedComponent* tc, const int insertIndex = -1)
  160. {
  161. tc->addTab ("Tab " + String (tc->getNumTabs()), Colours::lightgrey,
  162. new TabDemoContentComp(), true, insertIndex);
  163. }
  164. //==============================================================================
  165. static std::unique_ptr<XmlElement> getTabState (TabbedComponent* tc, int tabIndex)
  166. {
  167. auto xml = std::make_unique<XmlElement> ("TAB");
  168. xml->setAttribute ("name", tc->getTabNames() [tabIndex]);
  169. xml->setAttribute ("colour", tc->getTabBackgroundColour (tabIndex).toString());
  170. if (TabDemoContentComp* const tdc = dynamic_cast<TabDemoContentComp*> (tc->getTabContentComponent (tabIndex)))
  171. {
  172. xml->setAttribute ("useJucerComp", tdc->isUsingJucerComp);
  173. xml->setAttribute ("contentClassName", tdc->contentClassName);
  174. xml->setAttribute ("constructorParams", tdc->constructorParams);
  175. xml->setAttribute ("jucerComponentFile", tdc->jucerComponentFile);
  176. }
  177. return xml;
  178. }
  179. static void restoreTabState (TabbedComponent* tc, int tabIndex, const XmlElement& xml)
  180. {
  181. tc->setTabName (tabIndex, xml.getStringAttribute ("name", "Tab"));
  182. tc->setTabBackgroundColour (tabIndex, Colour::fromString (xml.getStringAttribute ("colour", Colours::lightgrey.toString())));
  183. if (TabDemoContentComp* const tdc = dynamic_cast<TabDemoContentComp*> (tc->getTabContentComponent (tabIndex)))
  184. {
  185. tdc->isUsingJucerComp = xml.getBoolAttribute ("useJucerComp", false);
  186. tdc->contentClassName = xml.getStringAttribute ("contentClassName");
  187. tdc->constructorParams = xml.getStringAttribute ("constructorParams");
  188. tdc->jucerComponentFile = xml.getStringAttribute ("jucerComponentFile");
  189. tdc->updateContent();
  190. }
  191. }
  192. //==============================================================================
  193. static bool isTabUsingJucerComp (TabbedComponent* tc, int tabIndex)
  194. {
  195. auto tdc = dynamic_cast<TabDemoContentComp*> (tc->getTabContentComponent (tabIndex));
  196. jassert (tdc != nullptr);
  197. return tdc != nullptr && tdc->isUsingJucerComp;
  198. }
  199. static void setTabUsingJucerComp (TabbedComponent* tc, int tabIndex, const bool b)
  200. {
  201. auto tdc = dynamic_cast<TabDemoContentComp*> (tc->getTabContentComponent (tabIndex));
  202. jassert (tdc != nullptr);
  203. if (tdc != nullptr)
  204. {
  205. tdc->isUsingJucerComp = b;
  206. tdc->updateContent();
  207. }
  208. }
  209. static String getTabClassName (TabbedComponent* tc, int tabIndex)
  210. {
  211. auto tdc = dynamic_cast<TabDemoContentComp*> (tc->getTabContentComponent (tabIndex));
  212. jassert (tdc != nullptr);
  213. return tdc != nullptr ? tdc->contentClassName : String();
  214. }
  215. static void setTabClassName (TabbedComponent* tc, int tabIndex, const String& newName)
  216. {
  217. TabDemoContentComp* const tdc = dynamic_cast<TabDemoContentComp*> (tc->getTabContentComponent (tabIndex));
  218. jassert (tdc != nullptr);
  219. if (tdc != nullptr)
  220. {
  221. tdc->contentClassName = newName;
  222. tdc->updateContent();
  223. }
  224. }
  225. static String getTabConstructorParams (TabbedComponent* tc, int tabIndex)
  226. {
  227. auto tdc = dynamic_cast<TabDemoContentComp*> (tc->getTabContentComponent (tabIndex));
  228. jassert (tdc != nullptr);
  229. return tdc != nullptr ? tdc->constructorParams : String();
  230. }
  231. static void setTabConstructorParams (TabbedComponent* tc, int tabIndex, const String& newParams)
  232. {
  233. auto tdc = dynamic_cast<TabDemoContentComp*> (tc->getTabContentComponent (tabIndex));
  234. jassert (tdc != nullptr);
  235. if (tdc != nullptr)
  236. {
  237. tdc->constructorParams = newParams;
  238. tdc->updateContent();
  239. }
  240. }
  241. static String getTabJucerFile (TabbedComponent* tc, int tabIndex)
  242. {
  243. auto tdc = dynamic_cast<TabDemoContentComp*> (tc->getTabContentComponent (tabIndex));
  244. jassert (tdc != nullptr);
  245. return tdc != nullptr ? tdc->jucerComponentFile : String();
  246. }
  247. static void setTabJucerFile (TabbedComponent* tc, int tabIndex, const String& newFile)
  248. {
  249. auto tdc = dynamic_cast<TabDemoContentComp*> (tc->getTabContentComponent (tabIndex));
  250. jassert (tdc != nullptr);
  251. if (tdc != nullptr)
  252. {
  253. tdc->jucerComponentFile = newFile;
  254. tdc->updateContent();
  255. }
  256. }
  257. private:
  258. //==============================================================================
  259. class TabDemoContentComp : public Component
  260. {
  261. public:
  262. TabDemoContentComp()
  263. : isUsingJucerComp (false)
  264. {
  265. setSize (2048, 2048);
  266. }
  267. void paint (Graphics& g) override
  268. {
  269. if (jucerComp == nullptr)
  270. g.fillCheckerBoard (getLocalBounds().toFloat(), 50.0f, 50.0f,
  271. Colour::greyLevel (0.9f).withAlpha (0.4f),
  272. Colour::greyLevel (0.8f).withAlpha (0.4f));
  273. }
  274. void resized() override
  275. {
  276. if (jucerComp != nullptr)
  277. {
  278. jucerComp->setBounds (getLocalBounds());
  279. setOpaque (jucerComp->isOpaque());
  280. }
  281. }
  282. void updateContent()
  283. {
  284. if (isUsingJucerComp)
  285. {
  286. if (jucerComp == nullptr
  287. || jucerComp->getOwnerDocument() == nullptr
  288. || jucerComp->getFilename() != jucerComponentFile)
  289. {
  290. jucerComp.reset();
  291. jucerComp.reset (new TestComponent (ComponentTypeHandler::findParentDocument (this), nullptr, false));
  292. jucerComp->setFilename (jucerComponentFile);
  293. jucerComp->setToInitialSize();
  294. addAndMakeVisible (jucerComp.get());
  295. }
  296. }
  297. else
  298. {
  299. jucerComp.reset();
  300. }
  301. resized();
  302. }
  303. void parentHierarchyChanged() override
  304. {
  305. updateContent();
  306. }
  307. bool isUsingJucerComp;
  308. String contentClassName, constructorParams;
  309. String jucerComponentFile;
  310. std::unique_ptr<TestComponent> jucerComp;
  311. };
  312. //==============================================================================
  313. class TabOrientationProperty : public ComponentChoiceProperty<TabbedComponent>
  314. {
  315. public:
  316. TabOrientationProperty (TabbedComponent* comp, JucerDocument& doc)
  317. : ComponentChoiceProperty<TabbedComponent> ("tab position", comp, doc)
  318. {
  319. choices.add ("Tabs at top");
  320. choices.add ("Tabs at bottom");
  321. choices.add ("Tabs at left");
  322. choices.add ("Tabs at right");
  323. }
  324. void setIndex (int newIndex)
  325. {
  326. const TabbedButtonBar::Orientation orientations[] = { TabbedButtonBar::TabsAtTop,
  327. TabbedButtonBar::TabsAtBottom,
  328. TabbedButtonBar::TabsAtLeft,
  329. TabbedButtonBar::TabsAtRight };
  330. document.perform (new TabOrienationChangeAction (component, *document.getComponentLayout(), orientations [newIndex]),
  331. "Change TabComponent orientation");
  332. }
  333. int getIndex() const
  334. {
  335. switch (component->getOrientation())
  336. {
  337. case TabbedButtonBar::TabsAtTop: return 0;
  338. case TabbedButtonBar::TabsAtBottom: return 1;
  339. case TabbedButtonBar::TabsAtLeft: return 2;
  340. case TabbedButtonBar::TabsAtRight: return 3;
  341. default: jassertfalse; break;
  342. }
  343. return 0;
  344. }
  345. private:
  346. class TabOrienationChangeAction : public ComponentUndoableAction<TabbedComponent>
  347. {
  348. public:
  349. TabOrienationChangeAction (TabbedComponent* const comp, ComponentLayout& l, const TabbedButtonBar::Orientation newState_)
  350. : ComponentUndoableAction<TabbedComponent> (comp, l),
  351. newState (newState_)
  352. {
  353. oldState = comp->getOrientation();
  354. }
  355. bool perform()
  356. {
  357. showCorrectTab();
  358. getComponent()->setOrientation (newState);
  359. changed();
  360. return true;
  361. }
  362. bool undo()
  363. {
  364. showCorrectTab();
  365. getComponent()->setOrientation (oldState);
  366. changed();
  367. return true;
  368. }
  369. TabbedButtonBar::Orientation newState, oldState;
  370. };
  371. };
  372. //==============================================================================
  373. class TabInitialTabProperty : public ComponentChoiceProperty<TabbedComponent>
  374. {
  375. public:
  376. TabInitialTabProperty (TabbedComponent* comp, JucerDocument& doc)
  377. : ComponentChoiceProperty<TabbedComponent> ("initial tab", comp, doc)
  378. {
  379. for (int i = 0; i < comp->getNumTabs(); ++i)
  380. choices.add ("Tab " + String (i) + ": \"" + comp->getTabNames() [i] + "\"");
  381. }
  382. void setIndex (int newIndex)
  383. {
  384. document.perform (new InitialTabChangeAction (component, *document.getComponentLayout(), newIndex),
  385. "Change initial tab");
  386. }
  387. int getIndex() const
  388. {
  389. return component->getCurrentTabIndex();
  390. }
  391. private:
  392. class InitialTabChangeAction : public ComponentUndoableAction<TabbedComponent>
  393. {
  394. public:
  395. InitialTabChangeAction (TabbedComponent* const comp, ComponentLayout& l, const int newValue_)
  396. : ComponentUndoableAction<TabbedComponent> (comp, l),
  397. newValue (newValue_)
  398. {
  399. oldValue = comp->getCurrentTabIndex();
  400. }
  401. bool perform()
  402. {
  403. showCorrectTab();
  404. getComponent()->setCurrentTabIndex (newValue);
  405. changed();
  406. return true;
  407. }
  408. bool undo()
  409. {
  410. showCorrectTab();
  411. getComponent()->setCurrentTabIndex (oldValue);
  412. changed();
  413. return true;
  414. }
  415. private:
  416. int newValue, oldValue;
  417. };
  418. };
  419. //==============================================================================
  420. class TabDepthProperty : public SliderPropertyComponent,
  421. public ChangeListener
  422. {
  423. public:
  424. TabDepthProperty (TabbedComponent* comp, JucerDocument& doc)
  425. : SliderPropertyComponent ("tab depth", 10.0, 80.0, 1.0, 1.0),
  426. component (comp),
  427. document (doc)
  428. {
  429. document.addChangeListener (this);
  430. }
  431. ~TabDepthProperty()
  432. {
  433. document.removeChangeListener (this);
  434. }
  435. void setValue (double newValue)
  436. {
  437. document.getUndoManager().undoCurrentTransactionOnly();
  438. document.perform (new TabDepthChangeAction (component, *document.getComponentLayout(), roundToInt (newValue)),
  439. "Change TabComponent tab depth");
  440. }
  441. double getValue() const
  442. {
  443. return component->getTabBarDepth();
  444. }
  445. void changeListenerCallback (ChangeBroadcaster*)
  446. {
  447. refresh();
  448. }
  449. TabbedComponent* const component;
  450. JucerDocument& document;
  451. private:
  452. class TabDepthChangeAction : public ComponentUndoableAction<TabbedComponent>
  453. {
  454. public:
  455. TabDepthChangeAction (TabbedComponent* const comp, ComponentLayout& l, const int newState_)
  456. : ComponentUndoableAction<TabbedComponent> (comp, l),
  457. newState (newState_)
  458. {
  459. oldState = comp->getTabBarDepth();
  460. }
  461. bool perform()
  462. {
  463. showCorrectTab();
  464. getComponent()->setTabBarDepth (newState);
  465. changed();
  466. return true;
  467. }
  468. bool undo()
  469. {
  470. showCorrectTab();
  471. getComponent()->setTabBarDepth (oldState);
  472. changed();
  473. return true;
  474. }
  475. int newState, oldState;
  476. };
  477. };
  478. //==============================================================================
  479. class TabAddTabProperty : public ButtonPropertyComponent
  480. {
  481. public:
  482. TabAddTabProperty (TabbedComponent* comp, JucerDocument& doc)
  483. : ButtonPropertyComponent ("add tab", false),
  484. component (comp),
  485. document (doc)
  486. {
  487. }
  488. void buttonClicked()
  489. {
  490. document.perform (new AddTabAction (component, *document.getComponentLayout()),
  491. "Add a new tab");
  492. }
  493. String getButtonText() const
  494. {
  495. return "Create a new tab";
  496. }
  497. TabbedComponent* const component;
  498. JucerDocument& document;
  499. private:
  500. class AddTabAction : public ComponentUndoableAction<TabbedComponent>
  501. {
  502. public:
  503. AddTabAction (TabbedComponent* const comp, ComponentLayout& l)
  504. : ComponentUndoableAction<TabbedComponent> (comp, l)
  505. {
  506. }
  507. bool perform()
  508. {
  509. showCorrectTab();
  510. addNewTab (getComponent());
  511. layout.getDocument()->refreshAllPropertyComps();
  512. changed();
  513. return true;
  514. }
  515. bool undo()
  516. {
  517. showCorrectTab();
  518. getComponent()->removeTab (getComponent()->getNumTabs() - 1);
  519. layout.getDocument()->refreshAllPropertyComps();
  520. changed();
  521. return true;
  522. }
  523. };
  524. };
  525. //==============================================================================
  526. class TabRemoveTabProperty : public ButtonPropertyComponent
  527. {
  528. public:
  529. TabRemoveTabProperty (TabbedComponent* comp, JucerDocument& doc)
  530. : ButtonPropertyComponent ("remove tab", true),
  531. component (comp),
  532. document (doc)
  533. {
  534. }
  535. void buttonClicked()
  536. {
  537. const StringArray names (component->getTabNames());
  538. PopupMenu m;
  539. for (int i = 0; i < component->getNumTabs(); ++i)
  540. m.addItem (i + 1, "Delete tab " + String (i)
  541. + ": \"" + names[i] + "\"");
  542. const int r = m.showAt (this);
  543. if (r > 0)
  544. {
  545. document.perform (new RemoveTabAction (component, *document.getComponentLayout(), r - 1),
  546. "Remove a tab");
  547. }
  548. }
  549. String getButtonText() const
  550. {
  551. return "Delete a tab...";
  552. }
  553. TabbedComponent* const component;
  554. JucerDocument& document;
  555. private:
  556. class RemoveTabAction : public ComponentUndoableAction<TabbedComponent>
  557. {
  558. public:
  559. RemoveTabAction (TabbedComponent* const comp, ComponentLayout& l, int indexToRemove_)
  560. : ComponentUndoableAction<TabbedComponent> (comp, l),
  561. indexToRemove (indexToRemove_)
  562. {
  563. previousState = getTabState (comp, indexToRemove);
  564. }
  565. bool perform()
  566. {
  567. showCorrectTab();
  568. getComponent()->removeTab (indexToRemove);
  569. layout.getDocument()->refreshAllPropertyComps();
  570. changed();
  571. return true;
  572. }
  573. bool undo()
  574. {
  575. showCorrectTab();
  576. addNewTab (getComponent(), indexToRemove);
  577. restoreTabState (getComponent(), indexToRemove, *previousState);
  578. layout.getDocument()->refreshAllPropertyComps();
  579. changed();
  580. return true;
  581. }
  582. private:
  583. int indexToRemove;
  584. std::unique_ptr<XmlElement> previousState;
  585. };
  586. };
  587. //==============================================================================
  588. class TabNameProperty : public ComponentTextProperty<TabbedComponent>
  589. {
  590. public:
  591. TabNameProperty (TabbedComponent* comp, JucerDocument& doc, const int tabIndex_)
  592. : ComponentTextProperty<TabbedComponent> ("name", 200, false, comp, doc),
  593. tabIndex (tabIndex_)
  594. {
  595. }
  596. void setText (const String& newText) override
  597. {
  598. document.perform (new TabNameChangeAction (component, *document.getComponentLayout(), tabIndex, newText),
  599. "Change tab name");
  600. }
  601. String getText() const override
  602. {
  603. return component->getTabNames() [tabIndex];
  604. }
  605. private:
  606. int tabIndex;
  607. class TabNameChangeAction : public ComponentUndoableAction<TabbedComponent>
  608. {
  609. public:
  610. TabNameChangeAction (TabbedComponent* const comp, ComponentLayout& l, const int tabIndex_, const String& newValue_)
  611. : ComponentUndoableAction<TabbedComponent> (comp, l),
  612. tabIndex (tabIndex_),
  613. newValue (newValue_)
  614. {
  615. oldValue = comp->getTabNames() [tabIndex];
  616. }
  617. bool perform()
  618. {
  619. showCorrectTab();
  620. getComponent()->setTabName (tabIndex, newValue);
  621. changed();
  622. return true;
  623. }
  624. bool undo()
  625. {
  626. showCorrectTab();
  627. getComponent()->setTabName (tabIndex, oldValue);
  628. changed();
  629. return true;
  630. }
  631. private:
  632. const int tabIndex;
  633. String newValue, oldValue;
  634. };
  635. };
  636. //==============================================================================
  637. class TabColourProperty : public JucerColourPropertyComponent,
  638. private ChangeListener
  639. {
  640. public:
  641. TabColourProperty (TabbedComponent* comp, JucerDocument& doc, const int tabIndex_)
  642. : JucerColourPropertyComponent ("colour", false),
  643. component (comp),
  644. document (doc),
  645. tabIndex (tabIndex_)
  646. {
  647. document.addChangeListener (this);
  648. }
  649. ~TabColourProperty() override
  650. {
  651. document.removeChangeListener (this);
  652. }
  653. void setColour (Colour newColour) override
  654. {
  655. document.getUndoManager().undoCurrentTransactionOnly();
  656. document.perform (new TabColourChangeAction (component, *document.getComponentLayout(), tabIndex, newColour),
  657. "Change tab colour");
  658. }
  659. Colour getColour() const override
  660. {
  661. return component->getTabBackgroundColour (tabIndex);
  662. }
  663. void resetToDefault() override
  664. {
  665. jassertfalse; // shouldn't get called
  666. }
  667. void changeListenerCallback (ChangeBroadcaster*) override { refresh(); }
  668. private:
  669. TabbedComponent* component;
  670. JucerDocument& document;
  671. int tabIndex;
  672. class TabColourChangeAction : public ComponentUndoableAction<TabbedComponent>
  673. {
  674. public:
  675. TabColourChangeAction (TabbedComponent* comp, ComponentLayout& l,
  676. int tabIndex_, Colour newValue_)
  677. : ComponentUndoableAction<TabbedComponent> (comp, l),
  678. tabIndex (tabIndex_),
  679. newValue (newValue_)
  680. {
  681. oldValue = comp->getTabBackgroundColour (tabIndex);
  682. }
  683. bool perform()
  684. {
  685. showCorrectTab();
  686. getComponent()->setTabBackgroundColour (tabIndex, newValue);
  687. changed();
  688. return true;
  689. }
  690. bool undo()
  691. {
  692. showCorrectTab();
  693. getComponent()->setTabBackgroundColour (tabIndex, oldValue);
  694. changed();
  695. return true;
  696. }
  697. private:
  698. const int tabIndex;
  699. Colour newValue, oldValue;
  700. };
  701. };
  702. //==============================================================================
  703. class TabContentTypeProperty : public ComponentChoiceProperty<TabbedComponent>
  704. {
  705. public:
  706. TabContentTypeProperty (TabbedComponent* comp, JucerDocument& doc, const int tabIndex_)
  707. : ComponentChoiceProperty<TabbedComponent> ("content type", comp, doc),
  708. tabIndex (tabIndex_)
  709. {
  710. choices.add ("Jucer content component");
  711. choices.add ("Named content component");
  712. }
  713. void setIndex (int newIndex)
  714. {
  715. document.perform (new TabContentTypeChangeAction (component, *document.getComponentLayout(), tabIndex, newIndex == 0),
  716. "Change tab content type");
  717. }
  718. int getIndex() const
  719. {
  720. return isTabUsingJucerComp (component, tabIndex) ? 0 : 1;
  721. }
  722. private:
  723. int tabIndex;
  724. class TabContentTypeChangeAction : public ComponentUndoableAction<TabbedComponent>
  725. {
  726. public:
  727. TabContentTypeChangeAction (TabbedComponent* const comp, ComponentLayout& l, const int tabIndex_, const bool newValue_)
  728. : ComponentUndoableAction<TabbedComponent> (comp, l),
  729. tabIndex (tabIndex_),
  730. newValue (newValue_)
  731. {
  732. oldValue = isTabUsingJucerComp (comp, tabIndex);
  733. }
  734. bool perform()
  735. {
  736. showCorrectTab();
  737. setTabUsingJucerComp (getComponent(), tabIndex, newValue);
  738. layout.getDocument()->refreshAllPropertyComps();
  739. changed();
  740. return true;
  741. }
  742. bool undo()
  743. {
  744. showCorrectTab();
  745. setTabUsingJucerComp (getComponent(), tabIndex, oldValue);
  746. layout.getDocument()->refreshAllPropertyComps();
  747. changed();
  748. return true;
  749. }
  750. private:
  751. int tabIndex;
  752. bool newValue, oldValue;
  753. };
  754. };
  755. //==============================================================================
  756. class TabJucerFileProperty : public FilePropertyComponent,
  757. public ChangeListener
  758. {
  759. public:
  760. TabJucerFileProperty (TabbedComponent* const comp, JucerDocument& doc, const int tabIndex_)
  761. : FilePropertyComponent ("jucer file", false, true),
  762. component (comp),
  763. document (doc),
  764. tabIndex (tabIndex_)
  765. {
  766. document.addChangeListener (this);
  767. }
  768. ~TabJucerFileProperty()
  769. {
  770. document.removeChangeListener (this);
  771. }
  772. //==============================================================================
  773. void setFile (const File& newFile)
  774. {
  775. document.perform (new JucerCompFileChangeAction (component, *document.getComponentLayout(), tabIndex,
  776. newFile.getRelativePathFrom (document.getCppFile().getParentDirectory())
  777. .replaceCharacter ('\\', '/')),
  778. "Change tab component file");
  779. }
  780. File getFile() const
  781. {
  782. return document.getCppFile().getSiblingFile (getTabJucerFile (component, tabIndex));
  783. }
  784. void changeListenerCallback (ChangeBroadcaster*) { refresh(); }
  785. private:
  786. TabbedComponent* const component;
  787. JucerDocument& document;
  788. int tabIndex;
  789. class JucerCompFileChangeAction : public ComponentUndoableAction<TabbedComponent>
  790. {
  791. public:
  792. JucerCompFileChangeAction (TabbedComponent* const comp, ComponentLayout& l, const int tabIndex_, const String& newState_)
  793. : ComponentUndoableAction<TabbedComponent> (comp, l),
  794. tabIndex (tabIndex_),
  795. newState (newState_)
  796. {
  797. oldState = getTabJucerFile (comp, tabIndex);
  798. }
  799. bool perform()
  800. {
  801. showCorrectTab();
  802. setTabJucerFile (getComponent(), tabIndex, newState);
  803. changed();
  804. return true;
  805. }
  806. bool undo()
  807. {
  808. showCorrectTab();
  809. setTabJucerFile (getComponent(), tabIndex, oldState);
  810. changed();
  811. return true;
  812. }
  813. int tabIndex;
  814. String newState, oldState;
  815. };
  816. };
  817. //==============================================================================
  818. class TabContentClassProperty : public ComponentTextProperty<TabbedComponent>
  819. {
  820. public:
  821. TabContentClassProperty (TabbedComponent* comp, JucerDocument& doc, const int tabIndex_)
  822. : ComponentTextProperty<TabbedComponent> ("content class", 256, false, comp, doc),
  823. tabIndex (tabIndex_)
  824. {
  825. }
  826. void setText (const String& newText) override
  827. {
  828. document.perform (new TabClassNameChangeAction (component, *document.getComponentLayout(), tabIndex, newText),
  829. "Change TabbedComponent content class");
  830. }
  831. String getText() const override
  832. {
  833. return getTabClassName (component, tabIndex);
  834. }
  835. private:
  836. int tabIndex;
  837. class TabClassNameChangeAction : public ComponentUndoableAction<TabbedComponent>
  838. {
  839. public:
  840. TabClassNameChangeAction (TabbedComponent* const comp, ComponentLayout& l, const int tabIndex_, const String& newValue_)
  841. : ComponentUndoableAction<TabbedComponent> (comp, l),
  842. tabIndex (tabIndex_),
  843. newValue (newValue_)
  844. {
  845. oldValue = getTabClassName (comp, tabIndex);
  846. }
  847. bool perform()
  848. {
  849. showCorrectTab();
  850. setTabClassName (getComponent(), tabIndex, newValue);
  851. changed();
  852. layout.getDocument()->refreshAllPropertyComps();
  853. return true;
  854. }
  855. bool undo()
  856. {
  857. showCorrectTab();
  858. setTabClassName (getComponent(), tabIndex, oldValue);
  859. changed();
  860. layout.getDocument()->refreshAllPropertyComps();
  861. return true;
  862. }
  863. int tabIndex;
  864. String newValue, oldValue;
  865. };
  866. };
  867. //==============================================================================
  868. class TabContentConstructorParamsProperty : public ComponentTextProperty<TabbedComponent>
  869. {
  870. public:
  871. TabContentConstructorParamsProperty (TabbedComponent* comp, JucerDocument& doc, const int tabIndex_)
  872. : ComponentTextProperty<TabbedComponent> ("constructor params", 512, false, comp, doc),
  873. tabIndex (tabIndex_)
  874. {
  875. }
  876. void setText (const String& newText) override
  877. {
  878. document.perform (new TabConstructorParamChangeAction (component, *document.getComponentLayout(), tabIndex, newText),
  879. "Change TabbedComponent content constructor param");
  880. }
  881. String getText() const override
  882. {
  883. return getTabConstructorParams (component, tabIndex);
  884. }
  885. private:
  886. int tabIndex;
  887. class TabConstructorParamChangeAction : public ComponentUndoableAction<TabbedComponent>
  888. {
  889. public:
  890. TabConstructorParamChangeAction (TabbedComponent* const comp, ComponentLayout& l, const int tabIndex_, const String& newValue_)
  891. : ComponentUndoableAction<TabbedComponent> (comp, l),
  892. tabIndex (tabIndex_),
  893. newValue (newValue_)
  894. {
  895. oldValue = getTabConstructorParams (comp, tabIndex);
  896. }
  897. bool perform()
  898. {
  899. showCorrectTab();
  900. setTabConstructorParams (getComponent(), tabIndex, newValue);
  901. changed();
  902. layout.getDocument()->refreshAllPropertyComps();
  903. return true;
  904. }
  905. bool undo()
  906. {
  907. showCorrectTab();
  908. setTabConstructorParams (getComponent(), tabIndex, oldValue);
  909. changed();
  910. layout.getDocument()->refreshAllPropertyComps();
  911. return true;
  912. }
  913. int tabIndex;
  914. String newValue, oldValue;
  915. };
  916. };
  917. //==============================================================================
  918. class TabMoveProperty : public ButtonPropertyComponent
  919. {
  920. public:
  921. TabMoveProperty (TabbedComponent* comp, JucerDocument& doc,
  922. const int tabIndex_, const int totalNumTabs_)
  923. : ButtonPropertyComponent ("move tab", false),
  924. component (comp),
  925. document (doc),
  926. tabIndex (tabIndex_),
  927. totalNumTabs (totalNumTabs_)
  928. {
  929. }
  930. void buttonClicked()
  931. {
  932. PopupMenu m;
  933. m.addItem (1, "Move this tab up", tabIndex > 0);
  934. m.addItem (2, "Move this tab down", tabIndex < totalNumTabs - 1);
  935. const int r = m.showAt (this);
  936. if (r != 0)
  937. document.perform (new MoveTabAction (component, *document.getComponentLayout(), tabIndex, tabIndex + (r == 2 ? 1 : -1)),
  938. "Move a tab");
  939. }
  940. String getButtonText() const
  941. {
  942. return "Move this tab...";
  943. }
  944. TabbedComponent* const component;
  945. JucerDocument& document;
  946. const int tabIndex, totalNumTabs;
  947. private:
  948. class MoveTabAction : public ComponentUndoableAction<TabbedComponent>
  949. {
  950. public:
  951. MoveTabAction (TabbedComponent* const comp, ComponentLayout& l,
  952. const int oldIndex_, const int newIndex_)
  953. : ComponentUndoableAction<TabbedComponent> (comp, l),
  954. oldIndex (oldIndex_),
  955. newIndex (newIndex_)
  956. {
  957. }
  958. void move (int from, int to)
  959. {
  960. showCorrectTab();
  961. auto state = getTabState (getComponent(), from);
  962. getComponent()->removeTab (from);
  963. addNewTab (getComponent(), to);
  964. restoreTabState (getComponent(), to, *state);
  965. layout.getDocument()->refreshAllPropertyComps();
  966. changed();
  967. }
  968. bool perform()
  969. {
  970. move (oldIndex, newIndex);
  971. return true;
  972. }
  973. bool undo()
  974. {
  975. move (newIndex, oldIndex);
  976. return true;
  977. }
  978. private:
  979. const int oldIndex, newIndex;
  980. };
  981. };
  982. };