juce_TabbedComponent.cpp 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  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* 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. TabbedButtonBar::Orientation orientation, 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 (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 (TabbedButtonBar::Orientation orientation)
  71. {
  72. tabs.reset (new ButtonBar (*this, orientation));
  73. addAndMakeVisible (tabs.get());
  74. }
  75. TabbedComponent::~TabbedComponent()
  76. {
  77. clearTabs();
  78. tabs.reset();
  79. }
  80. //==============================================================================
  81. void TabbedComponent::setOrientation (TabbedButtonBar::Orientation orientation)
  82. {
  83. tabs->setOrientation (orientation);
  84. resized();
  85. }
  86. TabbedButtonBar::Orientation TabbedComponent::getOrientation() const noexcept
  87. {
  88. return tabs->getOrientation();
  89. }
  90. void TabbedComponent::setTabBarDepth (int newDepth)
  91. {
  92. if (tabDepth != newDepth)
  93. {
  94. tabDepth = newDepth;
  95. resized();
  96. }
  97. }
  98. TabBarButton* TabbedComponent::createTabButton (const String& tabName, int /*tabIndex*/)
  99. {
  100. return new TabBarButton (tabName, *tabs);
  101. }
  102. //==============================================================================
  103. void TabbedComponent::clearTabs()
  104. {
  105. if (panelComponent != nullptr)
  106. {
  107. panelComponent->setVisible (false);
  108. removeChildComponent (panelComponent.get());
  109. panelComponent = nullptr;
  110. }
  111. tabs->clearTabs();
  112. for (int i = contentComponents.size(); --i >= 0;)
  113. TabbedComponentHelpers::deleteIfNecessary (contentComponents.getReference (i));
  114. contentComponents.clear();
  115. }
  116. void TabbedComponent::addTab (const String& tabName,
  117. Colour tabBackgroundColour,
  118. Component* contentComponent,
  119. bool deleteComponentWhenNotNeeded,
  120. int insertIndex)
  121. {
  122. contentComponents.insert (insertIndex, WeakReference<Component> (contentComponent));
  123. if (deleteComponentWhenNotNeeded && contentComponent != nullptr)
  124. contentComponent->getProperties().set (TabbedComponentHelpers::deleteComponentId, true);
  125. tabs->addTab (tabName, tabBackgroundColour, insertIndex);
  126. resized();
  127. }
  128. void TabbedComponent::setTabName (int tabIndex, const String& newName)
  129. {
  130. tabs->setTabName (tabIndex, newName);
  131. }
  132. void TabbedComponent::removeTab (int tabIndex)
  133. {
  134. if (isPositiveAndBelow (tabIndex, contentComponents.size()))
  135. {
  136. TabbedComponentHelpers::deleteIfNecessary (contentComponents.getReference (tabIndex).get());
  137. contentComponents.remove (tabIndex);
  138. tabs->removeTab (tabIndex);
  139. }
  140. }
  141. void TabbedComponent::moveTab (int currentIndex, int newIndex, bool animate)
  142. {
  143. contentComponents.move (currentIndex, newIndex);
  144. tabs->moveTab (currentIndex, newIndex, animate);
  145. }
  146. int TabbedComponent::getNumTabs() const
  147. {
  148. return tabs->getNumTabs();
  149. }
  150. StringArray TabbedComponent::getTabNames() const
  151. {
  152. return tabs->getTabNames();
  153. }
  154. Component* TabbedComponent::getTabContentComponent (int tabIndex) const noexcept
  155. {
  156. return contentComponents[tabIndex].get();
  157. }
  158. Colour TabbedComponent::getTabBackgroundColour (int tabIndex) const noexcept
  159. {
  160. return tabs->getTabBackgroundColour (tabIndex);
  161. }
  162. void TabbedComponent::setTabBackgroundColour (int tabIndex, Colour newColour)
  163. {
  164. tabs->setTabBackgroundColour (tabIndex, newColour);
  165. if (getCurrentTabIndex() == tabIndex)
  166. repaint();
  167. }
  168. void TabbedComponent::setCurrentTabIndex (int newTabIndex, bool sendChangeMessage)
  169. {
  170. tabs->setCurrentTabIndex (newTabIndex, sendChangeMessage);
  171. }
  172. int TabbedComponent::getCurrentTabIndex() const
  173. {
  174. return tabs->getCurrentTabIndex();
  175. }
  176. String TabbedComponent::getCurrentTabName() const
  177. {
  178. return tabs->getCurrentTabName();
  179. }
  180. void TabbedComponent::setOutline (int thickness)
  181. {
  182. outlineThickness = thickness;
  183. resized();
  184. repaint();
  185. }
  186. void TabbedComponent::setIndent (int indentThickness)
  187. {
  188. edgeIndent = indentThickness;
  189. resized();
  190. repaint();
  191. }
  192. void TabbedComponent::paint (Graphics& g)
  193. {
  194. g.fillAll (findColour (backgroundColourId));
  195. auto content = getLocalBounds();
  196. BorderSize<int> outline (outlineThickness);
  197. TabbedComponentHelpers::getTabArea (content, outline, getOrientation(), tabDepth);
  198. g.reduceClipRegion (content);
  199. g.fillAll (tabs->getTabBackgroundColour (getCurrentTabIndex()));
  200. if (outlineThickness > 0)
  201. {
  202. RectangleList<int> rl (content);
  203. rl.subtract (outline.subtractedFrom (content));
  204. g.reduceClipRegion (rl);
  205. g.fillAll (findColour (outlineColourId));
  206. }
  207. }
  208. void TabbedComponent::resized()
  209. {
  210. auto content = getLocalBounds();
  211. BorderSize<int> outline (outlineThickness);
  212. tabs->setBounds (TabbedComponentHelpers::getTabArea (content, outline, getOrientation(), tabDepth));
  213. content = BorderSize<int> (edgeIndent).subtractedFrom (outline.subtractedFrom (content));
  214. for (auto& c : contentComponents)
  215. if (auto comp = c.get())
  216. comp->setBounds (content);
  217. }
  218. void TabbedComponent::lookAndFeelChanged()
  219. {
  220. for (auto& c : contentComponents)
  221. if (auto comp = c.get())
  222. comp->lookAndFeelChanged();
  223. }
  224. void TabbedComponent::changeCallback (int newCurrentTabIndex, const String& newTabName)
  225. {
  226. auto* newPanelComp = getTabContentComponent (getCurrentTabIndex());
  227. if (newPanelComp != panelComponent)
  228. {
  229. if (panelComponent != nullptr)
  230. {
  231. panelComponent->setVisible (false);
  232. removeChildComponent (panelComponent);
  233. }
  234. panelComponent = newPanelComp;
  235. if (panelComponent != nullptr)
  236. {
  237. // do these ops as two stages instead of addAndMakeVisible() so that the
  238. // component has always got a parent when it gets the visibilityChanged() callback
  239. addChildComponent (panelComponent);
  240. panelComponent->sendLookAndFeelChange();
  241. panelComponent->setVisible (true);
  242. panelComponent->toFront (true);
  243. }
  244. repaint();
  245. }
  246. resized();
  247. currentTabChanged (newCurrentTabIndex, newTabName);
  248. }
  249. void TabbedComponent::currentTabChanged (int, const String&) {}
  250. void TabbedComponent::popupMenuClickOnTab (int, const String&) {}
  251. } // namespace juce