Slider TextBox custom position


I just wanted to know, whether there is a way to set the Slider TextBox position either relatively to the Slider itself or just absolutely within the parrent component. The point is that those Above, Below, Left and Right options are not enough in my case because I need to squeeze quite a lot of things in my audio mixer.  I suppose I can hide the edit and create my own, and than watch the changes of both and set the other one respectively whenever one of them changes but it seems it could be much simpler...



In other words, are NoTextBox, TextBoxLeft , TextBoxRight , TextBoxAbove and TextBoxBelow the only ways to set the TextBox position? What I would expect is also something like setTextBoxPosition(int x, int y)...

Yes, that is the situation right now.

To get what you want, you could create a Slider with NoTextBox, and then to create a Label, position it where you like, setEditable it if required, and then hook it up to listen to the Slider values (and vice versa). That would be some extra code, but much more flexible.

1 Like

I bet you can fix it up with a careful dance with ComponentListener, getChildComponent and dynamic_cast<> (the slider only has one, the label, as far as I know). 

Or, alternatively you could maybe return a carefully modified slider from createSliderTextBox?

Neither solution highly recommended.

A proper solution would be probably to update the Slider class and split its functionality into more independent/flexibly combinable pieces...


Quite ;-)  Pretend I didn't say anything!

If your textbox does not need a border then you can also override the LookAndFeel as follows:

class MyLookAndFeel : public LookAndFeel_V3
    Label* createSliderTextBox (Slider& slider) override
        Label* l = LookAndFeel_V3::createSliderTextBox (slider);
        l->setBorderSize (BorderSize<int> (2, 2, 40, 2));
        l->setColour (Label::outlineColourId, Colours::transparentWhite );
        l->setColour (Label::outlineWhenEditingColourId, Colours::transparentWhite );
        return l;

and for your slider you do:

slider->setTextBoxStyle (Slider::TextBoxAbove, false, 40, 80);

where the "40" in the LookAndFeel and the 80 in the setTextBoxStyle must be changed appropriately. For a less hacky workaround or if you want the label to draw a border, you can also let createSliderTextBox return a sub-class of Label where the resized method was overridden to leave a space at the bottom or the top.

Timur - or Slider taking two relative rectangle expressions might be a very use-friendly approach.

I've just implemented this for another class, which is going to be trivial to use. 

     Specify two bounds expressions to set the location of the buttons and lamps
     within our multi-select object. 

     The expressions can make use of the ComponentID's "button" and "lamp".
     Passing String::empty will restore the default 
     behaviour, which is placing the lamps above the buttons.
    void setLocations(const String & buttonLocationRectangle,
                      const String & lampLocationRectangle);


Sorry for the late answer I had to put the project aside for a few weeks.


In fact my I do need a border for my Label. I have solved the problem as Timur suggested. I created a Slider witho no textbox and then created a Label at defined position and linked them together. I did this a few weeks ago as nobody seemed to be interested in the thread.


Anyway I was trying to suggest, that this is a common thing to do and might be worth implementing a better support for this in JUCE. I have also found some old thread where Jules said it is on his TODO list but the Label class needs a major overhaul and that he had no time for that at the moment (I doubt this will ever change).

Hi there,

I found a solution. After refactoring the Slider class a little bit, I was able to separate the slider/textbox positioning part from the rest of the slider logic.

There is now a new SliderLayout structure:

    struct SliderLayout
        Rectangle<int> sliderBounds;
        Rectangle<int> textBoxBounds;

This defines the exact position of both the slider area and the textbox area within the Slider component. The code that calculates this position (depending on SliderStyle, TextBoxLeft/Right etc.) is now a part of the LookAndFeel, in the new method getSliderLayout (Slider&). The “default” implementation that produces the Juce sliders as they look and behave now, with all the default styles, is now in LookAndFeel_V2::getSliderLayout. For normal Juce Sliders, this all happens behind the scenes and there is no noticeable change at all.

