Tutorial: Slider values

This tutorial introduces the Slider class, shows how to respond to slider movements, and how to obtain values from a slider.

The tutorial also introduces some essential customisation techniques for displaying values with a slider.

Level: Beginner

Platforms: Windows, Mac OS X, Linux, iOS, Android

Classes: Slider, Slider::Listener

Getting started

Note
This tutorial leads on from Tutorial: Listeners and broadcasters, which you should have read and understood first.

Download the demo project for this tutorial here: tutorial_slider_values.zip. Unzip the project and open it in your IDE.

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

The demo project

The demo project shows two linear horizontal sliders. One slider is labelled Frequency and the other is labelled Duration as shown in the following screenshot:

tutorial_slider_values_screenshot1.png
The demo project user interface showing two sliders and their values.

The idea is that both sliders essentially display the same underlying value since frequency (f) is the reciprocal of duration (d):

f = 1d

When either of the sliders are moved, the other one updates to reflect the change.

The JUCE Slider class.

This tutorial shows how to create the sliders, configure their range, listen for changes in value, and update the slider value programmatically. You will notice in the demo application, when it runs, that both sliders include a text box, and this text box also includes the units for frequency (Hz, Hertz) and duration (s, seconds).

Adding the sliders

The sliders have been added as private members to our MainContentComponent class in the MainComponent.h file:

private:
Slider frequencySlider;
Label frequencyLabel;
Slider durationSlider;
Label durationLabel;
//==============================================================================
};

Notice that we also add a Label object for each Slider object. These are to display the text Frequency and Duration to the left of the sliders. The boxes immediately to the left of the slider controls, which show the current slider values, are actually part of the Slider objects.

We have also added the Slider::Listener class as base class, so that we can register our class to receive slider changes

class MainContentComponent : public Component,
{
// ...

In our MainContentComponent constructor, we add the sliders as child components (see Tutorial: The Component class, parents, and children), make them visible, and configure the range of values that the slider can represent. First we configure the frequencySlider member:

MainContentComponent()
{
addAndMakeVisible (frequencySlider);
frequencySlider.setRange (50, 5000.0); // [1]
frequencySlider.setTextValueSuffix (" Hz"); // [2]
frequencySlider.addListener (this); // [3]
// ...
  • [1]: The range of the slider is set using the Slider::setRange() function.
  • [2]: We add a suffix to the text display in the slider's text box to show the value's units.
  • [3]: We add our MainContentComponent object as a listener to the slider.

The corresponding label is set up as follows:

addAndMakeVisible (frequencyLabel);
frequencyLabel.setText ("Frequency", dontSendNotification);
frequencyLabel.attachToComponent (&frequencySlider, true); // [4]

The Label::attachToComponent() function [4] is really useful for placing a label adjacent to another component. The second argument, true, positions the label to the left of the other component (false would position it above). As we will see shortly, this avoids us having to position the labels manually in the MainContentComponent::resized() function.

The durationSlider and the durationLabel members are set up similarly, but the range of this slider is set to be the reciprocal of the range of the frequencySlider member:

addAndMakeVisible (durationSlider);
durationSlider.setRange (1.0 / frequencySlider.getMaximum(),
1.0 / frequencySlider.getMinimum());
durationSlider.setTextValueSuffix (" s");
durationSlider.addListener (this);
addAndMakeVisible (durationLabel);
durationLabel.setText ("Duration", dontSendNotification);
durationLabel.attachToComponent (&durationSlider, true);

Positioning the slider

The sliders are positioned in the MainContentComponent::resized() function. Since we used the Label::attachToComponent() function to attach the labels to the sliders, these are positioned to the left of the sliders automatically.

void resized() override
{
const int sliderLeft = 120;
frequencySlider.setBounds (sliderLeft, 20, getWidth() - sliderLeft - 10, 20);
durationSlider.setBounds (sliderLeft, 50, getWidth() - sliderLeft - 10, 20);
}

Responding to slider changes

The following code makes the listeners of the sliders react to changes in the sliders' values.

void sliderValueChanged (Slider* slider) override
{
if (slider == &frequencySlider)
durationSlider.setValue (1.0 / frequencySlider.getValue(), dontSendNotification);
else if (slider == &durationSlider)
frequencySlider.setValue (1.0 / durationSlider.getValue(), dontSendNotification);
}

This is the Slider::Listener::sliderValueChanged() function that we must override if we add Slider::Listener as a base class. Here we simply pass the reciprocal of the slider to the other slider by calling the Slider::setValue() function. We also tell the slider not to broadcast its change. This is because there is the potential for an infinite feedback loop to occur in cases such as this, where two sliders depend upon each other. The dontSendNotification value breaks this potential loop. Assuming the arithmetic is accurate, and the the conversions in both directions produce identical results, then this shouldn't be needed. This is because the slider will only broadcast to its listeners if the value has actually changed. (Problems can occur where there are slight rounding errors in the conversions in situations like this.) You can try omitting the dontSendNotification value which causes the default behaviour where the slider will broadcast changes. You really need to think carefully about whether to use dontSendNotification, or not, for specific use-cases in your own applications.

Setting the initial value

In the constructor the frequencySlider slider is set to a value of 500. This in turn will cause the durationSlider slider to update, since we omit the dontSendNotification value this time:

frequencySlider.setValue (500.0); // [5]

Some customisations

There are couple of simple customisations we can add here to make the interface more effective.

Making the text box wider

The text box for the durationSlider slider, in particular, needs many digits to display its value satisfactorily. To do this we can use the Slider::setTextBoxStyle() function. Add the following two lines of code to the MainContentComponent constructor:

frequencySlider.setTextBoxStyle (Slider::TextBoxLeft, false, 160, frequencySlider.getTextBoxHeight());
durationSlider.setTextBoxStyle (Slider::TextBoxLeft, false, 160, durationSlider.getTextBoxHeight());

This sets the text box to be 160 pixels in each case (but maintaining the height by using the Slider::getTextBoxHeight() function).

tutorial_slider_values_screenshot2.png
The sliders with wider text boxes.

Skewing the slider values

By default, the slider track is linear in the sense that the slider's value is proportion to the position of the slider thumb along the slider track. It is clear from manipulating the interface that this doesn't quite feel right. We can adjust the slider skew to make the slider track logarithmic. To do this we can use the Slider::setSkewFactorFromMidPoint() function. Try this out by adding the following two lines of code to the MainContentComponent constructor after the sliders have been configured:

frequencySlider.setSkewFactorFromMidPoint (500);
durationSlider.setSkewFactorFromMidPoint (0.002);

This places a value of 500 at the mid-point of the slider track for the frequencySlider slider, and 0.002 for the durationSlider slider. Effectively, the sliders will now seem to move equally but in opposite directions. A non-linear slider track like this works well for parameters such as time and frequency where we tend to want finer control over smaller values but need less fine control over larger values.

Note
The completed code for this section can be found in the MainComponent_02.h file within the Source directory of the demo project for this tutorial.
Exercise
Try out different values for the calls to the Slider::setSkewFactorFromMidPoint() function and try out different text box sizes. Have a look at the API reference for the Slider class and try out some other customisations.

Summary

In this tutorial we have introduced the Slider class. In particular we have learned:

  • How to set up a slider to operate over a specific range.
  • How to respond to slider value changes.
  • How to configure the slider skew to make it use a logarithmic scale.

See also