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.

320 lines
9.4KB

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