My primary objective for a while now has been to be able to create a nested system of Flexboxes and GroupComponent to create a flexible panel system. I’ve gotten reasonably close.
This was my last working code (which can be loaded as a standalone main.cpp for a Juce project):
Link to Github of file
/*
==============================================================================
This file was auto-generated and contains the startup code for a PIP.
==============================================================================
*/
#include "../JuceLibraryCode/JuceHeader.h"
///============================
///BEGINNING OF USUAL PROJECT HEADER
#pragma once
class LabeledSlider : public GroupComponent
{
public:
LabeledSlider(const String& name)
{
setText(name);
setTextLabelPosition(Justification::centredTop);
addAndMakeVisible(slider);
}
void resized() override
{
slider.setBounds(getLocalBounds().reduced(10));
}
Slider slider
{
Slider::RotaryHorizontalVerticalDrag, Slider::TextBoxBelow
};
};
class LabeledGroup : public GroupComponent
{
public:
LabeledGroup(const String& name)
{
setText(name);
setTextLabelPosition(Justification::centredTop);
addAndMakeVisible(dummy1);
addAndMakeVisible(dummy2);
addAndMakeVisible(dummy3);
addAndMakeVisible(dummy4);
}
void resized() override
{
//setBounds(getLocalBounds().reduced(10));
FlexBox knobBox1;
knobBox1.flexWrap = FlexBox::Wrap::wrap;
knobBox1.justifyContent = FlexBox::JustifyContent::flexStart;
knobBox1.alignContent = FlexBox::AlignContent::flexStart;
Array<LabeledSlider*> knobs1;
knobs1.add(&dummy1, &dummy2, &dummy3, &dummy4);
for (auto *k : knobs1)
knobBox1.items.add(FlexItem(*k).withMinHeight(80.0f).withMinWidth(80.0f).withFlex(1));
FlexBox fb1;
fb1.flexDirection = FlexBox::Direction::column;
fb1.items.add(FlexItem(knobBox1).withFlex(2.5));
fb1.performLayout(getLocalBounds().reduced(15).toFloat());
}
private:
LabeledSlider dummy1{ "Dummy 1" };
LabeledSlider dummy2{ "Dummy 2" };
LabeledSlider dummy3{ "Dummy 3" };
LabeledSlider dummy4{ "Dummy 4" };
};
//==============================================================================
//MAINCONTENTCOMPONENT
//==============================================================================
class MainContentComponent : public Component
{
public:
MainContentComponent()
{
addAndMakeVisible(group1);
addAndMakeVisible(group2);
level.slider.setRange(0.0, 1.0);
level.slider.setNumDecimalPlacesToDisplay(1);
level.slider.onValueChange = [this] { targetLevel = (float)level.slider.getValue(); };
addAndMakeVisible(level);
setSize(600, 600);
}
~MainContentComponent()
{
// shutdownAudio();
}
void resized() override
{
//group1.setBounds(getLocalBounds().expanded(10));
//group2.setBounds(getLocalBounds().expanded(10));
FlexBox groupBox1;
groupBox1.flexWrap = FlexBox::Wrap::wrap;
groupBox1.justifyContent = FlexBox::JustifyContent::flexStart;
groupBox1.alignContent = FlexBox::AlignContent::flexStart;
Array<LabeledGroup*> groups1array;
groups1array.add(&group1, &group2);
for (auto *g : groups1array)
groupBox1.items.add(FlexItem(*g).withMinHeight(100.0f).withMinWidth(350.0f).withFlex(3).withMargin(5));
FlexBox fb1;
fb1.flexDirection = FlexBox::Direction::column;
fb1.items.add(FlexItem(groupBox1).withFlex(3));
fb1.performLayout(getLocalBounds().toFloat());
}
private:
float currentLevel = 0.1f, targetLevel = 0.1f;
LabeledGroup group1{ "Group 1" };
LabeledGroup group2{ "Group 2" };
LabeledSlider level{ "Level" };
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MainContentComponent)
};
///============================
///BEGINNING OF USUAL MAIN.CPP
class Application : public JUCEApplication
{
public:
Application() {}
const String getApplicationName() override { return "SineSynthTutorial"; }
const String getApplicationVersion() override { return "3.0.0"; }
void initialise(const String&) override { mainWindow.reset(new MainWindow("SineSynthTutorial", new MainContentComponent(), *this)); }
void shutdown() override { mainWindow = nullptr; }
private:
class MainWindow : public DocumentWindow
{
public:
MainWindow(const String& name, Component* c, JUCEApplication& a)
: DocumentWindow(name, Desktop::getInstance().getDefaultLookAndFeel()
.findColour(ResizableWindow::backgroundColourId),
DocumentWindow::allButtons),
app(a)
{
setUsingNativeTitleBar(true);
setContentOwned(c, true);
#if JUCE_ANDROID || JUCE_IOS
setFullScreen(true);
#else
setResizable(true, false);
setResizeLimits(300, 250, 10000, 10000);
centreWithSize(getWidth(), getHeight());
#endif
setVisible(true);
}
void closeButtonPressed() override
{
app.systemRequestedQuit();
}
private:
JUCEApplication & app;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MainWindow)
};
std::unique_ptr<MainWindow> mainWindow;
};
//==============================================================================
START_JUCE_APPLICATION(Application)
@daniel helped come up with a lot of the original code I used there. It works but I had to cheat in the coding and hardcode a lot of parameters into the class definitions (as I still lack skill with C++), so it’s not yet a final working solution.
I chatted with @Matkatmusic on Discord who helped offer some ideas and code for how to put it together in a working format. This is the best I’ve been able to compose from that:
/*
==============================================================================
This file was auto-generated and contains the startup code for a PIP.
==============================================================================
*/
#include "../JuceLibraryCode/JuceHeader.h"
//===========================
//USUAL PROJECT HEADER
//===========================
#pragma once
//===LABELEDSLIDER===//
class LabeledSlider : public GroupComponent
{
public:
LabeledSlider(const String& name, Slider::SliderStyle style, Slider::TextEntryBoxPosition textPos)
{
setText(name);
setTextLabelPosition(Justification::centredTop);
slider.reset(new Slider(style, textPos));
addAndMakeVisible(slider.get());
}
/*
void setSliderRange(Range<float> r)
{
slider->setRange(r);
void setSliderDecimal(int num) { slider->setNumDecimalPlacesToDisplay(num); }
void setOnValueChange(std::function<void()> func) { slider->onValueChange = std::move(func); }
}
*/
void resized() override
{
slider.get()->setBounds(getLocalBounds().reduced(10));
}
private:
std::unique_ptr<Slider> slider;
};
//===LABELEDGROUP===//
class LabeledGroup : public GroupComponent
{
public:
LabeledGroup(const String& name, Array<LabeledSlider*> sliderArray) //: sliders(sliderArray)
{
std::unique_ptr<LabeledGroup> group;
setText(name);
setTextLabelPosition(Justification::centredTop);
for (auto* slider : sliders)
{
addAndMakeVisible(slider);
}
}
void resized() override
{
setBounds(getLocalBounds().reduced(10));
FlexBox knobBox;
knobBox.flexWrap = FlexBox::Wrap::wrap;
knobBox.justifyContent = FlexBox::JustifyContent::flexStart;
knobBox.alignContent = FlexBox::AlignContent::flexStart;
for (auto *k : knobs)
knobBox.items.add(FlexItem(*k).withMinHeight(80.0f).withMinWidth(80.0f).withFlex(1));
FlexBox fb1;
fb1.flexDirection = FlexBox::Direction::column;
fb1.items.add(FlexItem(knobBox).withFlex(2.5));
fb1.performLayout(getLocalBounds().reduced(15).toFloat());
}
private:
OwnedArray<LabeledSlider*> sliders;
OwnedArray<LabeledSlider> knobs;
};
//====================
//MAINCONTENTCOMPONENT
//====================
class MainContentComponent : public Component
{
public:
MainContentComponent()
{
/*
auto* attackLS = sliderObjects.add(new LabeledSlider("Attack", Slider::RotaryHorizontalVerticalDrag, Slider::TextBoxBelow));
slider->setRange(0, 3);
slider->setNumDecimal(1);
slider->setOnValueChange(5);
auto* decayLS = sliderObjects.add(new LabeledSlider("Decay", Slider::RotaryHorizontalVerticalDrag, Slider::TextBoxBelow));
slider->setRange(0, 3);
slider->setNumDecimal(1);
slider->setOnValueChange(5);
auto* releaseLS = sliderObjects.add(new LabeledSlider("Release", Slider::RotaryHorizontalVerticalDrag, Slider::TextBoxBelow));
slider->setRange(0, 3);
slider->setNumDecimal(1);
slider->setOnValueChange(5);
Array<LabeledSlider*> envelope = { attackLS, decayLS, releaseLS };
*/
Array<LabeledSlider*> group1array =
{
sliderObjects.add(new LabeledSlider("attack",Slider::RotaryHorizontalVerticalDrag, Slider::TextBoxBelow)),
sliderObjects.add(new LabeledSlider("decay", Slider::RotaryHorizontalVerticalDrag, Slider::TextBoxBelow)),
sliderObjects.add(new LabeledSlider("sustain", Slider::RotaryHorizontalVerticalDrag, Slider::TextBoxBelow)),
sliderObjects.add(new LabeledSlider("release", Slider::RotaryHorizontalVerticalDrag, Slider::TextBoxBelow))
};
LabeledGroup group1("My Group", group1array);
//auto group1 = std::make_unique<LabeledGroup>("Group1", {});
std::unique_ptr<LabeledGroup> group1;
std::unique_ptr<LabeledGroup> group2;
setSize(600, 600);
}
void resized() override
{
//group1.setBounds(getLocalBounds().expanded(10));
//group2.setBounds(getLocalBounds().expanded(10));
FlexBox groupBox;
groupBox.flexWrap = FlexBox::Wrap::wrap;
groupBox.justifyContent = FlexBox::JustifyContent::flexStart;
groupBox.alignContent = FlexBox::AlignContent::flexStart;
for (auto *g : groups)
groupBox.items.add(FlexItem(*g).withMinHeight(100.0f).withMinWidth(350.0f).withFlex(3).withMargin(5));
FlexBox fb1;
fb1.flexDirection = FlexBox::Direction::column;
fb1.items.add(FlexItem(groupBox).withFlex(3));
fb1.performLayout(getLocalBounds().toFloat());
}
~MainContentComponent()
{
// shutdownAudio();
}
private:
OwnedArray < LabeledGroup > groups;
std::unique_ptr<LabeledGroup> group1;
OwnedArray<LabeledSlider> sliderObjects;
};
//=====================
//USUAL MAIN.CPP
//=====================
class Application : public JUCEApplication
{
public:
Application() {}
const String getApplicationName() override { return "SineSynthTutorial"; }
const String getApplicationVersion() override { return "3.0.0"; }
void initialise(const String&) override { mainWindow.reset(new MainWindow("SineSynthTutorial", new MainContentComponent(), *this)); }
void shutdown() override { mainWindow = nullptr; }
private:
class MainWindow : public DocumentWindow
{
public:
MainWindow(const String& name, Component* c, JUCEApplication& a)
: DocumentWindow(name, Desktop::getInstance().getDefaultLookAndFeel()
.findColour(ResizableWindow::backgroundColourId),
DocumentWindow::allButtons),
app(a)
{
setUsingNativeTitleBar(true);
setContentOwned(c, true);
#if JUCE_ANDROID || JUCE_IOS
setFullScreen(true);
#else
setResizable(true, false);
setResizeLimits(300, 250, 10000, 10000);
centreWithSize(getWidth(), getHeight());
#endif
setVisible(true);
}
void closeButtonPressed() override
{
app.systemRequestedQuit();
}
private:
JUCEApplication & app;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MainWindow)
};
std::unique_ptr<MainWindow> mainWindow;
};
//==============================================================================
START_JUCE_APPLICATION(Application)
It’s only actually giving one error in Visual Studio now:
I’m not sure how to fix this error. Anyone know how to fix this or make it work? Any further suggestions?
Thanks


