123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373 |
- /*
- ==============================================================================
- This file is part of the JUCE library.
- Copyright (c) 2017 - ROLI Ltd.
- JUCE is an open source library subject to commercial or open-source
- licensing.
- By using JUCE, you agree to the terms of both the JUCE 5 End-User License
- Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
- 27th April 2017).
- End User License Agreement: www.juce.com/juce-5-licence
- Privacy Policy: www.juce.com/juce-5-privacy-policy
- Or: You may also use this code under the terms of the GPL v3 (see
- www.gnu.org/licenses).
- JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
- EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
- DISCLAIMED.
- ==============================================================================
- */
- namespace juce
- {
- class TabbedButtonBar;
- //==============================================================================
- /** In a TabbedButtonBar, this component is used for each of the buttons.
- If you want to create a TabbedButtonBar with custom tab components, derive
- your component from this class, and override the TabbedButtonBar::createTabButton()
- method to create it instead of the default one.
- @see TabbedButtonBar
- @tags{GUI}
- */
- class JUCE_API TabBarButton : public Button
- {
- public:
- //==============================================================================
- /** Creates the tab button. */
- TabBarButton (const String& name, TabbedButtonBar& ownerBar);
- /** Destructor. */
- ~TabBarButton() override;
- /** Returns the bar that contains this button. */
- TabbedButtonBar& getTabbedButtonBar() const { return owner; }
- //==============================================================================
- /** When adding an extra component to the tab, this indicates which side of
- the text it should be placed on. */
- enum ExtraComponentPlacement
- {
- beforeText,
- afterText
- };
- /** Sets an extra component that will be shown in the tab.
- This optional component will be positioned inside the tab, either to the left or right
- of the text. You could use this to implement things like a close button or a graphical
- status indicator. If a non-null component is passed-in, the TabbedButtonBar will take
- ownership of it and delete it when required.
- */
- void setExtraComponent (Component* extraTabComponent,
- ExtraComponentPlacement extraComponentPlacement);
- /** Returns the custom component, if there is one. */
- Component* getExtraComponent() const noexcept { return extraComponent.get(); }
- /** Returns the placement of the custom component, if there is one. */
- ExtraComponentPlacement getExtraComponentPlacement() const noexcept { return extraCompPlacement; }
- /** Returns an area of the component that's safe to draw in.
- This deals with the orientation of the tabs, which affects which side is
- touching the tabbed box's content component.
- */
- Rectangle<int> getActiveArea() const;
- /** Returns the area of the component that should contain its text. */
- Rectangle<int> getTextArea() const;
- /** Returns this tab's index in its tab bar. */
- int getIndex() const;
- /** Returns the colour of the tab. */
- Colour getTabBackgroundColour() const;
- /** Returns true if this is the frontmost (selected) tab. */
- bool isFrontTab() const;
- //==============================================================================
- /** Chooses the best length for the tab, given the specified depth.
- If the tab is horizontal, this should return its width, and the depth
- specifies its height. If it's vertical, it should return the height, and
- the depth is actually its width.
- */
- virtual int getBestTabLength (int depth);
- //==============================================================================
- /** @internal */
- void paintButton (Graphics&, bool, bool) override;
- /** @internal */
- void clicked (const ModifierKeys&) override;
- /** @internal */
- bool hitTest (int x, int y) override;
- /** @internal */
- void resized() override;
- /** @internal */
- void childBoundsChanged (Component*) override;
- protected:
- friend class TabbedButtonBar;
- TabbedButtonBar& owner;
- int overlapPixels = 0;
- std::unique_ptr<Component> extraComponent;
- ExtraComponentPlacement extraCompPlacement = afterText;
- private:
- using Button::clicked;
- void calcAreas (Rectangle<int>&, Rectangle<int>&) const;
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TabBarButton)
- };
- //==============================================================================
- /**
- A vertical or horizontal bar containing tabs that you can select.
- You can use one of these to generate things like a dialog box that has
- tabbed pages you can flip between. Attach a ChangeListener to the
- button bar to be told when the user changes the page.
- An easier method than doing this is to use a TabbedComponent, which
- contains its own TabbedButtonBar and which takes care of the layout
- and other housekeeping.
- @see TabbedComponent
- @tags{GUI}
- */
- class JUCE_API TabbedButtonBar : public Component,
- public ChangeBroadcaster
- {
- public:
- //==============================================================================
- /** The placement of the tab-bar
- @see setOrientation, getOrientation
- */
- enum Orientation
- {
- TabsAtTop,
- TabsAtBottom,
- TabsAtLeft,
- TabsAtRight
- };
- //==============================================================================
- /** Creates a TabbedButtonBar with a given orientation.
- You can change the orientation later if you need to.
- */
- TabbedButtonBar (Orientation orientation);
- /** Destructor. */
- ~TabbedButtonBar() override;
- //==============================================================================
- /** Changes the bar's orientation.
- This won't change the bar's actual size - you'll need to do that yourself,
- but this determines which direction the tabs go in, and which side they're
- stuck to.
- */
- void setOrientation (Orientation orientation);
- /** Returns the bar's current orientation.
- @see setOrientation
- */
- Orientation getOrientation() const noexcept { return orientation; }
- /** Returns true if the orientation is TabsAtLeft or TabsAtRight. */
- bool isVertical() const noexcept { return orientation == TabsAtLeft || orientation == TabsAtRight; }
- /** Returns the thickness of the bar, which may be its width or height, depending on the orientation. */
- int getThickness() const noexcept { return isVertical() ? getWidth() : getHeight(); }
- /** Changes the minimum scale factor to which the tabs can be compressed when trying to
- fit a lot of tabs on-screen.
- */
- void setMinimumTabScaleFactor (double newMinimumScale);
- //==============================================================================
- /** Deletes all the tabs from the bar.
- @see addTab
- */
- void clearTabs();
- /** Adds a tab to the bar.
- Tabs are added in left-to-right reading order.
- If this is the first tab added, it'll also be automatically selected.
- */
- void addTab (const String& tabName,
- Colour tabBackgroundColour,
- int insertIndex);
- /** Changes the name of one of the tabs. */
- void setTabName (int tabIndex, const String& newName);
- /** Gets rid of one of the tabs. */
- void removeTab (int tabIndex, bool animate = false);
- /** Moves a tab to a new index in the list.
- Pass -1 as the index to move it to the end of the list.
- */
- void moveTab (int currentIndex, int newIndex, bool animate = false);
- /** Returns the number of tabs in the bar. */
- int getNumTabs() const;
- /** Returns a list of all the tab names in the bar. */
- StringArray getTabNames() const;
- /** Changes the currently selected tab.
- This will send a change message and cause a synchronous callback to
- the currentTabChanged() method. (But if the given tab is already selected,
- nothing will be done).
- To deselect all the tabs, use an index of -1.
- */
- void setCurrentTabIndex (int newTabIndex, bool sendChangeMessage = true);
- /** Returns the name of the currently selected tab.
- This could be an empty string if none are selected.
- */
- String getCurrentTabName() const;
- /** Returns the index of the currently selected tab.
- This could return -1 if none are selected.
- */
- int getCurrentTabIndex() const noexcept { return currentTabIndex; }
- /** Returns the button for a specific tab.
- The button that is returned may be deleted later by this component, so don't hang
- on to the pointer that is returned. A null pointer may be returned if the index is
- out of range.
- */
- TabBarButton* getTabButton (int index) const;
- /** Returns the index of a TabBarButton if it belongs to this bar. */
- int indexOfTabButton (const TabBarButton* button) const;
- /** Returns the final bounds of this button if it is currently being animated. */
- Rectangle<int> getTargetBounds (TabBarButton* button) const;
- //==============================================================================
- /** Callback method to indicate the selected tab has been changed.
- @see setCurrentTabIndex
- */
- virtual void currentTabChanged (int newCurrentTabIndex,
- const String& newCurrentTabName);
- /** Callback method to indicate that the user has right-clicked on a tab. */
- virtual void popupMenuClickOnTab (int tabIndex, const String& tabName);
- /** Returns the colour of a tab.
- This is the colour that was specified in addTab().
- */
- Colour getTabBackgroundColour (int tabIndex);
- /** Changes the background colour of a tab.
- @see addTab, getTabBackgroundColour
- */
- void setTabBackgroundColour (int tabIndex, Colour newColour);
- //==============================================================================
- /** A set of colour IDs to use to change the colour of various aspects of the component.
- These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
- methods.
- @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
- */
- enum ColourIds
- {
- tabOutlineColourId = 0x1005812, /**< The colour to use to draw an outline around the tabs. */
- tabTextColourId = 0x1005813, /**< The colour to use to draw the tab names. If this isn't specified,
- the look and feel will choose an appropriate colour. */
- frontOutlineColourId = 0x1005814, /**< The colour to use to draw an outline around the currently-selected tab. */
- frontTextColourId = 0x1005815, /**< The colour to use to draw the currently-selected tab name. If
- this isn't specified, the look and feel will choose an appropriate
- colour. */
- };
- //==============================================================================
- /** This abstract base class is implemented by LookAndFeel classes to provide
- window drawing functionality.
- */
- struct JUCE_API LookAndFeelMethods
- {
- virtual ~LookAndFeelMethods() = default;
- virtual int getTabButtonSpaceAroundImage() = 0;
- virtual int getTabButtonOverlap (int tabDepth) = 0;
- virtual int getTabButtonBestWidth (TabBarButton&, int tabDepth) = 0;
- virtual Rectangle<int> getTabButtonExtraComponentBounds (const TabBarButton&, Rectangle<int>& textArea, Component& extraComp) = 0;
- virtual void drawTabButton (TabBarButton&, Graphics&, bool isMouseOver, bool isMouseDown) = 0;
- virtual Font getTabButtonFont (TabBarButton&, float height) = 0;
- virtual void drawTabButtonText (TabBarButton&, Graphics&, bool isMouseOver, bool isMouseDown) = 0;
- virtual void drawTabbedButtonBarBackground (TabbedButtonBar&, Graphics&) = 0;
- virtual void drawTabAreaBehindFrontButton (TabbedButtonBar&, Graphics&, int w, int h) = 0;
- virtual void createTabButtonShape (TabBarButton&, Path& path, bool isMouseOver, bool isMouseDown) = 0;
- virtual void fillTabButtonShape (TabBarButton&, Graphics&, const Path& path, bool isMouseOver, bool isMouseDown) = 0;
- virtual Button* createTabBarExtrasButton() = 0;
- };
- //==============================================================================
- /** @internal */
- void paint (Graphics&) override;
- /** @internal */
- void resized() override;
- /** @internal */
- void lookAndFeelChanged() override;
- protected:
- //==============================================================================
- /** This creates one of the tabs.
- If you need to use custom tab components, you can override this method and
- return your own class instead of the default.
- */
- virtual TabBarButton* createTabButton (const String& tabName, int tabIndex);
- private:
- struct TabInfo
- {
- std::unique_ptr<TabBarButton> button;
- String name;
- Colour colour;
- };
- OwnedArray<TabInfo> tabs;
- Orientation orientation;
- double minimumScale = 0.7;
- int currentTabIndex = -1;
- class BehindFrontTabComp;
- std::unique_ptr<BehindFrontTabComp> behindFrontTab;
- std::unique_ptr<Button> extraTabsButton;
- void showExtraItemsMenu();
- void updateTabPositions (bool animate);
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TabbedButtonBar)
- };
- } // namespace juce
|