478 lines
19 KiB
C
478 lines
19 KiB
C
|
/*
|
||
|
==============================================================================
|
||
|
|
||
|
This file is part of the JUCE library.
|
||
|
Copyright (c) 2020 - Raw Material Software Limited
|
||
|
|
||
|
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 6 End-User License
|
||
|
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||
|
|
||
|
End User License Agreement: www.juce.com/juce-6-licence
|
||
|
Privacy Policy: www.juce.com/juce-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
|
||
|
{
|
||
|
|
||
|
//==============================================================================
|
||
|
/**
|
||
|
A component that lets the user choose from a drop-down list of choices.
|
||
|
|
||
|
The combo-box has a list of text strings, each with an associated id number,
|
||
|
that will be shown in the drop-down list when the user clicks on the component.
|
||
|
|
||
|
The currently selected choice is displayed in the combo-box, and this can
|
||
|
either be read-only text, or editable.
|
||
|
|
||
|
To find out when the user selects a different item or edits the text, you
|
||
|
can assign a lambda to the onChange member, or register a ComboBox::Listener
|
||
|
to receive callbacks.
|
||
|
|
||
|
@tags{GUI}
|
||
|
*/
|
||
|
class JUCE_API ComboBox : public Component,
|
||
|
public SettableTooltipClient,
|
||
|
public Value::Listener,
|
||
|
private AsyncUpdater
|
||
|
{
|
||
|
public:
|
||
|
//==============================================================================
|
||
|
/** Creates a combo-box.
|
||
|
|
||
|
On construction, the text field will be empty, so you should call the
|
||
|
setSelectedId() or setText() method to choose the initial value before
|
||
|
displaying it.
|
||
|
|
||
|
@param componentName the name to set for the component (see Component::setName())
|
||
|
*/
|
||
|
explicit ComboBox (const String& componentName = {});
|
||
|
|
||
|
/** Destructor. */
|
||
|
~ComboBox() override;
|
||
|
|
||
|
//==============================================================================
|
||
|
/** Sets whether the text in the combo-box is editable.
|
||
|
|
||
|
The default state for a new ComboBox is non-editable, and can only be changed
|
||
|
by choosing from the drop-down list.
|
||
|
*/
|
||
|
void setEditableText (bool isEditable);
|
||
|
|
||
|
/** Returns true if the text is directly editable.
|
||
|
@see setEditableText
|
||
|
*/
|
||
|
bool isTextEditable() const noexcept;
|
||
|
|
||
|
/** Sets the style of justification to be used for positioning the text.
|
||
|
|
||
|
The default is Justification::centredLeft. The text is displayed using a
|
||
|
Label component inside the ComboBox.
|
||
|
*/
|
||
|
void setJustificationType (Justification justification);
|
||
|
|
||
|
/** Returns the current justification for the text box.
|
||
|
@see setJustificationType
|
||
|
*/
|
||
|
Justification getJustificationType() const noexcept;
|
||
|
|
||
|
//==============================================================================
|
||
|
/** Adds an item to be shown in the drop-down list.
|
||
|
|
||
|
@param newItemText the text of the item to show in the list
|
||
|
@param newItemId an associated ID number that can be set or retrieved - see
|
||
|
getSelectedId() and setSelectedId(). Note that this value can not
|
||
|
be 0!
|
||
|
@see setItemEnabled, addSeparator, addSectionHeading, getNumItems, getItemText, getItemId
|
||
|
*/
|
||
|
void addItem (const String& newItemText, int newItemId);
|
||
|
|
||
|
/** Adds an item to be shown in the drop-down list.
|
||
|
|
||
|
@param newItemText the text of the item to show in the list
|
||
|
@param newItemId an associated ID number that can be set or retrieved - see
|
||
|
getSelectedId() and setSelectedId(). Note that this value can not
|
||
|
be 0!
|
||
|
@param newItemImage an icon image that is shown to the left of text
|
||
|
@see setItemEnabled, addSeparator, addSectionHeading, getNumItems, getItemText, getItemId
|
||
|
*/
|
||
|
void addItem (const String& newItemText, int newItemId, const Image & newItemImage);
|
||
|
|
||
|
|
||
|
/** Adds an array of items to the drop-down list.
|
||
|
The item ID of each item will be its index in the StringArray + firstItemIdOffset.
|
||
|
*/
|
||
|
void addItemList (const StringArray& items, int firstItemIdOffset);
|
||
|
|
||
|
/** Adds a separator line to the drop-down list.
|
||
|
|
||
|
This is like adding a separator to a popup menu. See PopupMenu::addSeparator().
|
||
|
*/
|
||
|
void addSeparator();
|
||
|
|
||
|
/** Adds a heading to the drop-down list, so that you can group the items into
|
||
|
different sections.
|
||
|
|
||
|
The headings are indented slightly differently to set them apart from the
|
||
|
items on the list, and obviously can't be selected. You might want to add
|
||
|
separators between your sections too.
|
||
|
|
||
|
@see addItem, addSeparator
|
||
|
*/
|
||
|
void addSectionHeading (const String& headingName);
|
||
|
|
||
|
/** This allows items in the drop-down list to be selectively disabled.
|
||
|
|
||
|
When you add an item, it's enabled by default, but you can call this
|
||
|
method to change its status.
|
||
|
|
||
|
If you disable an item which is already selected, this won't change the
|
||
|
current selection - it just stops the user choosing that item from the list.
|
||
|
*/
|
||
|
void setItemEnabled (int itemId, bool shouldBeEnabled);
|
||
|
|
||
|
/** Returns true if the given item is enabled. */
|
||
|
bool isItemEnabled (int itemId) const noexcept;
|
||
|
|
||
|
/** Changes the text for an existing item.
|
||
|
*/
|
||
|
void changeItemText (int itemId, const String& newText);
|
||
|
|
||
|
/** Removes all the items from the drop-down list.
|
||
|
|
||
|
If this call causes the content to be cleared, and a change-message
|
||
|
will be broadcast according to the notification parameter.
|
||
|
|
||
|
@see addItem, getNumItems
|
||
|
*/
|
||
|
void clear (NotificationType notification = sendNotificationAsync);
|
||
|
|
||
|
/** Returns the number of items that have been added to the list.
|
||
|
|
||
|
Note that this doesn't include headers or separators.
|
||
|
*/
|
||
|
int getNumItems() const noexcept;
|
||
|
|
||
|
/** Returns the text for one of the items in the list.
|
||
|
Note that this doesn't include headers or separators.
|
||
|
@param index the item's index from 0 to (getNumItems() - 1)
|
||
|
*/
|
||
|
String getItemText (int index) const;
|
||
|
|
||
|
/** Returns the ID for one of the items in the list.
|
||
|
Note that this doesn't include headers or separators.
|
||
|
@param index the item's index from 0 to (getNumItems() - 1)
|
||
|
*/
|
||
|
int getItemId (int index) const noexcept;
|
||
|
|
||
|
/** Returns the index in the list of a particular item ID.
|
||
|
If no such ID is found, this will return -1.
|
||
|
*/
|
||
|
int indexOfItemId (int itemId) const noexcept;
|
||
|
|
||
|
//==============================================================================
|
||
|
/** Returns the ID of the item that's currently shown in the box.
|
||
|
|
||
|
If no item is selected, or if the text is editable and the user
|
||
|
has entered something which isn't one of the items in the list, then
|
||
|
this will return 0.
|
||
|
|
||
|
@see setSelectedId, getSelectedItemIndex, getText
|
||
|
*/
|
||
|
int getSelectedId() const noexcept;
|
||
|
|
||
|
/** Returns a Value object that can be used to get or set the selected item's ID.
|
||
|
|
||
|
You can call Value::referTo() on this object to make the combo box control
|
||
|
another Value object.
|
||
|
*/
|
||
|
Value& getSelectedIdAsValue() { return currentId; }
|
||
|
|
||
|
/** Sets one of the items to be the current selection.
|
||
|
|
||
|
This will set the ComboBox's text to that of the item that matches
|
||
|
this ID.
|
||
|
|
||
|
@param newItemId the new item to select
|
||
|
@param notification determines the type of change notification that will
|
||
|
be sent to listeners if the value changes
|
||
|
@see getSelectedId, setSelectedItemIndex, setText
|
||
|
*/
|
||
|
void setSelectedId (int newItemId,
|
||
|
NotificationType notification = sendNotificationAsync);
|
||
|
|
||
|
//==============================================================================
|
||
|
/** Returns the index of the item that's currently shown in the box.
|
||
|
|
||
|
If no item is selected, or if the text is editable and the user
|
||
|
has entered something which isn't one of the items in the list, then
|
||
|
this will return -1.
|
||
|
|
||
|
@see setSelectedItemIndex, getSelectedId, getText
|
||
|
*/
|
||
|
int getSelectedItemIndex() const;
|
||
|
|
||
|
/** Sets one of the items to be the current selection.
|
||
|
|
||
|
This will set the ComboBox's text to that of the item at the given
|
||
|
index in the list.
|
||
|
|
||
|
@param newItemIndex the new item to select
|
||
|
@param notification determines the type of change notification that will
|
||
|
be sent to listeners if the value changes
|
||
|
@see getSelectedItemIndex, setSelectedId, setText
|
||
|
*/
|
||
|
void setSelectedItemIndex (int newItemIndex,
|
||
|
NotificationType notification = sendNotificationAsync);
|
||
|
|
||
|
//==============================================================================
|
||
|
/** Returns the text that is currently shown in the combo-box's text field.
|
||
|
|
||
|
If the ComboBox has editable text, then this text may have been edited
|
||
|
by the user; otherwise it will be one of the items from the list, or
|
||
|
possibly an empty string if nothing was selected.
|
||
|
|
||
|
@see setText, getSelectedId, getSelectedItemIndex
|
||
|
*/
|
||
|
String getText() const;
|
||
|
|
||
|
/** Sets the contents of the combo-box's text field.
|
||
|
|
||
|
The text passed-in will be set as the current text regardless of whether
|
||
|
it is one of the items in the list. If the current text isn't one of the
|
||
|
items, then getSelectedId() will return 0, otherwise it will return
|
||
|
the appropriate ID.
|
||
|
|
||
|
@param newText the text to select
|
||
|
@param notification determines the type of change notification that will
|
||
|
be sent to listeners if the text changes
|
||
|
@see getText
|
||
|
*/
|
||
|
void setText (const String& newText,
|
||
|
NotificationType notification = sendNotificationAsync);
|
||
|
|
||
|
/** Programmatically opens the text editor to allow the user to edit the current item.
|
||
|
|
||
|
This is the same effect as when the box is clicked-on.
|
||
|
@see Label::showEditor();
|
||
|
*/
|
||
|
void showEditor();
|
||
|
|
||
|
/** Pops up the combo box's list.
|
||
|
This is virtual so that you can override it with your own custom popup
|
||
|
mechanism if you need some really unusual behaviour.
|
||
|
*/
|
||
|
virtual void showPopup();
|
||
|
|
||
|
/** Hides the combo box's popup list, if it's currently visible. */
|
||
|
void hidePopup();
|
||
|
|
||
|
/** Returns true if the popup menu is currently being shown. */
|
||
|
bool isPopupActive() const noexcept { return menuActive; }
|
||
|
|
||
|
/** Returns the PopupMenu object associated with the ComboBox.
|
||
|
Can be useful for adding sub-menus to the ComboBox standard PopupMenu
|
||
|
*/
|
||
|
PopupMenu* getRootMenu() noexcept { return ¤tMenu; }
|
||
|
|
||
|
/** Returns the PopupMenu object associated with the ComboBox. */
|
||
|
const PopupMenu* getRootMenu() const noexcept { return ¤tMenu; }
|
||
|
|
||
|
//==============================================================================
|
||
|
/**
|
||
|
A class for receiving events from a ComboBox.
|
||
|
|
||
|
You can register a ComboBox::Listener with a ComboBox using the ComboBox::addListener()
|
||
|
method, and it will be called when the selected item in the box changes.
|
||
|
|
||
|
@see ComboBox::addListener, ComboBox::removeListener
|
||
|
*/
|
||
|
class JUCE_API Listener
|
||
|
{
|
||
|
public:
|
||
|
/** Destructor. */
|
||
|
virtual ~Listener() = default;
|
||
|
|
||
|
/** Called when a ComboBox has its selected item changed. */
|
||
|
virtual void comboBoxChanged (ComboBox* comboBoxThatHasChanged) = 0;
|
||
|
};
|
||
|
|
||
|
/** Registers a listener that will be called when the box's content changes. */
|
||
|
void addListener (Listener* listener);
|
||
|
|
||
|
/** Deregisters a previously-registered listener. */
|
||
|
void removeListener (Listener* listener);
|
||
|
|
||
|
//==============================================================================
|
||
|
/** You can assign a lambda to this callback object to have it called when the selected ID is changed. */
|
||
|
std::function<void()> onChange;
|
||
|
|
||
|
//==============================================================================
|
||
|
/** Sets a message to display when there is no item currently selected.
|
||
|
@see getTextWhenNothingSelected
|
||
|
*/
|
||
|
void setTextWhenNothingSelected (const String& newMessage);
|
||
|
|
||
|
/** Returns the text that is shown when no item is selected.
|
||
|
@see setTextWhenNothingSelected
|
||
|
*/
|
||
|
String getTextWhenNothingSelected() const;
|
||
|
|
||
|
/** Sets the message to show when there are no items in the list, and the user clicks
|
||
|
on the drop-down box.
|
||
|
|
||
|
By default it just says "no choices", but this lets you change it to something more
|
||
|
meaningful.
|
||
|
*/
|
||
|
void setTextWhenNoChoicesAvailable (const String& newMessage);
|
||
|
|
||
|
/** Returns the text shown when no items have been added to the list.
|
||
|
@see setTextWhenNoChoicesAvailable
|
||
|
*/
|
||
|
String getTextWhenNoChoicesAvailable() const;
|
||
|
|
||
|
//==============================================================================
|
||
|
/** Gives the ComboBox a tooltip. */
|
||
|
void setTooltip (const String& newTooltip) override;
|
||
|
|
||
|
/** This can be used to allow the scroll-wheel to nudge the chosen item.
|
||
|
By default it's disabled, and I'd recommend leaving it disabled if there's any
|
||
|
chance that the control might be inside a scrollable list or viewport.
|
||
|
*/
|
||
|
void setScrollWheelEnabled (bool enabled) noexcept;
|
||
|
|
||
|
|
||
|
//==============================================================================
|
||
|
/** A set of colour IDs to use to change the colour of various aspects of the combo box.
|
||
|
|
||
|
These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
|
||
|
methods.
|
||
|
|
||
|
To change the colours of the menu that pops up, you can set the colour IDs in PopupMenu::ColourIDs.
|
||
|
|
||
|
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
|
||
|
*/
|
||
|
enum ColourIds
|
||
|
{
|
||
|
backgroundColourId = 0x1000b00, /**< The background colour to fill the box with. */
|
||
|
textColourId = 0x1000a00, /**< The colour for the text in the box. */
|
||
|
outlineColourId = 0x1000c00, /**< The colour for an outline around the box. */
|
||
|
buttonColourId = 0x1000d00, /**< The base colour for the button (a LookAndFeel class will probably use variations on this). */
|
||
|
arrowColourId = 0x1000e00, /**< The colour for the arrow shape that pops up the menu */
|
||
|
focusedOutlineColourId = 0x1000f00 /**< The colour that will be used to draw a box around the edge of the component when it has focus. */
|
||
|
};
|
||
|
|
||
|
//==============================================================================
|
||
|
/** This abstract base class is implemented by LookAndFeel classes to provide
|
||
|
ComboBox functionality.
|
||
|
*/
|
||
|
struct JUCE_API LookAndFeelMethods
|
||
|
{
|
||
|
virtual ~LookAndFeelMethods() = default;
|
||
|
|
||
|
virtual void drawComboBox (Graphics&, int width, int height, bool isButtonDown,
|
||
|
int buttonX, int buttonY, int buttonW, int buttonH,
|
||
|
ComboBox&) = 0;
|
||
|
|
||
|
virtual Font getComboBoxFont (ComboBox&) = 0;
|
||
|
|
||
|
virtual Label* createComboBoxTextBox (ComboBox&) = 0;
|
||
|
|
||
|
virtual void positionComboBoxText (ComboBox&, Label& labelToPosition, Drawable * image = nullptr) = 0;
|
||
|
|
||
|
virtual PopupMenu::Options getOptionsForComboBoxPopupMenu (ComboBox&, Label&) = 0;
|
||
|
|
||
|
virtual void drawComboBoxTextWhenNothingSelected (Graphics&, ComboBox&, Label&) = 0;
|
||
|
};
|
||
|
|
||
|
//==============================================================================
|
||
|
/** @internal */
|
||
|
void enablementChanged() override;
|
||
|
/** @internal */
|
||
|
void colourChanged() override;
|
||
|
/** @internal */
|
||
|
void focusGained (Component::FocusChangeType) override;
|
||
|
/** @internal */
|
||
|
void focusLost (Component::FocusChangeType) override;
|
||
|
/** @internal */
|
||
|
void handleAsyncUpdate() override;
|
||
|
/** @internal */
|
||
|
String getTooltip() override { return label->getTooltip(); }
|
||
|
/** @internal */
|
||
|
void mouseDown (const MouseEvent&) override;
|
||
|
/** @internal */
|
||
|
void mouseDrag (const MouseEvent&) override;
|
||
|
/** @internal */
|
||
|
void mouseUp (const MouseEvent&) override;
|
||
|
/** @internal */
|
||
|
void mouseWheelMove (const MouseEvent&, const MouseWheelDetails&) override;
|
||
|
/** @internal */
|
||
|
void lookAndFeelChanged() override;
|
||
|
/** @internal */
|
||
|
void paint (Graphics&) override;
|
||
|
/** @internal */
|
||
|
void resized() override;
|
||
|
/** @internal */
|
||
|
bool keyStateChanged (bool) override;
|
||
|
/** @internal */
|
||
|
bool keyPressed (const KeyPress&) override;
|
||
|
/** @internal */
|
||
|
void valueChanged (Value&) override;
|
||
|
/** @internal */
|
||
|
void parentHierarchyChanged() override;
|
||
|
|
||
|
//==============================================================================
|
||
|
#ifndef DOXYGEN
|
||
|
// These methods' bool parameters have changed: see their new method signatures.
|
||
|
[[deprecated]] void clear (bool);
|
||
|
[[deprecated]] void setSelectedId (int, bool);
|
||
|
[[deprecated]] void setSelectedItemIndex (int, bool);
|
||
|
[[deprecated]] void setText (const String&, bool);
|
||
|
#endif
|
||
|
|
||
|
private:
|
||
|
//==============================================================================
|
||
|
enum EditableState
|
||
|
{
|
||
|
editableUnknown,
|
||
|
labelIsNotEditable,
|
||
|
labelIsEditable
|
||
|
};
|
||
|
|
||
|
PopupMenu currentMenu;
|
||
|
Value currentId;
|
||
|
int lastCurrentId = 0;
|
||
|
bool isButtonDown = false, menuActive = false, scrollWheelEnabled = false;
|
||
|
float mouseWheelAccumulator = 0;
|
||
|
ListenerList<Listener> listeners;
|
||
|
std::unique_ptr<Label> label;
|
||
|
String textWhenNothingSelected, noChoicesMessage;
|
||
|
EditableState labelEditableState = editableUnknown;
|
||
|
|
||
|
std::unique_ptr<Drawable> image;
|
||
|
|
||
|
std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override;
|
||
|
PopupMenu::Item* getItemForId (int) const noexcept;
|
||
|
PopupMenu::Item* getItemForIndex (int) const noexcept;
|
||
|
bool selectIfEnabled (int index);
|
||
|
bool nudgeSelectedItem (int delta);
|
||
|
void sendChange (NotificationType);
|
||
|
void showPopupIfNotActive();
|
||
|
|
||
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComboBox)
|
||
|
};
|
||
|
|
||
|
|
||
|
} // namespace juce
|