Loading...
Searching...
No Matches
Tutorial: The ComboBox class

This tutorial introduces the ComboBox class, which is a component for displaying lists of items to the user. The contents of a ComboBox object can be modified dynamically, and can be used for text input, too.

Level: Beginner

Platforms: Windows, macOS, Linux, iOS, Android

Classes: ComboBox, Label, Font, Colour, Colours

Getting started

Download the demo project for this tutorial here: PIP | ZIP. Unzip the project and open the first header file in the Projucer.

If you need help with this step, see Tutorial: Projucer Part 1: Getting started with the Projucer.

The demo project

The demo project displays a piece of text at the top of the window within a Label component (see Tutorial: The Label class). A ComboBox component contains the items Plain, Bold, and Italic. The user can select one of these items to change the style of the text within the label.

Selecting font styles

The ComboBox class

This tutorial introduces many of the features of the ComboBox class. A ComboBox component contains a list of text strings. Each of these text strings is associated with an ID number (an int value). You can query which item is currently selected, either by:

The ComboBox class is also a broadcaster. To listen for changes you can register a ComboBox::Listener class (see Tutorial: Listeners and Broadcasters) or alternatively use a Lambda function with the ComboBox::onChange helper object.

Let's examine the demo project. In our MainContentComponent class we have four private members:

juce::Label textLabel { {}, "The quick brown fox jumps over the lazy dog." };
juce::Font textFont { 12.0f };
juce::ComboBox styleMenu;

The Label and Font objects are configured in the constructor:

MainContentComponent()
{
addAndMakeVisible (textLabel);
textLabel.setFont (textFont);

Adding items

Items can be added to a ComboBox object one at a time using the ComboBox::addItem() function. Here we add the "Plain", "Bold", and "Italic" items with ID numbers 1, 2, and 3 respectively:

// add items to the combo-box
addAndMakeVisible (styleMenu);
styleMenu.addItem ("Plain", 1);
styleMenu.addItem ("Bold", 2);
styleMenu.addItem ("Italic", 3);
styleMenu.onChange = [this] { styleMenuChanged(); };
styleMenu.setSelectedId (1);
setSize (400, 200);
}

Responding to changes

We could have registered our MainContentComponent object as a listener to get notified when the styleMenu ComboBox object is changed by the user in the application. But in this case we implement the ComboBox::onChange helper object to directly call the styleMenuChanged() function:

styleMenu.onChange = [this] { styleMenuChanged(); };

The styleMenuChanged() function handles changes to the styleMenu object:

void styleMenuChanged()
{
switch (styleMenu.getSelectedId())
{
case 1: textFont.setStyleFlags (juce::Font::plain); break;
case 2: textFont.setStyleFlags (juce::Font::bold); break;
case 3: textFont.setStyleFlags (juce::Font::italic); break;
default: break;
}
textLabel.setFont (textFont);
}

Here you can see that we set the textFont Font object appropriately based on the user's selection. Then we use this font to update the textLabel Label object's font.

Using item ID numbers

