I am trying to convert some complex Reaktor modal synthesizers I have built over to Juce. The overwhelming majority of the controls I need are simple rotary sliders. Although my synths are near complete they are not completely so (was running out of resources in Reaktor due to its inefficiency so couldn’t finish them). So this is still in the “90% done prototyping” phase.
This means I want flexibility and simplicity in my layout method.
Basically all I need is the simplest way to let 50 or 100 knobs be aligned and not overlapping on a given project size.
I’d like to be able ideally to just let them be generated from left to right in rows based on their order in the project so it’s very easy/brainless to reorganize them while testing.
Would the Grid function allow me to do this? I’m not worrying about “flipping the orientation” like FlexBox seems designed for. I just want the simplest way to get some knobs laid out on the screen, and the most flexible way to reorder them once they’re there.
But perhaps FlexBox is best, because for prototyping, I could even just keep everything on one “row” and let it wrap it into columns for me?
What I don’t get in particular about the FlexBox tutorial is all the knobs and sliders were generated by “for” loops, which is fine if they’re just dummy knobs and sliders that don’t each need their own properties and ranges etc. But that is not useful.
How do you make a FlexBox with actual real knobs in it, where each one needs its own specifications (range, default, label, etc.)?
I don’t understand how to get from the tutorial with these pointless auto-generated dummy knobs/sliders to using the same FlexBox with REAL knobs/sliders.
The loops in the tutorial are only creating the FlexItems, that control the layout.
The knobs are already in the array, so you are expected to have them already set up with parameters and connections like SliderAttachments.
However I would agree, that a knob without description is not very useful to be layouted automatically. I usually have a custom GroupComponent with the parameter name that aggregates a Slider to be displayed inside.
It makes a flexbox called fb with three flexitems, that are then added to an array so the flex box can organize them.
But I don’t understand how this works with the knobs and sliders. I don’t see the same method used. It’s just for loops. There’s no other “addarray” anywhere else. And I don’t even see the knobs/sliders defined anywhere except as (I think) auto-generated by for loops.
for (int i = 0; i < 6; ++i)
auto* slider = new Slider();
slider->setTextBoxStyle (Slider::NoTextBox, true, 0, 0);
addAndMakeVisible (knobs.add (slider));
As I said I think this is useless from a demonstration purpose because you never just want to generate 6 random sliders with nothing specific about any of them.
So let’s say in the simplest form, I want to individually define ~30-50 knobs each with parameters each like this:
And then I want to put those in a simple array and have it automatically assign each one an equal width/height and wrap it into rows, how would I do that?
I think that would make a much more useful application of the tutorial, even if it was demonstrated with just 3-5 knobs, since that’s what we actually have to try to do in real life.
I think I can do this by just using the same method as the panels above by manually defining each knob as a flex item (but with what width/height?) and then manually entering them to an array in the same way. But with all the “for” loops in the tutorial I’m not sure if this is the most efficient or correct way.
Thanks for your critique on the tutorial. The reason, why this is not covered here is probably, because this way it easier to focus on the layout, since adding the said functionality would add a considerable amount of code, that would dilute the topic.
For your purpose, you could simply remove that loop, and instead add your sliders to the knobs array (make sure to either use an OwnedArray and create the sliders as pointers with new, or using a simple Array, that doesn’t own the sliders):
// in editor constructor, assuming you have access to the AudioProcessorValueTreeState
auto& state = processor.getTreeState();
for (auto* param : processor.getParameters())
if (auto* paramWithID = dynamic_cast<AudioProcessorParameterWithID*> (param))
knobs.add (new AnnotatedSlider (state, paramWithID->paramID));
I think I understand the principle of what you’re suggesting:
Define a new class called “AnnotatedSlider” which inherits from GroupComponent to represent a unit containing both a slider and a label linked to that slider.
Define my knobs from this “AnnotatedSlider” class.
Create an Array called “knobs” and add to it one by one all the AnnotatedSlider elements.
Define a FlexBox and use a range-based for loop to create a FlexItem for each member of the “knobs” array.
Let the FlexBox “performLayout” of these FlexItems and thus organize the knobs and their attached labels.
However, I cannot seem to understand how to put this together. For example, I just tried copying your class declaration for “AnnotatedSlider” into the existing FlexBox tutorial, and I get errors just from that alone:
I really wish there could be a tutorial on this, since I gather from your posts that this is what we ought to be doing in almost any real GUI design situation.
If this is the proper modern way to make and organize labels/sliders in a JUCE design, isn’t this an essential skill we should all be getting in a tutorial? Doesn’t almost every design need to organize labelled sliders? And isn’t FlexBox the best approach?
Is there any chance you could write something simple that would demonstrate this with 2-3 knobs so I can see how it would work?
It would just be helpful to get something working so I can see how it’s supposed to come together. Without ever seeing an example, I can’t find enough information to deduce how it should be done. I would be very willing to PayPal you for your time if needed.
I wish there were books on JUCE so I didn’t have to ask these sorts of things because I’m not opposed to reading or doing work, but I feel like there’s just not enough info out there on this type of practical usage.
I just want the simplest way to get some knobs laid out on the screen, and the most flexible way to reorder them once they’re there.
I find FlexBox a pain. Here’s some layout code for a grid you could modify. This sets out a bunch of drum pads in a 4x4 grid. But you could just play with the numbers to do whatever you want. It make a box ‘b’ that is the size of the thing you are laying out and then just left-to-right lays them out till it’s got a row, then translates the box to the start of the next row.
void resized() override
auto b = getLocalBounds();
auto bh = b.getHeight() / 4;
auto bw = b.getWidth() / 4;
for (int i = 0; i < pads.size(); ++i)
if (i % 4 == 3) // i.e. we have just put the last item on the row.
In case it’s any use, this is what I use for temporary labelling of knobs when prototyping. It attaches a label to a component, displays it, positions it with one line of code, and deletes it when the component is deleted. I think it might have been inspired by something Dave said …
class JLabel : public Component, ComponentListener, public ReferenceCountedObject
/** Create a new JLabel and attached it as a reference counted object to the provided components properties. */
static JLabel* createAndAttach (Component* component, const String& text)
auto label = new JLabel (component, text);
component->getProperties().set ("_label_object", label);
void paint (Graphics& g) override
Font f (float (getHeight()), Font::FontStyleFlags::bold);
g.setColour (Colour::greyLevel (0.8f));
g.drawText (text.toUpperCase(), getLocalBounds(), Justification::centred, false);
auto b = componentToAttachTo->getBounds();
auto padding = 3;
setBounds (b.withHeight (12).withY (b.getBottom() + padding));
auto parent = componentToAttachTo->getParentComponent();
void componentMovedOrResized (Component&, bool, bool) override
void componentParentHierarchyChanged (Component&) override
void componentVisibilityChanged (Component&) override
JLabel (Component* componentToAttachTo, const String& text) : componentToAttachTo (componentToAttachTo), text (text)
It couldn’t get any easy without being telepathic.