Tutorial: Colours in JUCE

Table of Contents


Specify and apply colours within your application in various ways.

Level: Beginner

Platforms: Windows, macOS, Linux, iOS, Android

Classes: Colour, Colours, LookAndFeel

Getting started

Download the demo project for this tutorial here: PIP | ZIP. Unzip the project and open it in your IDE.

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

The demo project

The demo project displays some child components and performs some simple drawing commands, which are used to illustrate how colours are specified and applied to components in JUCE. The application should look similar to the following screenshot:

tutorial_colours_screenshot1.png
The colours demo application

The first part of this tutorial looks at specifying colours more generally. This is illustrated by modifying the drawing code in the paint() function within the demo application. The second part of the tutorial shows how colours are specified for elements of the built-in component types (such as labels, sliders, and so on).

Colours and general painting operations

JUCE specifies colours using red, green, blue, and alpha (transparency) values. This is, of course, a widely used method of specificy colours in computing, but all implementations are slightly different. In particular, JUCE provides some useful methods for manipulating colours, which can help you maintain a consistent colour palette for your application. First, let's look at the paint() function from the demo application:

void paint (Graphics& g) override
{
auto centralArea = getLocalBounds().toFloat().reduced (10.0f);
g.drawRoundedRectangle (centralArea, 5.0f, 3.0f);
auto colourBarArea = centralArea.reduced (4.0f).withHeight (20.0f);
auto colourArea = colourBarArea.withWidth (colourBarArea.getWidth() / colours.size());
for (auto colour : colours)
{
g.setColour (colour);
g.fillRect (colourArea);
colourArea.translate (colourArea.getWidth(), 0.0f);
}
}

