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_Button.h 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549
  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2022 - 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 7 End-User License
  8. Agreement and JUCE Privacy Policy.
  9. End User License Agreement: www.juce.com/juce-7-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. //==============================================================================
  21. /**
  22. A base class for buttons.
  23. This contains all the logic for button behaviours such as enabling/disabling,
  24. responding to shortcut keystrokes, auto-repeating when held down, toggle-buttons
  25. and radio groups, etc.
  26. @see TextButton, DrawableButton, ToggleButton
  27. @tags{GUI}
  28. */
  29. class JUCE_API Button : public Component,
  30. public SettableTooltipClient
  31. {
  32. protected:
  33. //==============================================================================
  34. /** Creates a button.
  35. @param buttonName the text to put in the button (the component's name is also
  36. initially set to this string, but these can be changed later
  37. using the setName() and setButtonText() methods)
  38. */
  39. explicit Button (const String& buttonName);
  40. public:
  41. /** Destructor. */
  42. ~Button() override;
  43. //==============================================================================
  44. /** Changes the button's text.
  45. @see getButtonText
  46. */
  47. void setButtonText (const String& newText);
  48. /** Returns the text displayed in the button.
  49. @see setButtonText
  50. */
  51. const String& getButtonText() const { return text; }
  52. //==============================================================================
  53. /** Returns true if the button is currently being held down.
  54. @see isOver
  55. */
  56. bool isDown() const noexcept;
  57. /** Returns true if the mouse is currently over the button.
  58. This will be also be true if the button is being held down.
  59. @see isDown
  60. */
  61. bool isOver() const noexcept;
  62. //==============================================================================
  63. /** Indicates that the button's on/off state is toggleable.
  64. By default this is false, and will only be true for ToggleButtons, buttons that
  65. are a part of a radio button group, and buttons for which
  66. getClickingTogglesState() == true, however you can use this method to manually
  67. indicate that a button is toggleable.
  68. This will present the button as toggleable to accessibility clients and add an
  69. accessible "toggle" action for the button that invokes setToggleState().
  70. @see ToggleButton, isToggleable, setToggleState, setClickingTogglesState, setRadioGroupId
  71. */
  72. void setToggleable (bool shouldBeToggleable);
  73. /** Returns true if the button's on/off state is toggleable.
  74. @see setToggleable, setClickingTogglesState
  75. */
  76. bool isToggleable() const noexcept { return canBeToggled || clickTogglesState; }
  77. /** A button has an on/off state associated with it, and this changes that.
  78. By default buttons are 'off' and for simple buttons that you click to perform
  79. an action you won't change this. Toggle buttons, however will want to
  80. change their state when turned on or off.
  81. @param shouldBeOn whether to set the button's toggle state to be on or
  82. off. If it's a member of a button group, this will
  83. always try to turn it on, and to turn off any other
  84. buttons in the group
  85. @param notification determines the behaviour if the value changes - this
  86. can invoke a synchronous call to clicked(), but
  87. sendNotificationAsync is not supported
  88. @see getToggleState, setRadioGroupId
  89. */
  90. void setToggleState (bool shouldBeOn, NotificationType notification);
  91. /** Returns true if the button is 'on'.
  92. By default buttons are 'off' and for simple buttons that you click to perform
  93. an action you won't change this. Toggle buttons, however will want to
  94. change their state when turned on or off.
  95. @see setToggleState
  96. */
  97. bool getToggleState() const noexcept { return isOn.getValue(); }
  98. /** Returns the Value object that represents the button's toggle state.
  99. You can use this Value object to connect the button's state to external values or setters,
  100. either by taking a copy of the Value, or by using Value::referTo() to make it point to
  101. your own Value object.
  102. @see getToggleState, Value
  103. */
  104. Value& getToggleStateValue() noexcept { return isOn; }
  105. /** This tells the button to automatically flip the toggle state when
  106. the button is clicked.
  107. If set to true, then before the clicked() callback occurs, the toggle-state
  108. of the button is flipped. This will also cause isToggleable() to return true.
  109. @see isToggleable
  110. */
  111. void setClickingTogglesState (bool shouldAutoToggleOnClick) noexcept;
  112. /** Returns true if this button is set to be an automatic toggle-button.
  113. This returns the last value that was passed to setClickingTogglesState().
  114. */
  115. bool getClickingTogglesState() const noexcept { return clickTogglesState; }
  116. //==============================================================================
  117. /** Enables the button to act as a member of a mutually-exclusive group
  118. of 'radio buttons'.
  119. If the group ID is set to a non-zero number, then this button will
  120. act as part of a group of buttons with the same ID, only one of
  121. which can be 'on' at the same time. Note that when it's part of
  122. a group, clicking a toggle-button that's 'on' won't turn it off.
  123. To find other buttons with the same ID, this button will search through
  124. its sibling components for ToggleButtons, so all the buttons for a
  125. particular group must be placed inside the same parent component.
  126. Set the group ID back to zero if you want it to act as a normal toggle
  127. button again.
  128. The notification argument lets you specify how other buttons should react
  129. to being turned on or off in response to this call.
  130. @see getRadioGroupId
  131. */
  132. void setRadioGroupId (int newGroupId, NotificationType notification = sendNotification);
  133. /** Returns the ID of the group to which this button belongs.
  134. (See setRadioGroupId() for an explanation of this).
  135. */
  136. int getRadioGroupId() const noexcept { return radioGroupId; }
  137. //==============================================================================
  138. /**
  139. Used to receive callbacks when a button is clicked.
  140. @see Button::addListener, Button::removeListener
  141. */
  142. class JUCE_API Listener
  143. {
  144. public:
  145. /** Destructor. */
  146. virtual ~Listener() = default;
  147. /** Called when the button is clicked. */
  148. virtual void buttonClicked (Button*) = 0;
  149. /** Called when the button's state changes. */
  150. virtual void buttonStateChanged (Button*) {}
  151. };
  152. /** Registers a listener to receive events when this button's state changes.
  153. If the listener is already registered, this will not register it again.
  154. @see removeListener
  155. */
  156. void addListener (Listener* newListener);
  157. /** Removes a previously-registered button listener
  158. @see addListener
  159. */
  160. void removeListener (Listener* listener);
  161. //==============================================================================
  162. /** You can assign a lambda to this callback object to have it called when the button is clicked. */
  163. std::function<void()> onClick;
  164. /** You can assign a lambda to this callback object to have it called when the button's state changes. */
  165. std::function<void()> onStateChange;
  166. //==============================================================================
  167. /** Causes the button to act as if it's been clicked.
  168. This will asynchronously make the button draw itself going down and up, and
  169. will then call back the clicked() method as if mouse was clicked on it.
  170. @see clicked
  171. */
  172. virtual void triggerClick();
  173. //==============================================================================
  174. /** Sets a command ID for this button to automatically invoke when it's clicked.
  175. When the button is pressed, it will use the given manager to trigger the
  176. command ID.
  177. Obviously be careful that the ApplicationCommandManager doesn't get deleted
  178. before this button is. To disable the command triggering, call this method and
  179. pass nullptr as the command manager.
  180. If generateTooltip is true, then the button's tooltip will be automatically
  181. generated based on the name of this command and its current shortcut key.
  182. @see addShortcut, getCommandID
  183. */
  184. void setCommandToTrigger (ApplicationCommandManager* commandManagerToUse,
  185. CommandID commandID,
  186. bool generateTooltip);
  187. /** Returns the command ID that was set by setCommandToTrigger(). */
  188. CommandID getCommandID() const noexcept { return commandID; }
  189. //==============================================================================
  190. /** Assigns a shortcut key to trigger the button.
  191. The button registers itself with its top-level parent component for keypresses.
  192. Note that a different way of linking buttons to keypresses is by using the
  193. setCommandToTrigger() method to invoke a command.
  194. @see clearShortcuts
  195. */
  196. void addShortcut (const KeyPress&);
  197. /** Removes all key shortcuts that had been set for this button.
  198. @see addShortcut
  199. */
  200. void clearShortcuts();
  201. /** Returns true if the given keypress is a shortcut for this button.
  202. @see addShortcut
  203. */
  204. bool isRegisteredForShortcut (const KeyPress&) const;
  205. //==============================================================================
  206. /** Sets an auto-repeat speed for the button when it is held down.
  207. (Auto-repeat is disabled by default).
  208. @param initialDelayInMillisecs how long to wait after the mouse is pressed before
  209. triggering the next click. If this is zero, auto-repeat
  210. is disabled
  211. @param repeatDelayInMillisecs the frequently subsequent repeated clicks should be
  212. triggered
  213. @param minimumDelayInMillisecs if this is greater than 0, the auto-repeat speed will
  214. get faster, the longer the button is held down, up to the
  215. minimum interval specified here
  216. */
  217. void setRepeatSpeed (int initialDelayInMillisecs,
  218. int repeatDelayInMillisecs,
  219. int minimumDelayInMillisecs = -1) noexcept;
  220. /** Sets whether the button click should happen when the mouse is pressed or released.
  221. By default the button is only considered to have been clicked when the mouse is
  222. released, but setting this to true will make it call the clicked() method as soon
  223. as the button is pressed.
  224. This is useful if the button is being used to show a pop-up menu, as it allows
  225. the click to be used as a drag onto the menu.
  226. */
  227. void setTriggeredOnMouseDown (bool isTriggeredOnMouseDown) noexcept;
  228. /** Returns whether the button click happens when the mouse is pressed or released.
  229. @see setTriggeredOnMouseDown
  230. */
  231. bool getTriggeredOnMouseDown() const noexcept;
  232. /** Returns the number of milliseconds since the last time the button
  233. went into the 'down' state.
  234. */
  235. uint32 getMillisecondsSinceButtonDown() const noexcept;
  236. //==============================================================================
  237. /** Sets the tooltip for this button.
  238. @see TooltipClient, TooltipWindow
  239. */
  240. void setTooltip (const String& newTooltip) override;
  241. //==============================================================================
  242. /** A combination of these flags are used by setConnectedEdges(). */
  243. enum ConnectedEdgeFlags
  244. {
  245. ConnectedOnLeft = 1,
  246. ConnectedOnRight = 2,
  247. ConnectedOnTop = 4,
  248. ConnectedOnBottom = 8
  249. };
  250. /** Hints about which edges of the button might be connected to adjoining buttons.
  251. The value passed in is a bitwise combination of any of the values in the
  252. ConnectedEdgeFlags enum.
  253. E.g. if you are placing two buttons adjacent to each other, you could use this to
  254. indicate which edges are touching, and the LookAndFeel might choose to drawn them
  255. without rounded corners on the edges that connect. It's only a hint, so the
  256. LookAndFeel can choose to ignore it if it's not relevant for this type of
  257. button.
  258. */
  259. void setConnectedEdges (int connectedEdgeFlags);
  260. /** Returns the set of flags passed into setConnectedEdges(). */
  261. int getConnectedEdgeFlags() const noexcept { return connectedEdgeFlags; }
  262. /** Indicates whether the button adjoins another one on its left edge.
  263. @see setConnectedEdges
  264. */
  265. bool isConnectedOnLeft() const noexcept { return (connectedEdgeFlags & ConnectedOnLeft) != 0; }
  266. /** Indicates whether the button adjoins another one on its right edge.
  267. @see setConnectedEdges
  268. */
  269. bool isConnectedOnRight() const noexcept { return (connectedEdgeFlags & ConnectedOnRight) != 0; }
  270. /** Indicates whether the button adjoins another one on its top edge.
  271. @see setConnectedEdges
  272. */
  273. bool isConnectedOnTop() const noexcept { return (connectedEdgeFlags & ConnectedOnTop) != 0; }
  274. /** Indicates whether the button adjoins another one on its bottom edge.
  275. @see setConnectedEdges
  276. */
  277. bool isConnectedOnBottom() const noexcept { return (connectedEdgeFlags & ConnectedOnBottom) != 0; }
  278. //==============================================================================
  279. /** Used by setState(). */
  280. enum ButtonState
  281. {
  282. buttonNormal,
  283. buttonOver,
  284. buttonDown
  285. };
  286. /** Can be used to force the button into a particular state.
  287. This only changes the button's appearance, it won't trigger a click, or stop any mouse-clicks
  288. from happening.
  289. The state that you set here will only last until it is automatically changed when the mouse
  290. enters or exits the button, or the mouse-button is pressed or released.
  291. */
  292. void setState (ButtonState newState);
  293. /** Returns the button's current over/down/up state. */
  294. ButtonState getState() const noexcept { return buttonState; }
  295. //==============================================================================
  296. /** This abstract base class is implemented by LookAndFeel classes to provide
  297. button-drawing functionality.
  298. */
  299. struct JUCE_API LookAndFeelMethods
  300. {
  301. virtual ~LookAndFeelMethods() = default;
  302. virtual void drawButtonBackground (Graphics&, Button&, const Colour& backgroundColour,
  303. bool shouldDrawButtonAsHighlighted, bool shouldDrawButtonAsDown) = 0;
  304. virtual Font getTextButtonFont (TextButton&, int buttonHeight) = 0;
  305. virtual int getTextButtonWidthToFitText (TextButton&, int buttonHeight) = 0;
  306. /** Draws the text for a TextButton. */
  307. virtual void drawButtonText (Graphics&, TextButton&,
  308. bool shouldDrawButtonAsHighlighted, bool shouldDrawButtonAsDown) = 0;
  309. /** Draws the contents of a standard ToggleButton. */
  310. virtual void drawToggleButton (Graphics&, ToggleButton&,
  311. bool shouldDrawButtonAsHighlighted, bool shouldDrawButtonAsDown) = 0;
  312. virtual void changeToggleButtonWidthToFitText (ToggleButton&) = 0;
  313. virtual void drawTickBox (Graphics&, Component&, float x, float y, float w, float h,
  314. bool ticked, bool isEnabled,
  315. bool shouldDrawButtonAsHighlighted, bool shouldDrawButtonAsDown) = 0;
  316. virtual void drawDrawableButton (Graphics&, DrawableButton&,
  317. bool shouldDrawButtonAsHighlighted, bool shouldDrawButtonAsDown) = 0;
  318. };
  319. //==============================================================================
  320. #ifndef DOXYGEN
  321. [[deprecated ("This method's parameters have changed.")]]
  322. void setToggleState (bool, bool);
  323. #endif
  324. protected:
  325. //==============================================================================
  326. /** This method is called when the button has been clicked.
  327. Subclasses can override this to perform whatever actions they need to do.
  328. In general, you wouldn't use this method to receive clicks, but should get your callbacks
  329. by attaching a std::function to the onClick callback, or adding a Button::Listener.
  330. @see triggerClick, onClick
  331. */
  332. virtual void clicked();
  333. /** This method is called when the button has been clicked.
  334. By default it just calls clicked(), but you might want to override it to handle
  335. things like clicking when a modifier key is pressed, etc.
  336. @see ModifierKeys
  337. */
  338. virtual void clicked (const ModifierKeys& modifiers);
  339. /** Subclasses should override this to actually paint the button's contents.
  340. It's better to use this than the paint method, because it gives you information
  341. about the over/down state of the button.
  342. @param g the graphics context to use
  343. @param shouldDrawButtonAsHighlighted true if the button is either in the 'over' or 'down' state
  344. @param shouldDrawButtonAsDown true if the button should be drawn in the 'down' position
  345. */
  346. virtual void paintButton (Graphics& g,
  347. bool shouldDrawButtonAsHighlighted,
  348. bool shouldDrawButtonAsDown) = 0;
  349. /** Called when the button's up/down/over state changes.
  350. Subclasses can override this if they need to do something special when the button
  351. goes up or down.
  352. @see isDown, isOver
  353. */
  354. virtual void buttonStateChanged();
  355. //==============================================================================
  356. /** @internal */
  357. virtual void internalClickCallback (const ModifierKeys&);
  358. /** @internal */
  359. void handleCommandMessage (int commandId) override;
  360. /** @internal */
  361. void mouseEnter (const MouseEvent&) override;
  362. /** @internal */
  363. void mouseExit (const MouseEvent&) override;
  364. /** @internal */
  365. void mouseDown (const MouseEvent&) override;
  366. /** @internal */
  367. void mouseDrag (const MouseEvent&) override;
  368. /** @internal */
  369. void mouseUp (const MouseEvent&) override;
  370. /** @internal */
  371. bool keyPressed (const KeyPress&) override;
  372. /** @internal */
  373. using Component::keyStateChanged;
  374. /** @internal */
  375. void paint (Graphics&) override;
  376. /** @internal */
  377. void parentHierarchyChanged() override;
  378. /** @internal */
  379. void visibilityChanged() override;
  380. /** @internal */
  381. void focusGained (FocusChangeType) override;
  382. /** @internal */
  383. void focusLost (FocusChangeType) override;
  384. /** @internal */
  385. void enablementChanged() override;
  386. private:
  387. //==============================================================================
  388. Array<KeyPress> shortcuts;
  389. WeakReference<Component> keySource;
  390. String text;
  391. ListenerList<Listener> buttonListeners;
  392. struct CallbackHelper;
  393. std::unique_ptr<CallbackHelper> callbackHelper;
  394. uint32 buttonPressTime = 0, lastRepeatTime = 0;
  395. ApplicationCommandManager* commandManagerToUse = nullptr;
  396. int autoRepeatDelay = -1, autoRepeatSpeed = 0, autoRepeatMinimumDelay = -1;
  397. int radioGroupId = 0, connectedEdgeFlags = 0;
  398. CommandID commandID = {};
  399. ButtonState buttonState = buttonNormal, lastStatePainted = buttonNormal;
  400. Value isOn;
  401. bool canBeToggled = false;
  402. bool lastToggleState = false;
  403. bool clickTogglesState = false;
  404. bool needsToRelease = false;
  405. bool needsRepainting = false;
  406. bool isKeyDown = false;
  407. bool triggerOnMouseDown = false;
  408. bool generateTooltip = false;
  409. std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override;
  410. void checkToggleableState (bool wasToggleable);
  411. void repeatTimerCallback();
  412. bool keyStateChangedCallback();
  413. void applicationCommandListChangeCallback();
  414. void updateAutomaticTooltip (const ApplicationCommandInfo&);
  415. ButtonState updateState();
  416. ButtonState updateState (bool isOver, bool isDown);
  417. bool isShortcutPressed() const;
  418. void turnOffOtherButtonsInGroup (NotificationType click, NotificationType state);
  419. void flashButtonState();
  420. void sendClickMessage (const ModifierKeys&);
  421. void sendStateMessage();
  422. void setToggleState (bool shouldBeOn, NotificationType click, NotificationType state);
  423. bool isMouseSourceOver (const MouseEvent& e);
  424. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Button)
  425. };
  426. } // namespace juce