Audio plugin host https://kx.studio/carla
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.

juce_TabbedButtonBar.h 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2020 - Raw Material Software Limited
  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 6 End-User License
  8. Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
  9. End User License Agreement: www.juce.com/juce-6-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. namespace juce
  19. {
  20. class TabbedButtonBar;
  21. //==============================================================================
  22. /** In a TabbedButtonBar, this component is used for each of the buttons.
  23. If you want to create a TabbedButtonBar with custom tab components, derive
  24. your component from this class, and override the TabbedButtonBar::createTabButton()
  25. method to create it instead of the default one.
  26. @see TabbedButtonBar
  27. @tags{GUI}
  28. */
  29. class JUCE_API TabBarButton : public Button
  30. {
  31. public:
  32. //==============================================================================
  33. /** Creates the tab button. */
  34. TabBarButton (const String& name, TabbedButtonBar& ownerBar);
  35. /** Destructor. */
  36. ~TabBarButton() override;
  37. /** Returns the bar that contains this button. */
  38. TabbedButtonBar& getTabbedButtonBar() const { return owner; }
  39. //==============================================================================
  40. /** When adding an extra component to the tab, this indicates which side of
  41. the text it should be placed on. */
  42. enum ExtraComponentPlacement
  43. {
  44. beforeText,
  45. afterText
  46. };
  47. /** Sets an extra component that will be shown in the tab.
  48. This optional component will be positioned inside the tab, either to the left or right
  49. of the text. You could use this to implement things like a close button or a graphical
  50. status indicator. If a non-null component is passed-in, the TabbedButtonBar will take
  51. ownership of it and delete it when required.
  52. */
  53. void setExtraComponent (Component* extraTabComponent,
  54. ExtraComponentPlacement extraComponentPlacement);
  55. /** Returns the custom component, if there is one. */
  56. Component* getExtraComponent() const noexcept { return extraComponent.get(); }
  57. /** Returns the placement of the custom component, if there is one. */
  58. ExtraComponentPlacement getExtraComponentPlacement() const noexcept { return extraCompPlacement; }
  59. /** Returns an area of the component that's safe to draw in.
  60. This deals with the orientation of the tabs, which affects which side is
  61. touching the tabbed box's content component.
  62. */
  63. Rectangle<int> getActiveArea() const;
  64. /** Returns the area of the component that should contain its text. */
  65. Rectangle<int> getTextArea() const;
  66. /** Returns this tab's index in its tab bar. */
  67. int getIndex() const;
  68. /** Returns the colour of the tab. */
  69. Colour getTabBackgroundColour() const;
  70. /** Returns true if this is the frontmost (selected) tab. */
  71. bool isFrontTab() const;
  72. //==============================================================================
  73. /** Chooses the best length for the tab, given the specified depth.
  74. If the tab is horizontal, this should return its width, and the depth
  75. specifies its height. If it's vertical, it should return the height, and
  76. the depth is actually its width.
  77. */
  78. virtual int getBestTabLength (int depth);
  79. //==============================================================================
  80. /** @internal */
  81. void paintButton (Graphics&, bool, bool) override;
  82. /** @internal */
  83. void clicked (const ModifierKeys&) override;
  84. /** @internal */
  85. bool hitTest (int x, int y) override;
  86. /** @internal */
  87. void resized() override;
  88. /** @internal */
  89. void childBoundsChanged (Component*) override;
  90. protected:
  91. friend class TabbedButtonBar;
  92. TabbedButtonBar& owner;
  93. int overlapPixels = 0;
  94. std::unique_ptr<Component> extraComponent;
  95. ExtraComponentPlacement extraCompPlacement = afterText;
  96. private:
  97. using Button::clicked;
  98. void calcAreas (Rectangle<int>&, Rectangle<int>&) const;
  99. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TabBarButton)
  100. };
  101. //==============================================================================
  102. /**
  103. A vertical or horizontal bar containing tabs that you can select.
  104. You can use one of these to generate things like a dialog box that has
  105. tabbed pages you can flip between. Attach a ChangeListener to the
  106. button bar to be told when the user changes the page.
  107. An easier method than doing this is to use a TabbedComponent, which
  108. contains its own TabbedButtonBar and which takes care of the layout
  109. and other housekeeping.
  110. @see TabbedComponent
  111. @tags{GUI}
  112. */
  113. class JUCE_API TabbedButtonBar : public Component,
  114. public ChangeBroadcaster
  115. {
  116. public:
  117. //==============================================================================
  118. /** The placement of the tab-bar
  119. @see setOrientation, getOrientation
  120. */
  121. enum Orientation
  122. {
  123. TabsAtTop,
  124. TabsAtBottom,
  125. TabsAtLeft,
  126. TabsAtRight
  127. };
  128. //==============================================================================
  129. /** Creates a TabbedButtonBar with a given orientation.
  130. You can change the orientation later if you need to.
  131. */
  132. TabbedButtonBar (Orientation orientation);
  133. /** Destructor. */
  134. ~TabbedButtonBar() override;
  135. //==============================================================================
  136. /** Changes the bar's orientation.
  137. This won't change the bar's actual size - you'll need to do that yourself,
  138. but this determines which direction the tabs go in, and which side they're
  139. stuck to.
  140. */
  141. void setOrientation (Orientation orientation);
  142. /** Returns the bar's current orientation.
  143. @see setOrientation
  144. */
  145. Orientation getOrientation() const noexcept { return orientation; }
  146. /** Returns true if the orientation is TabsAtLeft or TabsAtRight. */
  147. bool isVertical() const noexcept { return orientation == TabsAtLeft || orientation == TabsAtRight; }
  148. /** Returns the thickness of the bar, which may be its width or height, depending on the orientation. */
  149. int getThickness() const noexcept { return isVertical() ? getWidth() : getHeight(); }
  150. /** Changes the minimum scale factor to which the tabs can be compressed when trying to
  151. fit a lot of tabs on-screen.
  152. */
  153. void setMinimumTabScaleFactor (double newMinimumScale);
  154. //==============================================================================
  155. /** Deletes all the tabs from the bar.
  156. @see addTab
  157. */
  158. void clearTabs();
  159. /** Adds a tab to the bar.
  160. Tabs are added in left-to-right reading order.
  161. If this is the first tab added, it'll also be automatically selected.
  162. */
  163. void addTab (const String& tabName,
  164. Colour tabBackgroundColour,
  165. int insertIndex);
  166. /** Changes the name of one of the tabs. */
  167. void setTabName (int tabIndex, const String& newName);
  168. /** Gets rid of one of the tabs. */
  169. void removeTab (int tabIndex, bool animate = false);
  170. /** Moves a tab to a new index in the list.
  171. Pass -1 as the index to move it to the end of the list.
  172. */
  173. void moveTab (int currentIndex, int newIndex, bool animate = false);
  174. /** Returns the number of tabs in the bar. */
  175. int getNumTabs() const;
  176. /** Returns a list of all the tab names in the bar. */
  177. StringArray getTabNames() const;
  178. /** Changes the currently selected tab.
  179. This will send a change message and cause a synchronous callback to
  180. the currentTabChanged() method. (But if the given tab is already selected,
  181. nothing will be done).
  182. To deselect all the tabs, use an index of -1.
  183. */
  184. void setCurrentTabIndex (int newTabIndex, bool sendChangeMessage = true);
  185. /** Returns the name of the currently selected tab.
  186. This could be an empty string if none are selected.
  187. */
  188. String getCurrentTabName() const;
  189. /** Returns the index of the currently selected tab.
  190. This could return -1 if none are selected.
  191. */
  192. int getCurrentTabIndex() const noexcept { return currentTabIndex; }
  193. /** Returns the button for a specific tab.
  194. The button that is returned may be deleted later by this component, so don't hang
  195. on to the pointer that is returned. A null pointer may be returned if the index is
  196. out of range.
  197. */
  198. TabBarButton* getTabButton (int index) const;
  199. /** Returns the index of a TabBarButton if it belongs to this bar. */
  200. int indexOfTabButton (const TabBarButton* button) const;
  201. /** Returns the final bounds of this button if it is currently being animated. */
  202. Rectangle<int> getTargetBounds (TabBarButton* button) const;
  203. //==============================================================================
  204. /** Callback method to indicate the selected tab has been changed.
  205. @see setCurrentTabIndex
  206. */
  207. virtual void currentTabChanged (int newCurrentTabIndex,
  208. const String& newCurrentTabName);
  209. /** Callback method to indicate that the user has right-clicked on a tab. */
  210. virtual void popupMenuClickOnTab (int tabIndex, const String& tabName);
  211. /** Returns the colour of a tab.
  212. This is the colour that was specified in addTab().
  213. */
  214. Colour getTabBackgroundColour (int tabIndex);
  215. /** Changes the background colour of a tab.
  216. @see addTab, getTabBackgroundColour
  217. */
  218. void setTabBackgroundColour (int tabIndex, Colour newColour);
  219. //==============================================================================
  220. /** A set of colour IDs to use to change the colour of various aspects of the component.
  221. These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
  222. methods.
  223. @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
  224. */
  225. enum ColourIds
  226. {
  227. tabOutlineColourId = 0x1005812, /**< The colour to use to draw an outline around the tabs. */
  228. tabTextColourId = 0x1005813, /**< The colour to use to draw the tab names. If this isn't specified,
  229. the look and feel will choose an appropriate colour. */
  230. frontOutlineColourId = 0x1005814, /**< The colour to use to draw an outline around the currently-selected tab. */
  231. frontTextColourId = 0x1005815, /**< The colour to use to draw the currently-selected tab name. If
  232. this isn't specified, the look and feel will choose an appropriate
  233. colour. */
  234. };
  235. //==============================================================================
  236. /** This abstract base class is implemented by LookAndFeel classes to provide
  237. window drawing functionality.
  238. */
  239. struct JUCE_API LookAndFeelMethods
  240. {
  241. virtual ~LookAndFeelMethods() = default;
  242. virtual int getTabButtonSpaceAroundImage() = 0;
  243. virtual int getTabButtonOverlap (int tabDepth) = 0;
  244. virtual int getTabButtonBestWidth (TabBarButton&, int tabDepth) = 0;
  245. virtual Rectangle<int> getTabButtonExtraComponentBounds (const TabBarButton&, Rectangle<int>& textArea, Component& extraComp) = 0;
  246. virtual void drawTabButton (TabBarButton&, Graphics&, bool isMouseOver, bool isMouseDown) = 0;
  247. virtual Font getTabButtonFont (TabBarButton&, float height) = 0;
  248. virtual void drawTabButtonText (TabBarButton&, Graphics&, bool isMouseOver, bool isMouseDown) = 0;
  249. virtual void drawTabbedButtonBarBackground (TabbedButtonBar&, Graphics&) = 0;
  250. virtual void drawTabAreaBehindFrontButton (TabbedButtonBar&, Graphics&, int w, int h) = 0;
  251. virtual void createTabButtonShape (TabBarButton&, Path& path, bool isMouseOver, bool isMouseDown) = 0;
  252. virtual void fillTabButtonShape (TabBarButton&, Graphics&, const Path& path, bool isMouseOver, bool isMouseDown) = 0;
  253. virtual Button* createTabBarExtrasButton() = 0;
  254. };
  255. //==============================================================================
  256. /** @internal */
  257. void paint (Graphics&) override;
  258. /** @internal */
  259. void resized() override;
  260. /** @internal */
  261. void lookAndFeelChanged() override;
  262. protected:
  263. //==============================================================================
  264. /** This creates one of the tabs.
  265. If you need to use custom tab components, you can override this method and
  266. return your own class instead of the default.
  267. */
  268. virtual TabBarButton* createTabButton (const String& tabName, int tabIndex);
  269. private:
  270. struct TabInfo
  271. {
  272. std::unique_ptr<TabBarButton> button;
  273. String name;
  274. Colour colour;
  275. };
  276. OwnedArray<TabInfo> tabs;
  277. Orientation orientation;
  278. double minimumScale = 0.7;
  279. int currentTabIndex = -1;
  280. class BehindFrontTabComp;
  281. std::unique_ptr<BehindFrontTabComp> behindFrontTab;
  282. std::unique_ptr<Button> extraTabsButton;
  283. void showExtraItemsMenu();
  284. void updateTabPositions (bool animate);
  285. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TabbedButtonBar)
  286. };
  287. } // namespace juce