However, if you want to have a custom layout for your Slider, you can now create your own LookAndFeel and override the getSliderLayout method. There you are responsible to set the sliderBounds and textBoxBounds yourself and return your custom SliderLayout. Here is a simple example:

    class MyLookAndFeel : public LookAndFeel_V3
        Slider::SliderLayout getSliderLayout (Slider&) override
            Slider::SliderLayout layout;
            layout.textBoxBounds = Rectangle<int> (0, 0, 70, 20);
            layout.sliderBounds = Rectangle<int> (8, 30, 284, 30);
            return layout;

If you now do

    slider->setBounds (30, 30, 300, 60);
    slider->setLookAndFeel (&lf);

(with lf being an instance of MyLookAndFeel)
then you’ll get a custom layout slider that looks like this:

In real life you would probably not put hard-coded numbers in there, but rather provide some equations that reflect the custom geometry you want. Please have a look at the default implementation in LookAndFeel_V2::getSliderLayout to see how it works. Note that you have access to the properties of the slider there (style, textBoxPos etc.), so if you like you can also e.g. implement different custom geometries for specific slider styles.

This is on the latest tip now. I hope that it is clear what is going on and that this provides the functionality you want.



Hi Timur,

Those changes were great, but one thing still bothers me : when in incDec mode, there is no way to set custom bounds for the inc/dec buttons. 

Perhaps resizeIncDecButtons() could be moved to the lookAndFeel ?

Sounds like a reasonable change, thanks for the suggestion!

As I have already mentioned in some other recent threads, the Slider class is not ideal and we will probably re-write or re-factor it anyway at some point as soon as we have the time.

1 Like

Opening up an “old” wound :slight_smile:

I also need to customize the position of the textbox for my knobs and sliders. I am trying to follow your above example, but have yet to figure out how to replace your hard coded values, with what is specified in “setbounds”.

In other words, how do I “get” the values that are passed by “setBounds”, inside the customized layout?

Are you talking about the
slider->setBounds(30, 30, 300, 60); ?

or the layout.textBoxBounds = Rectangle<int> (0, 0, 70, 20); ?

slider->getBounds() will return the 30, 30, 300, 60

slider->getSliderLayout() will return the Slider::SliderLayout struct from that snippet. you can then access the public member variables of the layout:



Ok thank you very much.

This code was a huge help. I was able to create this


juce::Slider::SliderLayout RotaryLookAndFeel::getSliderLayout(juce::Slider& slider)


juce::Slider::SliderLayout layout = juce::LookAndFeel_V2::getSliderLayout(slider);

auto localBounds = slider.getLocalBounds();

auto textBoxWidth = layout.textBoxBounds.getWidth();
auto textBoxHeight = layout.textBoxBounds.getHeight();
layout.textBoxBounds.setX((localBounds.getWidth() - textBoxWidth) / 2);
layout.textBoxBounds.setY((localBounds.getHeight() - textBoxHeight) / 2);

return layout;


I combined what I saw in the getSliderLayout() with the code you posted and came up with my result. It’s a small thing for the more experienced, but I’m sure there are some other newbies that might be as challenged as I am when it comes to UI design :slight_smile:

        layout.textBoxBounds.setWidth (textBoxWidth);
        layout.textBoxBounds.setHeight (textBoxHeight);

        if (textBoxPos == Slider::TextBoxLeft)           layout.textBoxBounds.setX (0);
        else if (textBoxPos == Slider::TextBoxRight)     layout.textBoxBounds.setX (localBounds.getWidth() - textBoxWidth);
        else /* above or below -> centre horizontally */ layout.textBoxBounds.setX ((localBounds.getWidth() - textBoxWidth) / 2);

        if (textBoxPos == Slider::TextBoxAbove)          layout.textBoxBounds.setY (0);
        else if (textBoxPos == Slider::TextBoxBelow)     layout.textBoxBounds.setY (localBounds.getHeight() - textBoxHeight);
        else /* left or right -> centre vertically */    layout.textBoxBounds.setY ((localBounds.getHeight() - textBoxHeight) / 2);