The first line fills the entire graphics context with a single colour (which means the entire component's bounds):

The next line sets the colour for future drawing operations for a given graphics context.

Then we define a slightly inset rectangle and draw a rounded rectangle, as a border, using the current colour:

auto centralArea = getLocalBounds().toFloat().reduced (10.0f);
g.drawRoundedRectangle (centralArea, 5.0f, 3.0f);

Next, we set up an array of colours, which we will use to draw a row of different coloured rectangles.

To draw this row of coloured rectangles, we first define the area within which they will be placed:

auto colourBarArea = centralArea.reduced (4.0f).withHeight (20.0f);

Then we define the area for the first coloured rectangle. This will be a proportion of the total width of the colourBarArea rectangle, divided by the number of colours that we are using:

auto colourArea = colourBarArea.withWidth (colourBarArea.getWidth() / colours.size());

Finally, we iterate over the array of colours, fill the rectangle with the specified colour, and move the colourArea rectangle to the right for the next iteration:

for (auto colour : colours)
{
g.setColour (colour);
g.fillRect (colourArea);
colourArea.translate (colourArea.getWidth(), 0.0f);
}

In the next few examples we will demonstrate some methods for specifying colours by changing the colours added to the colours array.

Specifying colours by name

As shown in the demo project and the code above, colours can be specified in JUCE using some constants in the Colours class.

Note
Have a look at the API documentation for the Colours class for a full list, which are mostly standard HTML colours.

In addition to the constants within the Colours class, you can use the Colours::findColourForName() function, using a string to look up the desired colour name. For example, we could use the same red, green, and blue colours using the following code to fill our colours array:

auto defaultColour = Colours::black;
Array<Colour> colours { Colours::findColourForName ("red", defaultColour),
Colours::findColourForName ("green", defaultColour),
Colours::findColourForName ("blue", defaultColour) };
Note
We need to provide a default colour (in this case we just use black) just in case the search for the named colour fails.

The Colours::findColourForName() function performs a case-insensitive search and trims whitespace from the start and end of the string, but not spaces within the string. For example, the following code will still work as expected, even though the colours are stored internally using all lowercase strings:

auto defaultColour = Colours::black;
Array<Colour> colours { Colours::findColourForName ("DarkRed", defaultColour),
Colours::findColourForName ("DarkGreen", defaultColour),
Colours::findColourForName ("DarkBlue", defaultColour) };

This produces the following colours:

tutorial_colours_screenshot2.png
Dark red, green, and blue

But including spaces within the colour name will fail, in our case returning a black colour in each case:

auto defaultColour = Colours::black;
Array<Colour> colours { Colours::findColourForName ("Dark Red", defaultColour),
Colours::findColourForName ("Dark Green", defaultColour),
Colours::findColourForName ("Dark Blue", defaultColour) };

It is straightforward to write your own functions to suit your needs in these instances. For example, you could write a function to remove all spaces from a string:

static String removeSpaces (const String& text)
{
return text.removeCharacters (" ");
}

And use that when passing a string to the Colours::findColourForName() function:

auto defaultColour = Colours::black;
Array<Colour> colours { Colours::findColourForName (removeSpaces ("Dark Red"), defaultColour),
Colours::findColourForName (removeSpaces ("Dark Green"), defaultColour),
Colours::findColourForName (removeSpaces ("Dark Blue"), defaultColour) };

Specifying colours from values

Colours can also be specified using the raw red, green, blue, and alpha values. Here you can create a Colour object using either floating point values in the range 0.0-1.0, or integers (of type uint8) between 0-255. Using integers we can create the same red, green, and blue colours as follows:

Array<Colour> colours { Colour (255, 0, 0), // red
Colour (0, 128, 0), // green
Colour (0, 0, 255) }; // blue
Note
The standard "green" colour does not have the maximum value 255 in the green element of the colour.

Omitting the alpha value in this case sets the alpha value to the maximum (255) making the colour completely opaque.

We can also use a single hexadecimal value to specify a colour. In this case the order of the colour value elements is: alpha, red, green, and blue:

Array<Colour> colours { Colour (0xffff0000), // red
Colour (0xff008000), // green
Colour (0xff0000ff) }; // blue
Note
In this case we MUST specify the alpha value otherwise it will be set to zero (and therefore transparent).

We can also use floating point values using the Colour::fromFloatRGBA() function:

Array<Colour> colours { Colour::fromFloatRGBA (1.0f, 0.0f, 0.0f, 1.0f), // red
Colour::fromFloatRGBA (0.0f, 0.5f, 0.0f, 1.0f), // green
Colour::fromFloatRGBA (0.0f, 0.0f, 1.0f, 1.0f) }; // blue
Note
A integer value of 128 is equivalent to a floating point value of around 0.501961. Therefore the green colours is not quite the same as the previous example, but 0.5 is close enough for this demonstration.
Exercise
Try out different colour values and review the results by running the application. You are not limited to adding three colours to the colours array, you can use any number of colours (greater than or equal to one).

Hue, saturation, and brightness

Colour objects can also be initialised from hue, saturation, and brightness values. This is one way to generate different colours that share some perceptual qualities.

For example we could create a series of light and dark reds using the following code:

Array<Colour> colours { Colour::fromHSV (0.0f, // hue
0.5f, // saturation
0.3f, // brightness
1.0f), // alpha
Colour::fromHSV (0.0f, 0.5f, 0.5f, 1.0f),
Colour::fromHSV (0.0f, 0.5f, 0.7f, 1.0f) };

Here the hue, saturation, and alpha values are constant for each colour (a hue of 0.0f should generate colours perceived as "reds"). The result is shown in the following screenshot:

tutorial_colours_screenshot3.png
Reds with saturation 0.5 and brightnesses 0.3, 0.5, and 0.7

We can also obtain the hue, saturation, and brightness values from a Colour object. For example, if we wanted a series of purples of different brightnesses, we could use the following code:

auto purpleHue = Colours::purple.getHue();
Array<Colour> colours { Colour::fromHSV (purpleHue, 0.5f, 0.3f, 1.0f),
Colour::fromHSV (purpleHue, 0.5f, 0.5f, 1.0f),
Colour::fromHSV (purpleHue, 0.5f, 0.7f, 1.0f) };

The result of this is shown in the following screenshot:

tutorial_colours_screenshot4.png
Purples with saturation 0.5 and brightnesses 0.3, 0.5, and 0.7

Manipulating colour values

We can also use existing colours to create new colours. For example, to make colours that are slightly brighter or darker than an existing colour we can use the Colour::brighter() or Colour::darker() functions respectively:

auto baseColour = Colours::orange;
Array<Colour> colours { baseColour.darker(),
baseColour,
baseColour.brighter() };

Or you can blend between two colours using the Colour::interpolatedWith() function:

auto colour1 = Colours::red;
auto colour2 = Colours::purple;
Array<Colour> colours { colour1,
colour1.interpolatedWith (colour2, 0.5f),
colour2 };

The result of this is shown in the following screenshot:

tutorial_colours_screenshot5.png
Red and purple with an equal blend of red and purple in between

Given one colour you can create another colour that will be clearly visible against another colour using the Colour::contrasting() function. This allows you to specify the amount of contrast using an argument:

auto baseColour = Colours::darkcyan;
Array<Colour> colours { baseColour,
baseColour.contrasting (0.5f) };

You can even create a colour that is contrasting against two other colours:

auto colour1 = Colours::lightblue;
auto colour2 = Colours::darkred;
Array<Colour> colours { colour1,
Colour::contrasting (colour1, colour2),
colour2 };

There are various other manipulations that can be performed such as blending two colours taking into account the alpha channel of the overlaid colour using the Colour::overlaidWith() function.

Specifying component colours

The previous section explored the use of colours when performing your own drawing operations with your component's paint() function. To customise the colours of the built-in components (such as sliders, labels, and so on) you need to use Component::setColour() or LookAndFeel::setColour() functions.

Essentially, each of the built-in Component subclasses contains an enum that lists the various elements of the component that can have a specific colour. Each of these items is referred to as a colour ID. (The values of these colour IDs are unique across the JUCE library.) For example, the colour IDs for the Label class are as follows (from Label::ColourIds):

Let's try changing some of these colours. If you look in the MainContentComponent constructor you will see a Label, a TextEditor, a TextButton, and two Slider objects added as child components. Add the line [1] as shown below to change the label's text colour to black:

MainContentComponent()
{
label.setColour (Label::textColourId, Colours::black); // [1]
label.setEditable (true);
addAndMakeVisible (label);
//...

The result should be similar to the following screenshot:

tutorial_colours_screenshot6.png
Showing a label with a customised text colour
Exercise
Look at the colour IDs for the TextEditor, TextButton, and Slider classes (TextEditor::ColourIds, TextButton::ColourIds, and Slider::ColourIds) and experiment with setting different colours for the child components in the demo application.

Setting look-and-feel colours

It is very common for applications, or parts of applications, to require the same colour palette for all components of the same type. You may have found in the exercise in the previous section that you needed to repeat the calls to the Component::setColour() function for both sliders in order to give them the same appearance. One use of the LookAndFeel class is to provide a single point where these colours can be specified. To illustrate this, return the MainContentComponent constructor back to its original state as shown below:

MainContentComponent()
{
label.setEditable (true);
addAndMakeVisible (label);
textEditor.setText ("This is a text editor.");
addAndMakeVisible (textEditor);
textButton.setClickingTogglesState (true);
addAndMakeVisible (textButton);
addAndMakeVisible (slider1);
addAndMakeVisible (slider2);
setSize (600, 210);
}

Now add the following line [2] to set the colour of the thumbs for both sliders:

//...
getLookAndFeel().setColour (Slider::thumbColourId, Colours::red); // [2]
addAndMakeVisible (slider1);
addAndMakeVisible (slider2);
//...

This should produce a result similar to the following screenshot:

tutorial_colours_screenshot7.png
Customising the colour of multiple slider thumbs using a single line of code

Custom look-and-feel colours

You can also make a subclass of one of the LookAndFeel classes (LookAndFeel_V1, LookAndFeel_V2, LookAndFeel_V3, or LookAndFeel_V4) and customise specific colours in its constructor. To do this you could add the following class as a nested class of our MainContentComponent class:

class CustomLookAndFeel : public LookAndFeel_V4
{
public:
CustomLookAndFeel()
{
}
};

Add an instance of this class to our private member section [3]:

private:
CustomLookAndFeel lf; // [3]
Label label { {}, "This is some label text." };

And set the MainContentComponent class to use this look-and-feel in its constructor [4]:

MainContentComponent()
{
setLookAndFeel (&lf); // [4]
label.setEditable (true);
//...
Note
The changed code for this subsection can be found in the ColoursTutorial_02.h file of the demo project.
Exercise
Customise more colours in the CustomLookAndFeel constructor.

Summary

In this tutorial we have looked at the following items that you can use in your own applications:

See also