TIP: How to include value and label on RotaryKnob without declaring a label!

That being said, I think that the approach presented by @DKDiveDude is useful for different use cases.

For example, I follow a similar approach in my bitmap-based LnF for Sliders to be drawn as knobs: if the LnF finds a certain property of the Slider set to true, it will paint the current frame index in a corner of the Slider itself, which comes handy for debugging purposes

@yfede Exactly.
But not only that, there are other aspects there like the slider style and the size of the label compared to the slider which you might want to change without re-writing the entire paint function.

Not to mention, if you do end up having 50 sliders that are all with the same style, you could just have the one wrapper that sets them up it in the constructor, instead of writing:

Slider s1;
s1.setSliderStyle(Slider::Rotary);

Slider s2;
s2.setSliderStyle(Slider::Rotary);

Much better to have a:

//Constructor takes care of the setup:
RotarySlider s1, s2, s3;

I don’t think using a look and feel and then reflecting on the type is the best idea.
Just have a component that wraps the Slider, and shows that debug information in addition to the slider.

Look and feel should be about things that are common to many types of objects, and not a way to do things to a specific one.

1 Like

you’re correct, my list was partial to serve as example to convey the idea

The fact I omitted for sake of brevity is that that LnF class of mine, does the same for all bitmap-based widget it draws, not just knobs, and since it’s the LnF that knows exactly what frame it has to paint (it serves also as a contained for them) it just made sense to draw that info in paint() considering the fact that it’s only available in DEBUG builds.
It’s not meant for release.

Besides, I often found a real good use of LnF instances that only draw a specific kind of widget, e.g. one for buttons, one for sliders, etc.
Using one single LnF throughout your whole UI as a “skin” works well 90% of the time, but the remaining 10% you really need that widget to be “different” from those in your “default” LnF, and then spawning a new LnF class is easier than customising the existing one based on Component properties etc.

Surely you don’t have 100 separate Slider/knob instances declared manually in your code…? You should use some kind of container object for those.

I actually just do a function, and keep in mind my assembly language skills are much better than my C++ skills, plus I only started with JUCE two months ago.

void ToneGeneratorsComponent::createKnob (Slider& slider, String id, String label, double min, 
                                          double max, double interval, double value, uint8 red, 
                                          uint8 green, uint8 blue, String toolTip)
{
	slider.setComponentID (id);
	slider.getProperties ().set (slider.getComponentID (), label);
	slider.setSliderStyle (Slider::SliderStyle::Rotary);
	slider.setRange (min, max, interval);
	slider.setValue (value);
	slider.setColour (Slider::ColourIds::textBoxTextColourId, Colour::fromRGB (red, 
    green, blue));
	slider.setTextBoxStyle (Slider::TextEntryBoxPosition::NoTextBox, 0, 0, 0);
	slider.setTooltip (toolTip);

	addAndMakeVisible (slider);
}

So calling that like this;

createKnob (levelKnob, "Level", "LEVEL", 0, 1, 0.01f, 0, 255, 128, 0, 
            "Sets volume level of this tone generator");

I think the levelKnob[module] here should be better replaced by simply using slider, right?

that looks like something left over after a refactoring.

Also, you probably gain in clarity by passing a Colour (which data-wise is only an uint32) rather than 3 separate args for red, green and blue, also in consideration of the fact that you use those just to obtain a Colour object inside the function

1 Like

Let me test that, thanks. I am quite comfortable, used to, using separated RGB values which makes me clearly see blend of red, green, and blue, thanks though.

Yes you are right, and that is how I actually got it in my app.

You might consider using a lambda for createKnob. If it is only called from the one place all the knobs are created, then, from an scoping/architectural standpoint, it doesn’t need to be an additional member of the class, but could instead be a lambda in the function where you use it. It’s always good practice to keep your scoping to the limits required by usage.

auto createKnob = [this] (Slider& slider, String id, String label, double min, 
                                          double max, double interval, double value, uint8 red, 
                                          uint8 green, uint8 blue, String toolTip)
{
	slider.setComponentID (id);
	slider.getProperties ().set (slider.getComponentID (), label);
	slider.setSliderStyle (Slider::SliderStyle::Rotary);
	slider.setRange (min, max, interval);
	slider.setValue (value);
	slider.setColour (Slider::ColourIds::textBoxTextColourId, Colour::fromRGB (red, 
    green, blue));
	slider.setTextBoxStyle (Slider::TextEntryBoxPosition::NoTextBox, 0, 0, 0);
	slider.setTooltip (toolTip);

	addAndMakeVisible (slider);
};
2 Likes

I agree with you about the LnF working for a specific type best.

As for debugging all kinds of Components, try this:

template <typename T>
class DebugComponent: public T
{
public:
    template <typename... ARGS>
    DebugComponent(ARGS&&... args): T(std::forward<ARGS>(args)...)
    {
        //needed in case you have custom constructors
    }

    void paint(Graphics& g) override
    {
        //In release builds, only calls the parent paint:
        T::paint(g);

#if JUCE_DEBUG
        //Add frame drawing code here:
        g.setColour(Colours::black);
        g.drawText("Frame", this->getBounds(), Justification::centred);
#endif

    }
};

Use it like this:
DebugComponent<Slider> mySlider;

So you can attach it to any component, no need to add special cases to the LnF for that…

1 Like

I’m going to argue against this, exclusively on xcode, because Xcode doesn’t show you the parameter placeholders for lambdas, but it does for constructors and functions. Maybe Visual Studio is different, but it’s an annoyance in Xcode to not see Lambda parameter placeholders.

DebugComponent(ARGS&& ... args)
You’re gonna want forwarding references here.

Fair enough. Although it’s too bad your code choices are dictated by your IDE. :frowning:

Does Visual Studio show parameter placeholders for lambdas?

How do you mean ‘show’?

Like this:

Xcode won’t show placeholders like that for lambdas that need arguments passed during invocation.

Your lambda suggestion had a TON of arguments.

Good point, I’ll fix my post now…

:frowning:

vs. using a free function:

Does Visual Studio have this problem?

Looks like VS does

1 Like