I am trying to set up a flexible GUI design that is based on FlexBox and allows for two tiers of FlexBox organization - one for all the elements within each “group”, and one for all the groups on the panel. I’ve been stumped on it for over a month.
This is what I have:
This is what I want to have:
The principle is that:
- Every knob should be created as also belonging to a group (or somehow assigned to a group).
- All the knobs that belong to any given group should be automatically added to an array for that group so they can be automatically FlexBoxed within the group.
- MainContentComponent can then add all groups to another array and arrange that with Flexbox so the groups are arranged on the panel.
I cannot figure out the proper way to do this though.
Here is the working code I have that will create the first screenshot in this thread as a single JUCE CPP 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
};
};
//==============================================================================
//MAINCONTENTCOMPONENT
//==============================================================================
class MainContentComponent : public Component
{
public:
MainContentComponent()
{
LabeledSlider *control = new LabeledSlider("Level");
control->slider.setRange(0.0, 1.0);
control->slider.onValueChange = [this] { targetLevel = (float)level.slider.getValue(); };
addAndMakeVisible(knobs.add(control));
control = new LabeledSlider("Dummy1");
addAndMakeVisible(knobs.add(control));
control = new LabeledSlider("Dummy2");
addAndMakeVisible(knobs.add(control));
control = new LabeledSlider("Dummy3");
addAndMakeVisible(knobs.add(control));
control = new LabeledSlider("Dummy4");
addAndMakeVisible(knobs.add(control));
control = new LabeledSlider("Dummy5");
addAndMakeVisible(knobs.add(control));
control = new LabeledSlider("Dummy6");
addAndMakeVisible(knobs.add(control));
control = new LabeledSlider("Dummy7");
addAndMakeVisible(knobs.add(control));
control = new LabeledSlider("Dummy8");
addAndMakeVisible(knobs.add(control));
control = new LabeledSlider("Dummy9");
addAndMakeVisible(knobs.add(control));
setSize(600, 600);
}
~MainContentComponent()
{
// shutdownAudio();
}
void resized() override
{
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 fb;
fb.flexDirection = FlexBox::Direction::column;
fb.items.add(FlexItem(knobBox).withFlex(2.5));
fb.performLayout(getLocalBounds().toFloat());
}
private:
float currentLevel = 0.1f, targetLevel = 0.1f;
LabeledSlider frequency{ "Frequency" };
LabeledSlider level{ "Level" };
LabeledSlider dummy1{ "Dummy 1" };
LabeledSlider dummy2{ "Dummy 2" };
LabeledSlider dummy3{ "Dummy 3" };
LabeledSlider dummy4{ "Dummy 4" };
LabeledSlider dummy5{ "Dummy 5" };
LabeledSlider dummy6{ "Dummy 6" };
LabeledSlider dummy7{ "Dummy 7" };
LabeledSlider dummy8{ "Dummy 8" };
LabeledSlider dummy9{ "Dummy 9" };
OwnedArray<LabeledSlider> knobs;
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)
?