You can use any integer as an item ID except zero. Zero has a special meaning. It is used to indicate that none of the items are selected (either an item hasn't been selected yet or the ComboBox object is displaying some other custom text).

With a small selection of items using simple numbers like 1, 2, and 3 in your code is easy to manage. This would quickly become unmanageable as you develop your app further. In our case it would be clearer to use an enum. Let's add a private enum for our styles:

enum FontStyles
{
stylePlain = 1,
styleBold,
styleItalic,
numberOfStyles
};

Then we can use these values when setting up the ComboBox object:

addAndMakeVisible (styleMenu);
// add items to the combo-box
styleMenu.addItem ("Plain", 1);
styleMenu.addItem ("Bold", 2);
styleMenu.addItem ("Italic", 3);
styleMenu.onChange = [this] { styleMenuChanged(); };
styleMenu.setSelectedId (stylePlain);

And in our styleMenuChanged() function:

void styleMenuChanged()
{
switch (styleMenu.getSelectedId())
{
case stylePlain: textFont.setStyleFlags (juce::Font::plain); break;
case styleBold: textFont.setStyleFlags (juce::Font::bold); break;
case styleItalic: textFont.setStyleFlags (juce::Font::italic); break;
}
textLabel.setFont (textFont);
}

At the very least this make the code much more readable.

Other common strategies for using ID numbers are:

  • Using an array to store data relevant to the items in the combo-box. You can use the ID as an index into the array although you will either need to leave index 0 unused or use an offset. For example, your IDs could start at 100 (100, 101, 102, and so on). ID 100 would associate with index 0 in the array and you can then easily convert between IDs and indices by adding or subtracting 100.
  • Using different batches of integers in different ranges for each of the combo-box in your app. For example, you could use IDs 100, 101, 102... for one combo-box, 200, 201, 202... for another, and so on.
  • Using a hash of the item text. You could use String::hashCode() to get a probably unique hashcode for the string. But, be aware that this may return zero (using this function on an empty string returns a hashcode of zero).

Sections and dividers

The list of items in a combo-box can contain dividers and section headings. This is especially useful in very long lists. Let's add a combo-box to our app that changes the text colour. First, let's add another enum for our colours:

enum TextColours
{
black = 1,
red,
numberOfColours
};

And a new member to our MainContentComponent class:

juce::Label textLabel { {}, "The quick brown fox jumps over the lazy dog." };
juce::Font textFont { 12.0f };
juce::ComboBox styleMenu;
juce::ComboBox coloursMenu;

In the MainContentComponent constructor we need to add the code to set up the new combo-box. Here we introduce two new functions, ComboBox::addSeparator() and ComboBox::addSectionHeading():

addAndMakeVisible (coloursMenu);
coloursMenu.addItem ("Black", black);
coloursMenu.addItem ("White", white);
coloursMenu.addSeparator();
coloursMenu.addSectionHeading ("Reds");
coloursMenu.addItem ("Red", red);
coloursMenu.addItem ("Dark Red", darkred);
coloursMenu.addItem ("Indian Red", indianred);
coloursMenu.addSeparator();
coloursMenu.addSectionHeading ("Greens");
coloursMenu.addItem ("Green", green);
coloursMenu.addItem ("Dark Green", darkgreen);
coloursMenu.addItem ("Light Green", lightgreen);
coloursMenu.addSeparator();
coloursMenu.addSectionHeading ("Blues");
coloursMenu.addItem ("Blue", blue);
coloursMenu.addItem ("Dark Blue", darkblue);
coloursMenu.addItem ("Light Blue", lightblue);
coloursMenu.onChange = [this] { coloursMenuChanged(); };
coloursMenu.setSelectedId (black);
coloursMenu.setEditableText (true);
setSize (400, 200);
}

You can use separators without section headings and section headings without separators but they do work well together.

Note
Separators and section headings can't be selected and don't have associated ID numbers.

We need to implement a new function to handle the changes to the coloursMenu object:

void coloursMenuChanged()
{
juce::Colour textColour;
switch (coloursMenu.getSelectedId())
{
case black: textColour = Colours::black; break;
case white: textColour = Colours::white; break;
case red: textColour = Colours::red; break;
case darkred: textColour = Colours::darkred; break;
case indianred: textColour = Colours::indianred; break;
case green: textColour = Colours::green; break;
case darkgreen: textColour = Colours::darkgreen; break;
case lightgreen: textColour = Colours::lightgreen; break;
case blue: textColour = Colours::blue; break;
case darkblue: textColour = Colours::darkblue; break;
case lightblue: textColour = Colours::lightblue; break;
}
textLabel.setColour (juce::Label::textColourId, textColour);
}
const Colour lightblue
Definition juce_Colours.h:105
const Colour green
Definition juce_Colours.h:93
const Colour darkblue
Definition juce_Colours.h:63
const Colour darkred
Definition juce_Colours.h:73
const Colour white
Definition juce_Colours.h:179
const Colour blue
Definition juce_Colours.h:51
const Colour black
Definition juce_Colours.h:49
const Colour indianred
Definition juce_Colours.h:97
const Colour darkgreen
Definition juce_Colours.h:67
const Colour red
Definition juce_Colours.h:156
const Colour lightgreen
Definition juce_Colours.h:109

