How to make a slider label have rounded corners

i have sliders all with the labels with the values of the sliders, how do i make the labels have rounded corners? please tell me how to implement this as well.
label code -
lpKnob->setTextBoxStyle(Slider::TextBoxBelow, false, 75, 30);

Override the look and feel method(s) for Label using Graphics::drawRoundedRectangle().

https://docs.juce.com/master/tutorial_look_and_feel_customisation.html
https://docs.juce.com/master/classGraphics.html#ac5972cee7e293bf9205cea64cb86233b

thanks, i still dont think i am doing it right, like this?
lpKnob->setLookAndFeel(Graphics::drawRoundedRectangle(Rectangle, 5, 5));

No, that’s not right - I think you should have a look at the tutorial link provided :wink:

In short, this is what you have to do:

  • Create your own LookAndFeel class that inherits from an existing LookAndFeel - preferably LookAndFeel_V4
  • Find the method that is used for drawing the Label in the original LookAndFeel class. Override it and replace the drawing code with your customizations.
  • Create an instance of your own look and feel class
  • Set this instance as look and feel for your lpKnob (and all other knobs you want to look that way)

But the tutorial will give you a lot more details on that process

okay thanks ill have a better look and tell you how it goes.

Instead of changing all Labels, you can also override Slider::LookAndFeelMethods::createSliderTextBox() to return a Label with a LookAndFeel, where you changed Label::LookAndFeelMethods::drawLabel (Graphics &, Label&) to your needs, start off with the original:

HTH

sorry that makes no sense at all to me. i just want to know as simply as possible (with a good explanation) how to make the corners of all of the text boxes created by the sliders, rounded.
i have loaded look and feel v4.
this is example of one of the pieces of code for the sliders.
lpKnob.reset(new Slider(“Low Pass”));
addAndMakeVisible(lpKnob.get());
lpKnob->setSliderStyle(Slider::RotaryHorizontalVerticalDrag);
lpKnob->setTextBoxStyle(Slider::TextBoxBelow, false, 75, 30);
lpKnob->setLookAndFeel(&otherLookAndFeel);

  1. Make a custom look and feel class something like this:
class CustomLAF : public juce::LookAndFeel_v4
{
public:
    void drawLabel(Graphics& g, Label& l) override
    {
        // draw your label here...
        // set up colours etc...
        // then at some point call this...
        g.drawRoundedRectangle(
            0,              // x
            0,              // y
            l.getWidth(),   // width
            l.getHeight(),  // height
            5,              // cornerSize
            2               // lineThickness
        );
    }
};
  1. Make an instance of that class.

  2. Call lpKnob->setLookAndFeel(&otherLookAndFeel);

Maybe explaining it the other way round might help you more.

Think about this: How does any widget know how it should draw itself? Where is the code for the drawing stored? The answer is in the LookAndFeel class. Let’s look a bit under the hood of the framework to understand how it works.

Each slider inherits from Component. And each Component has a private member that points to an instance of a LookAndFeel instance:

But if you create a new Component this points to a nullptr. That’s why there is the member function getLookAndFeel() which scans the component hierarchy if any of the parent components has a special LookAndFeel assigned. If it won’t find any, it will return LookAndFeel::getDefaultLookAndFeel() which will return a static default LookAndFeel instance that exists in any JUCE GUI app. This is the one that describes the default look of any JUCE widget existing - for a current JUCE app this is LookAndFeel_v4 if you haven’t set the default look and feel to something different.

Now let’s take a look how the Slider makes use of this LookAndFeel instance. Every time the slider value is changed a repaint will be triggered which will ultimately call this function:

The result of the getLookAndFeel() call will be passed to this function as the LookAndFeel& lf argument. Inside, this function checks which slider style is selected and then calls LookAndFeel::drawRotarySlider or LookAndFeel::drawLinearSlider. Now you see, it’s up to the LookAndFeel class to describe how this drawing should happen. It will draw everything that makes up a slider as you know it. And if your slider has a label, at some point it will also call LookAndFeel::drawLabel to draw the label.

Now that’s the moment when you want things to happen different. You don’t want the default LookAndFeel_v4 label drawing call to be invoked, instead you want your own custom label drawing call to be invoked. So what can you do to make that happen? Polymorphism in C++ is the answer here. You can simply create your custom version of a look and feel class. As you don’t want everything in this class to be different from what you are used to, you can inherit from the existing look and feel, which is done by declaring the class this way:

class CustomLAF : public LookAndFeel_v4
{

}

At this point CustomLAF is exactly the same as LookAndFeel_v4. As you now want to change one method, you write

class CustomLAF : public LookAndFeel_v4
{
    void drawLabel(Graphics& g, Label& l) override
    {
        // your very own implementation of how a label should be drawn
    }
}

