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.

315 lines
9.3KB

  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. struct TabbedComponent::ButtonBar : public TabbedButtonBar
  45. {
  46. ButtonBar (TabbedComponent& tabComp, TabbedButtonBar::Orientation o)
  47. : TabbedButtonBar (o), owner (tabComp)
  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. TabbedComponent& owner;
  67. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ButtonBar)
  68. };
  69. //==============================================================================
  70. TabbedComponent::TabbedComponent (const TabbedButtonBar::Orientation orientation)
  71. {
  72. addAndMakeVisible (tabs = new ButtonBar (*this, orientation));
  73. }
  74. TabbedComponent::~TabbedComponent()
  75. {
  76. clearTabs();
  77. tabs = nullptr;
  78. }
  79. //==============================================================================
  80. void TabbedComponent::setOrientation (const 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 (const int newDepth)
  90. {
  91. if (tabDepth != newDepth)
  92. {
  93. tabDepth = newDepth;
  94. resized();
  95. }
  96. }
  97. TabBarButton* TabbedComponent::createTabButton (const String& tabName, const 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);
  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* const contentComponent,
  118. const bool deleteComponentWhenNotNeeded,
  119. const 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 (const int tabIndex, const String& newName)
  128. {
  129. tabs->setTabName (tabIndex, newName);
  130. }
  131. void TabbedComponent::removeTab (const int tabIndex)
  132. {
  133. if (isPositiveAndBelow (tabIndex, contentComponents.size()))
  134. {
  135. TabbedComponentHelpers::deleteIfNecessary (contentComponents.getReference (tabIndex));
  136. contentComponents.remove (tabIndex);
  137. tabs->removeTab (tabIndex);
  138. }
  139. }
  140. void TabbedComponent::moveTab (const int currentIndex, const int newIndex, const 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 (const int tabIndex) const noexcept
  154. {
  155. return contentComponents [tabIndex];
  156. }
  157. Colour TabbedComponent::getTabBackgroundColour (const int tabIndex) const noexcept
  158. {
  159. return tabs->getTabBackgroundColour (tabIndex);
  160. }
  161. void TabbedComponent::setTabBackgroundColour (const int tabIndex, Colour newColour)
  162. {
  163. tabs->setTabBackgroundColour (tabIndex, newColour);
  164. if (getCurrentTabIndex() == tabIndex)
  165. repaint();
  166. }
  167. void TabbedComponent::setCurrentTabIndex (const int newTabIndex, const 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 (const int thickness)
  180. {
  181. outlineThickness = thickness;
  182. resized();
  183. repaint();
  184. }
  185. void TabbedComponent::setIndent (const 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 (int i = contentComponents.size(); --i >= 0;)
  214. if (Component* c = contentComponents.getReference(i))
  215. c->setBounds (content);
  216. }
  217. void TabbedComponent::lookAndFeelChanged()
  218. {
  219. for (int i = contentComponents.size(); --i >= 0;)
  220. if (Component* c = contentComponents.getReference(i))
  221. c->lookAndFeelChanged();
  222. }
  223. void TabbedComponent::changeCallback (const 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. } // namespace juce