1 /*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2020 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 By using JUCE, you agree to the terms of both the JUCE 6 End-User License
11 Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
12
13 End User License Agreement: www.juce.com/juce-6-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
15
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
18
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21 DISCLAIMED.
22
23 ==============================================================================
24 */
25
26 namespace juce
27 {
28
29 namespace TabbedComponentHelpers
30 {
31 const Identifier deleteComponentId ("deleteByTabComp_");
32
deleteIfNecessary(Component * comp)33 static void deleteIfNecessary (Component* comp)
34 {
35 if (comp != nullptr && (bool) comp->getProperties() [deleteComponentId])
36 delete comp;
37 }
38
getTabArea(Rectangle<int> & content,BorderSize<int> & outline,TabbedButtonBar::Orientation orientation,int tabDepth)39 static Rectangle<int> getTabArea (Rectangle<int>& content, BorderSize<int>& outline,
40 TabbedButtonBar::Orientation orientation, int tabDepth)
41 {
42 switch (orientation)
43 {
44 case TabbedButtonBar::TabsAtTop: outline.setTop (0); return content.removeFromTop (tabDepth);
45 case TabbedButtonBar::TabsAtBottom: outline.setBottom (0); return content.removeFromBottom (tabDepth);
46 case TabbedButtonBar::TabsAtLeft: outline.setLeft (0); return content.removeFromLeft (tabDepth);
47 case TabbedButtonBar::TabsAtRight: outline.setRight (0); return content.removeFromRight (tabDepth);
48 default: jassertfalse; break;
49 }
50
51 return Rectangle<int>();
52 }
53 }
54
55 //==============================================================================
56 struct TabbedComponent::ButtonBar : public TabbedButtonBar
57 {
ButtonBarjuce::TabbedComponent::ButtonBar58 ButtonBar (TabbedComponent& tabComp, TabbedButtonBar::Orientation o)
59 : TabbedButtonBar (o), owner (tabComp)
60 {
61 }
62
currentTabChangedjuce::TabbedComponent::ButtonBar63 void currentTabChanged (int newCurrentTabIndex, const String& newTabName)
64 {
65 owner.changeCallback (newCurrentTabIndex, newTabName);
66 }
67
popupMenuClickOnTabjuce::TabbedComponent::ButtonBar68 void popupMenuClickOnTab (int tabIndex, const String& tabName)
69 {
70 owner.popupMenuClickOnTab (tabIndex, tabName);
71 }
72
getTabBackgroundColourjuce::TabbedComponent::ButtonBar73 Colour getTabBackgroundColour (int tabIndex)
74 {
75 return owner.tabs->getTabBackgroundColour (tabIndex);
76 }
77
createTabButtonjuce::TabbedComponent::ButtonBar78 TabBarButton* createTabButton (const String& tabName, int tabIndex)
79 {
80 return owner.createTabButton (tabName, tabIndex);
81 }
82
83 TabbedComponent& owner;
84
85 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ButtonBar)
86 };
87
88 //==============================================================================
TabbedComponent(TabbedButtonBar::Orientation orientation)89 TabbedComponent::TabbedComponent (TabbedButtonBar::Orientation orientation)
90 {
91 tabs.reset (new ButtonBar (*this, orientation));
92 addAndMakeVisible (tabs.get());
93 }
94
~TabbedComponent()95 TabbedComponent::~TabbedComponent()
96 {
97 clearTabs();
98 tabs.reset();
99 }
100
101 //==============================================================================
setOrientation(TabbedButtonBar::Orientation orientation)102 void TabbedComponent::setOrientation (TabbedButtonBar::Orientation orientation)
103 {
104 tabs->setOrientation (orientation);
105 resized();
106 }
107
getOrientation() const108 TabbedButtonBar::Orientation TabbedComponent::getOrientation() const noexcept
109 {
110 return tabs->getOrientation();
111 }
112
setTabBarDepth(int newDepth)113 void TabbedComponent::setTabBarDepth (int newDepth)
114 {
115 if (tabDepth != newDepth)
116 {
117 tabDepth = newDepth;
118 resized();
119 }
120 }
121
createTabButton(const String & tabName,int)122 TabBarButton* TabbedComponent::createTabButton (const String& tabName, int /*tabIndex*/)
123 {
124 return new TabBarButton (tabName, *tabs);
125 }
126
127 //==============================================================================
clearTabs()128 void TabbedComponent::clearTabs()
129 {
130 if (panelComponent != nullptr)
131 {
132 panelComponent->setVisible (false);
133 removeChildComponent (panelComponent.get());
134 panelComponent = nullptr;
135 }
136
137 tabs->clearTabs();
138
139 for (int i = contentComponents.size(); --i >= 0;)
140 TabbedComponentHelpers::deleteIfNecessary (contentComponents.getReference (i));
141
142 contentComponents.clear();
143 }
144
addTab(const String & tabName,Colour tabBackgroundColour,Component * contentComponent,bool deleteComponentWhenNotNeeded,int insertIndex)145 void TabbedComponent::addTab (const String& tabName,
146 Colour tabBackgroundColour,
147 Component* contentComponent,
148 bool deleteComponentWhenNotNeeded,
149 int insertIndex)
150 {
151 contentComponents.insert (insertIndex, WeakReference<Component> (contentComponent));
152
153 if (deleteComponentWhenNotNeeded && contentComponent != nullptr)
154 contentComponent->getProperties().set (TabbedComponentHelpers::deleteComponentId, true);
155
156 tabs->addTab (tabName, tabBackgroundColour, insertIndex);
157 resized();
158 }
159
setTabName(int tabIndex,const String & newName)160 void TabbedComponent::setTabName (int tabIndex, const String& newName)
161 {
162 tabs->setTabName (tabIndex, newName);
163 }
164
removeTab(int tabIndex)165 void TabbedComponent::removeTab (int tabIndex)
166 {
167 if (isPositiveAndBelow (tabIndex, contentComponents.size()))
168 {
169 TabbedComponentHelpers::deleteIfNecessary (contentComponents.getReference (tabIndex).get());
170 contentComponents.remove (tabIndex);
171 tabs->removeTab (tabIndex);
172 }
173 }
174
moveTab(int currentIndex,int newIndex,bool animate)175 void TabbedComponent::moveTab (int currentIndex, int newIndex, bool animate)
176 {
177 contentComponents.move (currentIndex, newIndex);
178 tabs->moveTab (currentIndex, newIndex, animate);
179 }
180
getNumTabs() const181 int TabbedComponent::getNumTabs() const
182 {
183 return tabs->getNumTabs();
184 }
185
getTabNames() const186 StringArray TabbedComponent::getTabNames() const
187 {
188 return tabs->getTabNames();
189 }
190
getTabContentComponent(int tabIndex) const191 Component* TabbedComponent::getTabContentComponent (int tabIndex) const noexcept
192 {
193 return contentComponents[tabIndex].get();
194 }
195
getTabBackgroundColour(int tabIndex) const196 Colour TabbedComponent::getTabBackgroundColour (int tabIndex) const noexcept
197 {
198 return tabs->getTabBackgroundColour (tabIndex);
199 }
200
setTabBackgroundColour(int tabIndex,Colour newColour)201 void TabbedComponent::setTabBackgroundColour (int tabIndex, Colour newColour)
202 {
203 tabs->setTabBackgroundColour (tabIndex, newColour);
204
205 if (getCurrentTabIndex() == tabIndex)
206 repaint();
207 }
208
setCurrentTabIndex(int newTabIndex,bool sendChangeMessage)209 void TabbedComponent::setCurrentTabIndex (int newTabIndex, bool sendChangeMessage)
210 {
211 tabs->setCurrentTabIndex (newTabIndex, sendChangeMessage);
212 }
213
getCurrentTabIndex() const214 int TabbedComponent::getCurrentTabIndex() const
215 {
216 return tabs->getCurrentTabIndex();
217 }
218
getCurrentTabName() const219 String TabbedComponent::getCurrentTabName() const
220 {
221 return tabs->getCurrentTabName();
222 }
223
setOutline(int thickness)224 void TabbedComponent::setOutline (int thickness)
225 {
226 outlineThickness = thickness;
227 resized();
228 repaint();
229 }
230
setIndent(int indentThickness)231 void TabbedComponent::setIndent (int indentThickness)
232 {
233 edgeIndent = indentThickness;
234 resized();
235 repaint();
236 }
237
paint(Graphics & g)238 void TabbedComponent::paint (Graphics& g)
239 {
240 g.fillAll (findColour (backgroundColourId));
241
242 auto content = getLocalBounds();
243 BorderSize<int> outline (outlineThickness);
244 TabbedComponentHelpers::getTabArea (content, outline, getOrientation(), tabDepth);
245
246 g.reduceClipRegion (content);
247 g.fillAll (tabs->getTabBackgroundColour (getCurrentTabIndex()));
248
249 if (outlineThickness > 0)
250 {
251 RectangleList<int> rl (content);
252 rl.subtract (outline.subtractedFrom (content));
253
254 g.reduceClipRegion (rl);
255 g.fillAll (findColour (outlineColourId));
256 }
257 }
258
resized()259 void TabbedComponent::resized()
260 {
261 auto content = getLocalBounds();
262 BorderSize<int> outline (outlineThickness);
263
264 tabs->setBounds (TabbedComponentHelpers::getTabArea (content, outline, getOrientation(), tabDepth));
265 content = BorderSize<int> (edgeIndent).subtractedFrom (outline.subtractedFrom (content));
266
267 for (auto& c : contentComponents)
268 if (auto comp = c.get())
269 comp->setBounds (content);
270 }
271
lookAndFeelChanged()272 void TabbedComponent::lookAndFeelChanged()
273 {
274 for (auto& c : contentComponents)
275 if (auto comp = c.get())
276 comp->lookAndFeelChanged();
277 }
278
changeCallback(int newCurrentTabIndex,const String & newTabName)279 void TabbedComponent::changeCallback (int newCurrentTabIndex, const String& newTabName)
280 {
281 auto* newPanelComp = getTabContentComponent (getCurrentTabIndex());
282
283 if (newPanelComp != panelComponent)
284 {
285 if (panelComponent != nullptr)
286 {
287 panelComponent->setVisible (false);
288 removeChildComponent (panelComponent);
289 }
290
291 panelComponent = newPanelComp;
292
293 if (panelComponent != nullptr)
294 {
295 // do these ops as two stages instead of addAndMakeVisible() so that the
296 // component has always got a parent when it gets the visibilityChanged() callback
297 addChildComponent (panelComponent);
298 panelComponent->sendLookAndFeelChange();
299 panelComponent->setVisible (true);
300 panelComponent->toFront (true);
301 }
302
303 repaint();
304 }
305
306 resized();
307 currentTabChanged (newCurrentTabIndex, newTabName);
308 }
309
currentTabChanged(int,const String &)310 void TabbedComponent::currentTabChanged (int, const String&) {}
popupMenuClickOnTab(int,const String &)311 void TabbedComponent::popupMenuClickOnTab (int, const String&) {}
312
313 } // namespace juce
314