Until now, you only have described your custom look and feel class. To use it, you have to create an actual instance of it somewhere:
CustomLAF myCustomLAF

If you now call lpKnow->setLookAndFeel (&myCustomLAF), lpKnob will invoke your custom look and feel class to do it’s drawing. If it is nearly almost a simple duplicate of LookAndFeel_v4 nearly everything will look the same - except for the label. Here CustomLAF has overrides the drawLabel function which means that this CustomLAF::drawLabel function will be called instead of LookAndFeel_v4::drawLabel.

I hope this made the idea behind this a bit clearer, as I got the impression that this maybe was the missing part of the puzzle for you. Of course you don’t need to study thousands of lines of internal JUCE code to actually make use of the LookAndFeel functionality, but sometimes it helps me to roughly understand how things work in the background to get an idea of how to use some framework features.

One last advice:
Make sure that the lifetime of your own LookAndFeel instance is at least as long as the sliders lifetime. It’s best to make it a member to the class that also holds the slider.

3 Likes

Thanks for the help, a lot of that did go over my head as i am trying to learn c++ and juce atm but it did help. Still cant get it to work, can you just check my code is correct, in the function where there is (Graphics& g, Label& label), do i write the name of the slider I want to effect or do I just leave it as label?

class CustomLAF : juce::LookAndFeel_V4
{
	
public:
	void drawLabel(Graphics& g, Label& label)override {
		g.drawRoundedRectangle(0, 0, 75, 30, 5, 2);
	}
};

public: 
LookAndFeel_V4 otherLookAndFeel;

lpKnob->setLookAndFeel(&otherLookAndFeel), lpKnob;
this doesnt seem to be doing anything to the text boxes and i am unsure where i am going wrong. thanks for the help

That’s because you made an instance of LookAndFeel_V4, not your custom look and feel.
Change LookAndFeel_V4 otherLookAndFeel; to CustomLAF otherLookAndFeel; and you should be good.

@Im_Jimmi found the problem, here some help towards customising the draw code:

It’s best to start off with the original code. You don’t find it in LookAndFeel_V4, which means, it was inherited by a super class. If you look at the definition of the class, you see it inherits LookAndFeel_V3. But bummer, it’s not there either… do it again, until you find it in LookAndFeel_V2

There are two lines to change, the first one and the last one:

void drawLabel (Graphics& g, Label& label) override
{
    // g.fillAll (label.findColour (Label::backgroundColourId));
    g.setColour (label.findColour (Label::backgroundColourId));
    g.fillRoundedRectangle (label.getLocalBounds().toFloat(), 5);

// ...

    // g.drawRect (label.getLocalBounds());
    g.drawRoundedRectangle (label.getLocalBounds().toFloat(), 5, 1);
}
1 Like

ohhh that would make sense, thanks. only thing is now i get these build errors.


“CustomLAF otherLookAndFeel;” is in public so i dont understand what the issue is.

Oh yeah I think that was my bad…
When you inherit from LookAndFeel_v4 you need to add the public keyword…

class CustomLAF : public juce::LookAndFeel_V4
{
    ...
};

I forgot that in my first comment…

You have to inherit using the keyword public. otherwise only th class itself can use the inherited functions:

class CustomLAF : public juce::LookAndFeel_V4

You beat me @Im_Jimmi :slight_smile:

1 Like

right okay that works thanks everyone, now i just need to make it so the boxes are the correct colour and actually have the text in them again aha

Have a look at those two links I provided… They should help you get started with changing colours and adding text!

Does that mean I get to be an admin too now? :wink:

Haha :slight_smile:

right im having a look on how to write the text value of the slider like it did before, im looking through the documentation but still cant get used to it, is it something to do with g.drawFittedText(); ?

This is the code used in LookAndFeel_v2 for drawing labels…

void drawLabel (Graphics& g, Label& label)
{
    g.fillAll (label.findColour (Label::backgroundColourId));

    if (! label.isBeingEdited())
    {
        auto alpha = label.isEnabled() ? 1.0f : 0.5f;
        const Font font (getLabelFont (label));

        g.setColour (label.findColour (Label::textColourId).withMultipliedAlpha (alpha));
        g.setFont (font);

        Rectangle<int> textArea (label.getBorderSize().subtractedFrom (label.getLocalBounds()));

        g.drawFittedText (label.getText(), textArea, label.getJustificationType(),
                          jmax (1, (int) (textArea.getHeight() / font.getHeight())),
                          label.getMinimumHorizontalScale());

        g.setColour (label.findColour (Label::outlineColourId).withMultipliedAlpha (alpha));
    }
    else if (label.isEnabled())
    {
        g.setColour (label.findColour (Label::outlineColourId));
    }

    g.drawRect (label.getLocalBounds());
}

If you want yours to work the same just with rounded corners then copy this all exactly but replace the two lines that @daniel suggested: