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.

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