JUCE-7.0.12-0-g4f43011b96 JUCE-7.0.12-0-g4f43011b96
JUCE — C++ application framework with suport for VST, VST3, LV2 audio plug-ins

« « « Anklang Documentation
Loading...
Searching...
No Matches
juce_TabbedButtonBar.cpp
Go to the documentation of this file.
1 /*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - 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 7 End-User License
11 Agreement and JUCE Privacy Policy.
12
13 End User License Agreement: www.juce.com/juce-7-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
26namespace juce
27{
28
30 : Button (name), owner (bar)
31{
33}
34
36
37int TabBarButton::getIndex() const { return owner.indexOfTabButton (this); }
39bool TabBarButton::isFrontTab() const { return getToggleState(); }
40
45
46void TabBarButton::clicked (const ModifierKeys& mods)
47{
48 if (mods.isPopupMenu())
50 else
52}
53
55{
56 auto area = getActiveArea();
57
58 if (owner.isVertical())
59 {
61 && my >= area.getY() + overlapPixels && my < area.getBottom() - overlapPixels)
62 return true;
63 }
64 else
65 {
67 && mx >= area.getX() + overlapPixels && mx < area.getRight() - overlapPixels)
68 return true;
69 }
70
71 Path p;
72 getLookAndFeel().createTabButtonShape (*this, p, false, false);
73
74 return p.contains ((float) (mx - area.getX()),
75 (float) (my - area.getY()));
76}
77
78int TabBarButton::getBestTabLength (const int depth)
79{
80 return getLookAndFeel().getTabButtonBestWidth (*this, depth);
81}
82
83void TabBarButton::calcAreas (Rectangle<int>& extraComp, Rectangle<int>& textArea) const
84{
85 auto& lf = getLookAndFeel();
86 textArea = getActiveArea();
87
88 auto depth = owner.isVertical() ? textArea.getWidth() : textArea.getHeight();
89 auto overlap = lf.getTabButtonOverlap (depth);
90
91 if (overlap > 0)
92 {
93 if (owner.isVertical())
94 textArea.reduce (0, overlap);
95 else
96 textArea.reduce (overlap, 0);
97 }
98
99 if (extraComponent != nullptr)
100 {
101 extraComp = lf.getTabButtonExtraComponentBounds (*this, textArea, *extraComponent);
102
103 auto orientation = owner.getOrientation();
104
105 if (orientation == TabbedButtonBar::TabsAtLeft || orientation == TabbedButtonBar::TabsAtRight)
106 {
107 if (extraComp.getCentreY() > textArea.getCentreY())
108 textArea.setBottom (jmin (textArea.getBottom(), extraComp.getY()));
109 else
110 textArea.setTop (jmax (textArea.getY(), extraComp.getBottom()));
111 }
112 else
113 {
114 if (extraComp.getCentreX() > textArea.getCentreX())
115 textArea.setRight (jmin (textArea.getRight(), extraComp.getX()));
116 else
117 textArea.setLeft (jmax (textArea.getX(), extraComp.getRight()));
118 }
119 }
120}
121
123{
124 Rectangle<int> extraComp, textArea;
125 calcAreas (extraComp, textArea);
126 return textArea;
127}
128
130{
131 auto r = getLocalBounds();
132 auto spaceAroundImage = getLookAndFeel().getTabButtonSpaceAroundImage();
133 auto orientation = owner.getOrientation();
134
135 if (orientation != TabbedButtonBar::TabsAtLeft) r.removeFromRight (spaceAroundImage);
136 if (orientation != TabbedButtonBar::TabsAtRight) r.removeFromLeft (spaceAroundImage);
137 if (orientation != TabbedButtonBar::TabsAtBottom) r.removeFromTop (spaceAroundImage);
138 if (orientation != TabbedButtonBar::TabsAtTop) r.removeFromBottom (spaceAroundImage);
139
140 return r;
141}
142
144{
145 jassert (extraCompPlacement == beforeText || extraCompPlacement == afterText);
146 extraCompPlacement = placement;
147 extraComponent.reset (comp);
148 addAndMakeVisible (extraComponent.get());
149 resized();
150}
151
153{
154 if (c == extraComponent.get())
155 {
156 owner.resized();
157 resized();
158 }
159}
160
162{
163 if (extraComponent != nullptr)
164 {
165 Rectangle<int> extraComp, textArea;
166 calcAreas (extraComp, textArea);
167
168 if (! extraComp.isEmpty())
169 extraComponent->setBounds (extraComp);
170 }
171}
172
173//==============================================================================
175{
176public:
178 {
179 setInterceptsMouseClicks (false, false);
180 }
181
182 void paint (Graphics& g) override
183 {
184 getLookAndFeel().drawTabAreaBehindFrontButton (owner, g, getWidth(), getHeight());
185 }
186
187 void enablementChanged() override
188 {
189 repaint();
190 }
191
192 TabbedButtonBar& owner;
193
195};
196
197
198//==============================================================================
200 : orientation (orientationToUse)
201{
202 setInterceptsMouseClicks (false, true);
203 behindFrontTab.reset (new BehindFrontTabComp (*this));
204 addAndMakeVisible (behindFrontTab.get());
206}
207
209{
210 tabs.clear();
211 extraTabsButton.reset();
212}
213
214//==============================================================================
216{
217 orientation = newOrientation;
218
219 for (auto* child : getChildren())
220 child->resized();
221
222 resized();
223}
224
225TabBarButton* TabbedButtonBar::createTabButton (const String& name, const int /*index*/)
226{
227 return new TabBarButton (name, *this);
228}
229
231{
232 minimumScale = newMinimumScale;
233 resized();
234}
235
236//==============================================================================
238{
239 tabs.clear();
240 extraTabsButton.reset();
242}
243
246 int insertIndex)
247{
248 jassert (tabName.isNotEmpty()); // you have to give them all a name..
249
250 if (tabName.isNotEmpty())
251 {
252 if (! isPositiveAndBelow (insertIndex, tabs.size()))
253 insertIndex = tabs.size();
254
255 auto* currentTab = tabs[currentTabIndex];
256
257 auto* newTab = new TabInfo();
258 newTab->name = tabName;
259 newTab->colour = tabBackgroundColour;
260 newTab->button.reset (createTabButton (tabName, insertIndex));
261 jassert (newTab->button != nullptr);
262
263 tabs.insert (insertIndex, newTab);
264 currentTabIndex = tabs.indexOf (currentTab);
265 addAndMakeVisible (newTab->button.get(), insertIndex);
266
267 resized();
268
269 if (currentTabIndex < 0)
271 }
272}
273
275{
276 if (auto* tab = tabs[tabIndex])
277 {
278 if (tab->name != newName)
279 {
280 tab->name = newName;
281 tab->button->setButtonText (newName);
282 resized();
283 }
284 }
285}
286
288{
290 {
291 auto oldSelectedIndex = currentTabIndex;
292
293 if (indexToRemove == currentTabIndex)
294 oldSelectedIndex = -1;
297
298 tabs.remove (indexToRemove);
299
301 updateTabPositions (animate);
302 }
303}
304
305void TabbedButtonBar::moveTab (const int currentIndex, const int newIndex, const bool animate)
306{
307 auto* currentTab = tabs[currentTabIndex];
308 tabs.move (currentIndex, newIndex);
309 currentTabIndex = tabs.indexOf (currentTab);
310 updateTabPositions (animate);
311}
312
314{
315 return tabs.size();
316}
317
319{
320 if (auto* tab = tabs [currentTabIndex])
321 return tab->name;
322
323 return {};
324}
325
327{
328 StringArray names;
329
330 for (auto* t : tabs)
331 names.add (t->name);
332
333 return names;
334}
335
337{
338 if (currentTabIndex != newIndex)
339 {
340 if (! isPositiveAndBelow (newIndex, tabs.size()))
341 newIndex = -1;
342
343 currentTabIndex = newIndex;
344
345 for (int i = 0; i < tabs.size(); ++i)
346 tabs.getUnchecked (i)->button->setToggleState (i == newIndex, dontSendNotification);
347
348 resized();
349
352
354 }
355}
356
358{
359 if (auto* tab = tabs[index])
360 return static_cast<TabBarButton*> (tab->button.get());
361
362 return nullptr;
363}
364
366{
367 for (int i = tabs.size(); --i >= 0;)
368 if (tabs.getUnchecked (i)->button.get() == button)
369 return i;
370
371 return -1;
372}
373
375{
376 if (button == nullptr || indexOfTabButton (button) == -1)
377 return {};
378
379 auto& animator = Desktop::getInstance().getAnimator();
380
381 return animator.isAnimating (button) ? animator.getComponentDestination (button)
382 : button->getBounds();
383}
384
386{
387 extraTabsButton.reset();
388 resized();
389}
390
392{
393 getLookAndFeel().drawTabbedButtonBarBackground (*this, g);
394}
395
397{
398 updateTabPositions (false);
399}
400
401//==============================================================================
402void TabbedButtonBar::updateTabPositions (bool animate)
403{
404 auto& lf = getLookAndFeel();
405
406 auto depth = getWidth();
407 auto length = getHeight();
408
409 if (! isVertical())
410 std::swap (depth, length);
411
412 auto overlap = lf.getTabButtonOverlap (depth) + lf.getTabButtonSpaceAroundImage() * 2;
413
414 auto totalLength = jmax (0, overlap);
415 auto numVisibleButtons = tabs.size();
416
417 for (int i = 0; i < tabs.size(); ++i)
418 {
419 auto* tb = tabs.getUnchecked (i)->button.get();
420
421 totalLength += tb->getBestTabLength (depth) - overlap;
422 tb->overlapPixels = jmax (0, overlap / 2);
423 }
424
425 double scale = 1.0;
426
427 if (totalLength > length)
428 scale = jmax (minimumScale, length / (double) totalLength);
429
430 const bool isTooBig = (int) (totalLength * scale) > length;
431 int tabsButtonPos = 0;
432
433 if (isTooBig)
434 {
435 if (extraTabsButton == nullptr)
436 {
437 extraTabsButton.reset (lf.createTabBarExtrasButton());
438 addAndMakeVisible (extraTabsButton.get());
439 extraTabsButton->setAlwaysOnTop (true);
440 extraTabsButton->setTriggeredOnMouseDown (true);
441 extraTabsButton->onClick = [this] { showExtraItemsMenu(); };
442 }
443
444 auto buttonSize = jmin (proportionOfWidth (0.7f), proportionOfHeight (0.7f));
445 extraTabsButton->setSize (buttonSize, buttonSize);
446
447 if (isVertical())
448 {
449 tabsButtonPos = getHeight() - buttonSize / 2 - 1;
450 extraTabsButton->setCentrePosition (getWidth() / 2, tabsButtonPos);
451 }
452 else
453 {
454 tabsButtonPos = getWidth() - buttonSize / 2 - 1;
455 extraTabsButton->setCentrePosition (tabsButtonPos, getHeight() / 2);
456 }
457
458 totalLength = 0;
459
460 for (int i = 0; i < tabs.size(); ++i)
461 {
462 auto* tb = tabs.getUnchecked (i)->button.get();
463 auto newLength = totalLength + tb->getBestTabLength (depth);
464
465 if (i > 0 && newLength * minimumScale > tabsButtonPos)
466 {
467 totalLength += overlap;
468 break;
469 }
470
471 numVisibleButtons = i + 1;
472 totalLength = newLength - overlap;
473 }
474
475 scale = jmax (minimumScale, tabsButtonPos / (double) totalLength);
476 }
477 else
478 {
479 extraTabsButton.reset();
480 }
481
482 int pos = 0;
483
484 TabBarButton* frontTab = nullptr;
485 auto& animator = Desktop::getInstance().getAnimator();
486
487 for (int i = 0; i < tabs.size(); ++i)
488 {
489 if (auto* tb = getTabButton (i))
490 {
491 auto bestLength = roundToInt (scale * tb->getBestTabLength (depth));
492
493 if (i < numVisibleButtons)
494 {
495 auto newBounds = isVertical() ? Rectangle<int> (0, pos, getWidth(), bestLength)
496 : Rectangle<int> (pos, 0, bestLength, getHeight());
497
498 if (animate)
499 {
500 animator.animateComponent (tb, newBounds, 1.0f, 200, false, 3.0, 0.0);
501 }
502 else
503 {
504 animator.cancelAnimation (tb, false);
505 tb->setBounds (newBounds);
506 }
507
508 tb->toBack();
509
510 if (i == currentTabIndex)
511 frontTab = tb;
512
513 tb->setVisible (true);
514 }
515 else
516 {
517 tb->setVisible (false);
518 }
519
520 pos += bestLength - overlap;
521 }
522 }
523
524 behindFrontTab->setBounds (getLocalBounds());
525
526 if (frontTab != nullptr)
527 {
528 frontTab->toFront (false);
529 behindFrontTab->toBehind (frontTab);
530 }
531}
532
533//==============================================================================
535{
536 if (auto* tab = tabs[tabIndex])
537 return tab->colour;
538
539 return Colours::transparentBlack;
540}
541
543{
544 if (auto* tab = tabs [tabIndex])
545 {
546 if (tab->colour != newColour)
547 {
548 tab->colour = newColour;
549 repaint();
550 }
551 }
552}
553
554void TabbedButtonBar::showExtraItemsMenu()
555{
556 PopupMenu m;
557
558 for (int i = 0; i < tabs.size(); ++i)
559 {
560 auto* tab = tabs.getUnchecked (i);
561
562 if (! tab->button->isVisible())
563 m.addItem (PopupMenu::Item (tab->name)
564 .setTicked (i == currentTabIndex)
565 .setAction ([this, i] { setCurrentTabIndex (i); }));
566 }
567
568 m.showMenuAsync (PopupMenu::Options()
569 .withDeletionCheck (*this)
570 .withTargetComponent (extraTabsButton.get()));
571}
572
573//==============================================================================
576
577//==============================================================================
579{
580 return std::make_unique<AccessibilityHandler> (*this, AccessibilityRole::group);
581}
582
583} // namespace juce
A base class for buttons.
Definition juce_Button.h:43
bool getToggleState() const noexcept
Returns true if the button is 'on'.
const String & getButtonText() const
Returns the text displayed in the button.
Definition juce_Button.h:67
void sendChangeMessage()
Causes an asynchronous change message to be sent to all the registered listeners.
Represents a colour, also including a transparency value.
Definition juce_Colour.h:38
bool isAnimating(Component *component) const noexcept
Returns true if the specified component is currently being animated.
The base class for all JUCE user-interface objects.
int proportionOfWidth(float proportion) const noexcept
Returns a proportion of the component's width.
void setInterceptsMouseClicks(bool allowClicksOnThisComponent, bool allowClicksOnChildComponents) noexcept
Changes the default return value for the hitTest() method.
int proportionOfHeight(float proportion) const noexcept
Returns a proportion of the component's height.
void setFocusContainerType(FocusContainerType containerType) noexcept
Sets whether this component is a container for components that can have their focus traversed,...
int getHeight() const noexcept
Returns the component's height in pixels.
void addAndMakeVisible(Component *child, int zOrder=-1)
Adds a child component to this one, and also makes the child visible if it isn't already.
void repaint()
Marks the whole component as needing to be redrawn.
@ keyboardFocusContainer
The component will act as a top-level component within which keyboard focus is passed around.
void setWantsKeyboardFocus(bool wantsFocus) noexcept
Sets a flag to indicate whether this component wants keyboard focus or not.
int getWidth() const noexcept
Returns the component's width in pixels.
LookAndFeel & getLookAndFeel() const noexcept
Finds the appropriate look-and-feel to use for this component.
Rectangle< int > getLocalBounds() const noexcept
Returns the component's bounds, relative to its own origin.
const Array< Component * > & getChildren() const noexcept
Provides access to the underlying array of child components.
ComponentAnimator & getAnimator() noexcept
The Desktop object has a ComponentAnimator instance which can be used for performing your animations.
static Desktop &JUCE_CALLTYPE getInstance()
There's only one desktop object, and this method will return it.
A graphics context, used for drawing a component or image.
Represents the state of the mouse buttons and modifier keys.
bool isPopupMenu() const noexcept
Checks whether the user is trying to launch a pop-up menu.
int size() const noexcept
Returns the number of items currently in the array.
ObjectClass * getUnchecked(int index) const noexcept
Returns a pointer to the object at this index in the array, without checking whether the index is in-...
void remove(int indexToRemove, bool deleteObject=true)
Removes an object from the array.
void clear(bool deleteObjects=true)
Clears the array, optionally deleting the objects inside it first.
int indexOf(const ObjectClass *objectToLookFor) const noexcept
Finds the index of an object which might be in the array.
void move(int currentIndex, int newIndex) noexcept
Moves one of the objects to a different position.
ObjectClass * insert(int indexToInsertAt, ObjectClass *newObject)
Inserts a new object into the array at the given index.
A path is a sequence of lines and curves that may either form a closed shape or be open-ended.
Definition juce_Path.h:65
bool contains(float x, float y, float tolerance=defaultToleranceForTesting) const
Checks whether a point lies within the path.
Creates and displays a popup-menu.
void showMenuAsync(const Options &options)
Runs the menu asynchronously.
void addItem(Item newItem)
Adds an item to the menu.
Manages a rectangle and allows geometric operations to be performed on it.
ValueType getRight() const noexcept
Returns the x coordinate of the rectangle's right-hand-side.
void setLeft(ValueType newLeft) noexcept
Moves the x position, adjusting the width so that the right-hand edge remains in the same place.
ValueType getX() const noexcept
Returns the x coordinate of the rectangle's left-hand-side.
ValueType getCentreX() const noexcept
Returns the x coordinate of the rectangle's centre.
void setRight(ValueType newRight) noexcept
Adjusts the width so that the right-hand edge of the rectangle has this new value.
void setBottom(ValueType newBottom) noexcept
Adjusts the height so that the bottom edge of the rectangle has this new value.
ValueType getCentreY() const noexcept
Returns the y coordinate of the rectangle's centre.
ValueType getBottom() const noexcept
Returns the y coordinate of the rectangle's bottom edge.
ValueType getWidth() const noexcept
Returns the width of the rectangle.
void setTop(ValueType newTop) noexcept
Moves the y position, adjusting the height so that the bottom edge remains in the same place.
ValueType getY() const noexcept
Returns the y coordinate of the rectangle's top edge.
void reduce(ValueType deltaX, ValueType deltaY) noexcept
Shrinks the rectangle by a given amount.
ValueType getHeight() const noexcept
Returns the height of the rectangle.
A special array for holding a list of strings.
void add(String stringToAdd)
Appends a string at the end of the array.
The JUCE String class!
Definition juce_String.h:53
In a TabbedButtonBar, this component is used for each of the buttons.
int getIndex() const
Returns this tab's index in its tab bar.
Rectangle< int > getActiveArea() const
Returns an area of the component that's safe to draw in.
bool hitTest(int x, int y) override
Tests whether a given point is inside the component.
Rectangle< int > getTextArea() const
Returns the area of the component that should contain its text.
TabBarButton(const String &name, TabbedButtonBar &ownerBar)
Creates the tab button.
Colour getTabBackgroundColour() const
Returns the colour of the tab.
ExtraComponentPlacement
When adding an extra component to the tab, this indicates which side of the text it should be placed ...
bool isFrontTab() const
Returns true if this is the frontmost (selected) tab.
void paintButton(Graphics &, bool, bool) override
Subclasses should override this to actually paint the button's contents.
void setExtraComponent(Component *extraTabComponent, ExtraComponentPlacement extraComponentPlacement)
Sets an extra component that will be shown in the tab.
void resized() override
Called when this component's size has been changed.
virtual int getBestTabLength(int depth)
Chooses the best length for the tab, given the specified depth.
~TabBarButton() override
Destructor.
void childBoundsChanged(Component *) override
Called when one of this component's children is moved or resized.
void enablementChanged() override
Callback to indicate that this component has been enabled or disabled.
void paint(Graphics &g) override
Components can override this method to draw their content.
A vertical or horizontal bar containing tabs that you can select.
String getCurrentTabName() const
Returns the name of the currently selected tab.
void setTabName(int tabIndex, const String &newName)
Changes the name of one of the tabs.
std::unique_ptr< AccessibilityHandler > createAccessibilityHandler() override
Override this method to return a custom AccessibilityHandler for this component.
TabBarButton * getTabButton(int index) const
Returns the button for a specific tab.
~TabbedButtonBar() override
Destructor.
TabbedButtonBar(Orientation orientation)
Creates a TabbedButtonBar with a given orientation.
void moveTab(int currentIndex, int newIndex, bool animate=false)
Moves a tab to a new index in the list.
int indexOfTabButton(const TabBarButton *button) const
Returns the index of a TabBarButton if it belongs to this bar.
void setTabBackgroundColour(int tabIndex, Colour newColour)
Changes the background colour of a tab.
Rectangle< int > getTargetBounds(TabBarButton *button) const
Returns the final bounds of this button if it is currently being animated.
void setOrientation(Orientation orientation)
Changes the bar's orientation.
Orientation getOrientation() const noexcept
Returns the bar's current orientation.
Orientation
The placement of the tab-bar.
void removeTab(int tabIndex, bool animate=false)
Gets rid of one of the tabs.
void setCurrentTabIndex(int newTabIndex, bool sendChangeMessage=true)
Changes the currently selected tab.
virtual TabBarButton * createTabButton(const String &tabName, int tabIndex)
This creates one of the tabs.
bool isVertical() const noexcept
Returns true if the orientation is TabsAtLeft or TabsAtRight.
int getNumTabs() const
Returns the number of tabs in the bar.
virtual void currentTabChanged(int newCurrentTabIndex, const String &newCurrentTabName)
Callback method to indicate the selected tab has been changed.
StringArray getTabNames() const
Returns a list of all the tab names in the bar.
virtual void popupMenuClickOnTab(int tabIndex, const String &tabName)
Callback method to indicate that the user has right-clicked on a tab.
void clearTabs()
Deletes all the tabs from the bar.
void resized() override
Called when this component's size has been changed.
void lookAndFeelChanged() override
Called to let the component react to a change in the look-and-feel setting.
void paint(Graphics &) override
Components can override this method to draw their content.
Colour getTabBackgroundColour(int tabIndex)
Returns the colour of a tab.
void setMinimumTabScaleFactor(double newMinimumScale)
Changes the minimum scale factor to which the tabs can be compressed when trying to fit a lot of tabs...
void addTab(const String &tabName, Colour tabBackgroundColour, int insertIndex)
Adds a tab to the bar.
#define jassert(expression)
Platform-independent assertion macro.
#define JUCE_DECLARE_NON_COPYABLE(className)
This is a shorthand macro for deleting a class's copy constructor and copy assignment operator.
typedef int
JUCE Namespace.
constexpr Type jmin(Type a, Type b)
Returns the smaller of two values.
constexpr Type jmax(Type a, Type b)
Returns the larger of two values.
@ dontSendNotification
No notification message should be sent.
Type unalignedPointerCast(void *ptr) noexcept
Casts a pointer to another type via void*, which suppresses the cast-align warning which sometimes ar...
Definition juce_Memory.h:88
bool isPositiveAndBelow(Type1 valueToTest, Type2 upperLimit) noexcept
Returns true if a value is at least zero, and also below a specified upper limit.
int roundToInt(const FloatType value) noexcept
Fast floating-point-to-integer conversion.
Describes a popup menu item.
Item & setTicked(bool shouldBeTicked=true) &noexcept
Sets the isTicked flag (and returns a reference to this item to allow chaining).
Item & setAction(std::function< void()> action) &noexcept
Sets the action property (and returns a reference to this item to allow chaining).
T swap(T... args)