Don't forget to set the bounds of the coloursMenu object in the resized() function:

void resized() override
{
textLabel .setBounds (10, 10, getWidth() - 20, 20);
styleMenu .setBounds (10, 40, getWidth() - 20, 20);
coloursMenu.setBounds (10, 70, getWidth() - 20, 20);
}

Run the project and the window should look something like this:

Selecting colours and styles

You can now change the colour of the label text.

Text entry

The default behaviour of a combo-box is to allow the user to select only the items listed. But, you can make the combo-box editable to allow the user to enter other text. In the MainContentComponent constructor let's make our coloursMenu object editable:

Now let's add a function that takes the text from the coloursMenu object and treats the text as a hexadecimal colour code. The colour code is in the ARGB format (for example, "ff888888"" would be an opaque mid-grey colour): @snippet GUI/ComboBoxTutorial/ComboBoxTutorial_02.h handleColourText Notice that we convert the string to a hex value then convert it back to a string, setting the combo-box text. This is a simple way to filter the text entered. @note A more sophisticated method to filter input text would be to implement a custom class that derives from the ComboBox class. Then you can override its Label::Listener::editorShown() virtual function to customise its text editor. You can use the TextEditor::setInputFilter() function to use a TextEditor::InputFilter object to filter the text entered before it even appears on the screen. In our <code class="project_code">coloursMenuChanged()</code> function we need to update the <code class="code">switch..case</code> to call our <code class="project_code">handleColourText()</code> function in the <code class="code">default</code> case: @snippet GUI/ComboBoxTutorial/ComboBoxTutorial_02.h coloursMenuChanged @image html tutorial_combo_box_screenshot3.png "Entering custom text" Run the application and you should be able to enter custom colours in the six-digit hex format (RGB). <dl class="section attention"><dt>Exercise</dt><dd> Modify the code to add any custom hex colours to the <code class="project_code">coloursMenu</code> combo-box so that the user can choose these items again if they wish. </dd></dl> @subsection tutorial_combo_box_disabling_enabling Disabling and enabling items Items in a combo-box can be disabled and enabled (they are, of course, enabled by default). When they are disabled they will still be visible but shown greyed-out. Let's change the code so that none of the lighter versions colours (and white) can be selected if the bold style is selected. First, let's add a function to disable or enable the lighter colours using the ComboBox::setItemEnabled() function: @snippet GUI/ComboBoxTutorial/ComboBoxTutorial_02.h setLightColoursEnabled Now let's refactor our <code class="project_code">styleMenuChanged()</code> function and make use of the <code class="project_code">setLightColoursEnabled()</code> function: @snippet GUI/ComboBoxTutorial/ComboBoxTutorial_02.h styleMenuChanged Notice that in the <code class="project_code">setStyleBold()</code> function we disable the lighter colours and we need to enable them in the other cases. Run the application and you should now see that the lighter colours are disabled when the <b class="onscreen_text">Bold</b> style is selected. @image html tutorial_combo_box_screenshot4.png "Disabling combo-box items" <dl class="section attention"><dt>Exercise</dt><dd> One problem here is that you can select a lighter colour <em>then</em> choose the bold style. Modify the code to change the colour back to <b class="onscreen_text">black</b> if one of the lighter colours is selected when the user selects the <b class="onscreen_text">Bold</b> style. </dd></dl> @section tutorial_combo_box_notes Notes The final implementation of the code for this tutorial can be found in the <code class="filename">ComboBoxTutorial_02.h file of the demo project.

Summary

This tutorial has introduced the ComboBox class. After reading this tutorial you should be able to:

  • Create a combo-box and add items to it.
  • Manage ID numbers associated with the items in your combo-box.
  • Respond to users changing the item selected in the combo-box.
  • Allow custom text to be entered into the combo-box.
  • Disable and enable items in the combo-box.

See also

linkedin facebook pinterest youtube rss twitter instagram facebook-blank rss-blank linkedin-blank pinterest youtube twitter instagram