Audio plugin host https://kx.studio/carla
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.

314 lines
9.4KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2015 - ROLI Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. namespace TabbedComponentHelpers
  18. {
  19. const Identifier deleteComponentId ("deleteByTabComp_");
  20. static void deleteIfNecessary (Component* const comp)
  21. {
  22. if (comp != nullptr && (bool) comp->getProperties() [deleteComponentId])
  23. delete comp;
  24. }
  25. static Rectangle<int> getTabArea (Rectangle<int>& content, BorderSize<int>& outline,
  26. const TabbedButtonBar::Orientation orientation, const int tabDepth)
  27. {
  28. switch (orientation)
  29. {
  30. case TabbedButtonBar::TabsAtTop: outline.setTop (0); return content.removeFromTop (tabDepth);
  31. case TabbedButtonBar::TabsAtBottom: outline.setBottom (0); return content.removeFromBottom (tabDepth);
  32. case TabbedButtonBar::TabsAtLeft: outline.setLeft (0); return content.removeFromLeft (tabDepth);
  33. case TabbedButtonBar::TabsAtRight: outline.setRight (0); return content.removeFromRight (tabDepth);
  34. default: jassertfalse; break;
  35. }
  36. return Rectangle<int>();
  37. }
  38. }
  39. //==============================================================================
  40. class TabbedComponent::ButtonBar : public TabbedButtonBar
  41. {
  42. public:
  43. ButtonBar (TabbedComponent& owner_, const TabbedButtonBar::Orientation orientation_)
  44. : TabbedButtonBar (orientation_),
  45. owner (owner_)
  46. {
  47. }
  48. void currentTabChanged (int newCurrentTabIndex, const String& newTabName)
  49. {
  50. owner.changeCallback (newCurrentTabIndex, newTabName);
  51. }
  52. void popupMenuClickOnTab (int tabIndex, const String& tabName)
  53. {
  54. owner.popupMenuClickOnTab (tabIndex, tabName);
  55. }
  56. Colour getTabBackgroundColour (const int tabIndex)
  57. {
  58. return owner.tabs->getTabBackgroundColour (tabIndex);
  59. }
  60. TabBarButton* createTabButton (const String& tabName, int tabIndex)
  61. {
  62. return owner.createTabButton (tabName, tabIndex);
  63. }
  64. private:
  65. TabbedComponent& owner;
  66. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ButtonBar)
  67. };
  68. //==============================================================================
  69. TabbedComponent::TabbedComponent (const TabbedButtonBar::Orientation orientation)
  70. : tabDepth (30),
  71. outlineThickness (1),
  72. edgeIndent (0)
  73. {
  74. addAndMakeVisible (tabs = new ButtonBar (*this, orientation));
  75. }
  76. TabbedComponent::~TabbedComponent()
  77. {
  78. clearTabs();
  79. tabs = nullptr;
  80. }
  81. //==============================================================================
  82. void TabbedComponent::setOrientation (const TabbedButtonBar::Orientation orientation)
  83. {
  84. tabs->setOrientation (orientation);
  85. resized();
  86. }
  87. TabbedButtonBar::Orientation TabbedComponent::getOrientation() const noexcept
  88. {
  89. return tabs->getOrientation();
  90. }
  91. void TabbedComponent::setTabBarDepth (const int newDepth)
  92. {
  93. if (tabDepth != newDepth)
  94. {
  95. tabDepth = newDepth;
  96. resized();
  97. }
  98. }
  99. TabBarButton* TabbedComponent::createTabButton (const String& tabName, const int /*tabIndex*/)
  100. {
  101. return new TabBarButton (tabName, *tabs);
  102. }
  103. //==============================================================================
  104. void TabbedComponent::clearTabs()
  105. {
  106. if (panelComponent != nullptr)
  107. {
  108. panelComponent->setVisible (false);
  109. removeChildComponent (panelComponent);
  110. panelComponent = nullptr;
  111. }
  112. tabs->clearTabs();
  113. for (int i = contentComponents.size(); --i >= 0;)
  114. TabbedComponentHelpers::deleteIfNecessary (contentComponents.getReference (i));
  115. contentComponents.clear();
  116. }
  117. void TabbedComponent::addTab (const String& tabName,
  118. Colour tabBackgroundColour,
  119. Component* const contentComponent,
  120. const bool deleteComponentWhenNotNeeded,
  121. const int insertIndex)
  122. {
  123. contentComponents.insert (insertIndex, WeakReference<Component> (contentComponent));
  124. if (deleteComponentWhenNotNeeded && contentComponent != nullptr)
  125. contentComponent->getProperties().set (TabbedComponentHelpers::deleteComponentId, true);
  126. tabs->addTab (tabName, tabBackgroundColour, insertIndex);
  127. resized();
  128. }
  129. void TabbedComponent::setTabName (const int tabIndex, const String& newName)
  130. {
  131. tabs->setTabName (tabIndex, newName);
  132. }
  133. void TabbedComponent::removeTab (const int tabIndex)
  134. {
  135. if (isPositiveAndBelow (tabIndex, contentComponents.size()))
  136. {
  137. TabbedComponentHelpers::deleteIfNecessary (contentComponents.getReference (tabIndex));
  138. contentComponents.remove (tabIndex);
  139. tabs->removeTab (tabIndex);
  140. }
  141. }
  142. void TabbedComponent::moveTab (const int currentIndex, const int newIndex, const bool animate)
  143. {
  144. contentComponents.move (currentIndex, newIndex);
  145. tabs->moveTab (currentIndex, newIndex, animate);
  146. }
  147. int TabbedComponent::getNumTabs() const
  148. {
  149. return tabs->getNumTabs();
  150. }
  151. StringArray TabbedComponent::getTabNames() const
  152. {
  153. return tabs->getTabNames();
  154. }
  155. Component* TabbedComponent::getTabContentComponent (const int tabIndex) const noexcept
  156. {
  157. return contentComponents [tabIndex];
  158. }
  159. Colour TabbedComponent::getTabBackgroundColour (const int tabIndex) const noexcept
  160. {
  161. return tabs->getTabBackgroundColour (tabIndex);
  162. }
  163. void TabbedComponent::setTabBackgroundColour (const int tabIndex, Colour newColour)
  164. {
  165. tabs->setTabBackgroundColour (tabIndex, newColour);
  166. if (getCurrentTabIndex() == tabIndex)
  167. repaint();
  168. }
  169. void TabbedComponent::setCurrentTabIndex (const int newTabIndex, const bool sendChangeMessage)
  170. {
  171. tabs->setCurrentTabIndex (newTabIndex, sendChangeMessage);
  172. }
  173. int TabbedComponent::getCurrentTabIndex() const
  174. {
  175. return tabs->getCurrentTabIndex();
  176. }
  177. String TabbedComponent::getCurrentTabName() const
  178. {
  179. return tabs->getCurrentTabName();
  180. }
  181. void TabbedComponent::setOutline (const int thickness)
  182. {
  183. outlineThickness = thickness;
  184. resized();
  185. repaint();
  186. }
  187. void TabbedComponent::setIndent (const int indentThickness)
  188. {
  189. edgeIndent = indentThickness;
  190. resized();
  191. repaint();
  192. }
  193. void TabbedComponent::paint (Graphics& g)
  194. {
  195. g.fillAll (findColour (backgroundColourId));
  196. Rectangle<int> content (getLocalBounds());
  197. BorderSize<int> outline (outlineThickness);
  198. TabbedComponentHelpers::getTabArea (content, outline, getOrientation(), tabDepth);
  199. g.reduceClipRegion (content);
  200. g.fillAll (tabs->getTabBackgroundColour (getCurrentTabIndex()));
  201. if (outlineThickness > 0)
  202. {
  203. RectangleList<int> rl (content);
  204. rl.subtract (outline.subtractedFrom (content));
  205. g.reduceClipRegion (rl);
  206. g.fillAll (findColour (outlineColourId));
  207. }
  208. }
  209. void TabbedComponent::resized()
  210. {
  211. Rectangle<int> content (getLocalBounds());
  212. BorderSize<int> outline (outlineThickness);
  213. tabs->setBounds (TabbedComponentHelpers::getTabArea (content, outline, getOrientation(), tabDepth));
  214. content = BorderSize<int> (edgeIndent).subtractedFrom (outline.subtractedFrom (content));
  215. for (int i = contentComponents.size(); --i >= 0;)
  216. if (Component* c = contentComponents.getReference(i))
  217. c->setBounds (content);
  218. }
  219. void TabbedComponent::lookAndFeelChanged()
  220. {
  221. for (int i = contentComponents.size(); --i >= 0;)
  222. if (Component* c = contentComponents.getReference(i))
  223. c->lookAndFeelChanged();
  224. }
  225. void TabbedComponent::changeCallback (const int newCurrentTabIndex, const String& newTabName)
  226. {
  227. Component* const newPanelComp = getTabContentComponent (getCurrentTabIndex());
  228. if (newPanelComp != panelComponent)
  229. {
  230. if (panelComponent != nullptr)
  231. {
  232. panelComponent->setVisible (false);
  233. removeChildComponent (panelComponent);
  234. }
  235. panelComponent = newPanelComp;
  236. if (panelComponent != nullptr)
  237. {
  238. // do these ops as two stages instead of addAndMakeVisible() so that the
  239. // component has always got a parent when it gets the visibilityChanged() callback
  240. addChildComponent (panelComponent);
  241. panelComponent->setVisible (true);
  242. panelComponent->toFront (true);
  243. }
  244. repaint();
  245. }
  246. resized();
  247. currentTabChanged (newCurrentTabIndex, newTabName);
  248. }
  249. void TabbedComponent::currentTabChanged (const int, const String&) {}
  250. void TabbedComponent::popupMenuClickOnTab (const int, const String&) {}