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.

308 lines
9.5KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. namespace TabbedComponentHelpers
  19. {
  20. const Identifier deleteComponentId ("deleteByTabComp_");
  21. static void deleteIfNecessary (Component* const comp)
  22. {
  23. if (comp != nullptr && (bool) comp->getProperties() [deleteComponentId])
  24. delete comp;
  25. }
  26. static Rectangle<int> getTabArea (Rectangle<int>& content, BorderSize<int>& outline,
  27. const TabbedButtonBar::Orientation orientation, const int tabDepth)
  28. {
  29. switch (orientation)
  30. {
  31. case TabbedButtonBar::TabsAtTop: outline.setTop (0); return content.removeFromTop (tabDepth);
  32. case TabbedButtonBar::TabsAtBottom: outline.setBottom (0); return content.removeFromBottom (tabDepth);
  33. case TabbedButtonBar::TabsAtLeft: outline.setLeft (0); return content.removeFromLeft (tabDepth);
  34. case TabbedButtonBar::TabsAtRight: outline.setRight (0); return content.removeFromRight (tabDepth);
  35. default: jassertfalse; break;
  36. }
  37. return Rectangle<int>();
  38. }
  39. }
  40. //==============================================================================
  41. class TabbedComponent::ButtonBar : public TabbedButtonBar
  42. {
  43. public:
  44. ButtonBar (TabbedComponent& owner_, const TabbedButtonBar::Orientation orientation_)
  45. : TabbedButtonBar (orientation_),
  46. owner (owner_)
  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 (const 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. private:
  66. TabbedComponent& owner;
  67. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ButtonBar);
  68. };
  69. //==============================================================================
  70. TabbedComponent::TabbedComponent (const TabbedButtonBar::Orientation orientation)
  71. : tabDepth (30),
  72. outlineThickness (1),
  73. edgeIndent (0)
  74. {
  75. addAndMakeVisible (tabs = new ButtonBar (*this, orientation));
  76. }
  77. TabbedComponent::~TabbedComponent()
  78. {
  79. clearTabs();
  80. tabs = nullptr;
  81. }
  82. //==============================================================================
  83. void TabbedComponent::setOrientation (const TabbedButtonBar::Orientation orientation)
  84. {
  85. tabs->setOrientation (orientation);
  86. resized();
  87. }
  88. TabbedButtonBar::Orientation TabbedComponent::getOrientation() const noexcept
  89. {
  90. return tabs->getOrientation();
  91. }
  92. void TabbedComponent::setTabBarDepth (const int newDepth)
  93. {
  94. if (tabDepth != newDepth)
  95. {
  96. tabDepth = newDepth;
  97. resized();
  98. }
  99. }
  100. TabBarButton* TabbedComponent::createTabButton (const String& tabName, const int /*tabIndex*/)
  101. {
  102. return new TabBarButton (tabName, *tabs);
  103. }
  104. //==============================================================================
  105. void TabbedComponent::clearTabs()
  106. {
  107. if (panelComponent != nullptr)
  108. {
  109. panelComponent->setVisible (false);
  110. removeChildComponent (panelComponent);
  111. panelComponent = nullptr;
  112. }
  113. tabs->clearTabs();
  114. for (int i = contentComponents.size(); --i >= 0;)
  115. TabbedComponentHelpers::deleteIfNecessary (contentComponents.getReference (i));
  116. contentComponents.clear();
  117. }
  118. void TabbedComponent::addTab (const String& tabName,
  119. const Colour& tabBackgroundColour,
  120. Component* const contentComponent,
  121. const bool deleteComponentWhenNotNeeded,
  122. const int insertIndex)
  123. {
  124. contentComponents.insert (insertIndex, WeakReference<Component> (contentComponent));
  125. if (deleteComponentWhenNotNeeded && contentComponent != nullptr)
  126. contentComponent->getProperties().set (TabbedComponentHelpers::deleteComponentId, true);
  127. tabs->addTab (tabName, tabBackgroundColour, insertIndex);
  128. resized();
  129. }
  130. void TabbedComponent::setTabName (const int tabIndex, const String& newName)
  131. {
  132. tabs->setTabName (tabIndex, newName);
  133. }
  134. void TabbedComponent::removeTab (const int tabIndex)
  135. {
  136. if (isPositiveAndBelow (tabIndex, contentComponents.size()))
  137. {
  138. TabbedComponentHelpers::deleteIfNecessary (contentComponents.getReference (tabIndex));
  139. contentComponents.remove (tabIndex);
  140. tabs->removeTab (tabIndex);
  141. }
  142. }
  143. int TabbedComponent::getNumTabs() const
  144. {
  145. return tabs->getNumTabs();
  146. }
  147. StringArray TabbedComponent::getTabNames() const
  148. {
  149. return tabs->getTabNames();
  150. }
  151. Component* TabbedComponent::getTabContentComponent (const int tabIndex) const noexcept
  152. {
  153. return contentComponents [tabIndex];
  154. }
  155. Colour TabbedComponent::getTabBackgroundColour (const int tabIndex) const noexcept
  156. {
  157. return tabs->getTabBackgroundColour (tabIndex);
  158. }
  159. void TabbedComponent::setTabBackgroundColour (const int tabIndex, const Colour& newColour)
  160. {
  161. tabs->setTabBackgroundColour (tabIndex, newColour);
  162. if (getCurrentTabIndex() == tabIndex)
  163. repaint();
  164. }
  165. void TabbedComponent::setCurrentTabIndex (const int newTabIndex, const bool sendChangeMessage)
  166. {
  167. tabs->setCurrentTabIndex (newTabIndex, sendChangeMessage);
  168. }
  169. int TabbedComponent::getCurrentTabIndex() const
  170. {
  171. return tabs->getCurrentTabIndex();
  172. }
  173. String TabbedComponent::getCurrentTabName() const
  174. {
  175. return tabs->getCurrentTabName();
  176. }
  177. void TabbedComponent::setOutline (const int thickness)
  178. {
  179. outlineThickness = thickness;
  180. resized();
  181. repaint();
  182. }
  183. void TabbedComponent::setIndent (const int indentThickness)
  184. {
  185. edgeIndent = indentThickness;
  186. resized();
  187. repaint();
  188. }
  189. void TabbedComponent::paint (Graphics& g)
  190. {
  191. g.fillAll (findColour (backgroundColourId));
  192. Rectangle<int> content (getLocalBounds());
  193. BorderSize<int> outline (outlineThickness);
  194. TabbedComponentHelpers::getTabArea (content, outline, getOrientation(), tabDepth);
  195. g.reduceClipRegion (content);
  196. g.fillAll (tabs->getTabBackgroundColour (getCurrentTabIndex()));
  197. if (outlineThickness > 0)
  198. {
  199. RectangleList rl (content);
  200. rl.subtract (outline.subtractedFrom (content));
  201. g.reduceClipRegion (rl);
  202. g.fillAll (findColour (outlineColourId));
  203. }
  204. }
  205. void TabbedComponent::resized()
  206. {
  207. Rectangle<int> content (getLocalBounds());
  208. BorderSize<int> outline (outlineThickness);
  209. tabs->setBounds (TabbedComponentHelpers::getTabArea (content, outline, getOrientation(), tabDepth));
  210. content = BorderSize<int> (edgeIndent).subtractedFrom (outline.subtractedFrom (content));
  211. for (int i = contentComponents.size(); --i >= 0;)
  212. if (contentComponents.getReference (i) != nullptr)
  213. contentComponents.getReference (i)->setBounds (content);
  214. }
  215. void TabbedComponent::lookAndFeelChanged()
  216. {
  217. for (int i = contentComponents.size(); --i >= 0;)
  218. if (contentComponents.getReference (i) != nullptr)
  219. contentComponents.getReference (i)->lookAndFeelChanged();
  220. }
  221. void TabbedComponent::changeCallback (const int newCurrentTabIndex, const String& newTabName)
  222. {
  223. if (panelComponent != nullptr)
  224. {
  225. panelComponent->setVisible (false);
  226. removeChildComponent (panelComponent);
  227. panelComponent = nullptr;
  228. }
  229. if (getCurrentTabIndex() >= 0)
  230. {
  231. panelComponent = getTabContentComponent (getCurrentTabIndex());
  232. if (panelComponent != nullptr)
  233. {
  234. // do these ops as two stages instead of addAndMakeVisible() so that the
  235. // component has always got a parent when it gets the visibilityChanged() callback
  236. addChildComponent (panelComponent);
  237. panelComponent->setVisible (true);
  238. panelComponent->toFront (true);
  239. }
  240. repaint();
  241. }
  242. resized();
  243. currentTabChanged (newCurrentTabIndex, newTabName);
  244. }
  245. void TabbedComponent::currentTabChanged (const int, const String&) {}
  246. void TabbedComponent::popupMenuClickOnTab (const int, const String&) {}