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.

322 lines
9.5KB

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