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.

307 lines
8.9KB

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