maybe the question is a little trivial, but my c++ skills are pretty rough at the moment.
I’m trying to create a dynamic 2D array of TextButtons (or any other button) and fill it with TextButton instances and just can’t get it to work.
Which container type is recommended for this and what would the syntax look like? I already tried with std::vector and with juce::OwnedArray but I fail with both.
Looks like your problem is in the inner for loop. You create the TextButton and assign it to newTextButton, but you don’t actually add that button to your OwnedArray. Instead, you add a reference to a default-constructed temporary (which is a bad idea!):
After calling addAndMakeVisible(), you need to call setBounds() on each button in order to set their position. This is often done in the container component’s resized() method. For example:
// Inside your Component class that contains the rowButtons:
void resized() override
{
auto bounds = getLocalBounds();
for (auto* button : rowButtons)
button->setBounds (bounds.removeFromTop (50));
}
@connorreviere
I already did this in the creation loop and also tried your suggestion but it didn’t make a difference. It seems like in the creation loop the reference to the created TextButtons is lost. E.g. after 2 rows I see this in the debugger:
Again sorry for my lack of knowledge regarding modern c++, I had lived the last years in the somewhat friendly python world (and the not so friendly java world), but when it comes to real-time audio there seems to be no way around c++.
You’re passing a raw pointer to a local variable. At the end of the for-loop, rowButtons is deleted so that pointer you added to stepCoreButtons is null.
Instead you should do something like:
auto rowButtons = std::make_unique<juce::OwnedArray<juce::TextButton>> ();
// ...
stepCoreButtons.add (std::move (rowButtons));
I finally made it work by switching from OwnedArrays to vector<unique_ptr>> like this:
stepCoreGridComponent::stepCoreGridComponent(int nColumns, int nRows)
: coreGridDimensions(nColumns, nRows)
{
for (int row = 0; row < coreGridDimensions.second; row++)
{
std::vector<std::unique_ptr<juce::TextButton>> rowButtons;
for (int column = 0; column < coreGridDimensions.first; column++)
{
auto newTextButton = std::make_unique<juce::TextButton>("test");
newTextButton->setBounds(column * 100, row * 100, 100, 100);
addAndMakeVisible(*newTextButton);
rowButtons.push_back(std::move(newTextButton));
}
stepCoreButtons.push_back(std::move(rowButtons));
}
}
are there any strong reasons to use an OwnedArray and not use a vector?
juce::OwnedArray is functionally identical to std::vector<std::unique_ptr>. Internally juce::OwnedArray using a juce::Array<std::unique_ptr>.
If you’re more comfortable using the vector approach then go for that - the biggest advantage I’ve found to using JUCE’s arrays is that it’s much easier to remove items than with STL containers.