Adding removing components in FlexBox

Hi I need to add and remove components dinamicly to/from flexbox. I have strange bahaivior.
Adding is easy and it working but when you try to remove first element and add new one then there is a gap in the layout. It only happend for the first element (removing). For next elemetns after gap when you try to add and remove it everithing is fine (no gap).

Here is an example pic. Aplication is very simple. Slider button +/- is adding or removing component (test group you see 1 to 5 on pic)

Process:

  1. Added 3 Boxes.
  2. Removed 1.
  3. Added 1 (see number 3 in pic after gap)
  4. Added 1 and removing 1 going ok after

I know that I do not supose to remove by size of the array (but by name) but even that it supouse to work.

Also there is a problem sometimes with invoiking the resized()

Code:

Main COMP

#include "MainComponent.h"

//==============================================================================
MainComponent::MainComponent()
{
    addBox.setSliderStyle(juce::Slider::IncDecButtons);
    addBox.setRange(0, 10, 1);
    addBox.setTextBoxStyle(juce::Slider::TextBoxLeft, false, 50, 20);
    addBox.setIncDecButtonsMode(juce::Slider::incDecButtonsDraggable_Vertical);

    addAndMakeVisible(addBox);

    addBox.onValueChange = [this] {
             
        if (counter < addBox.getValue()) {
            DBG("in add");
            holder.push_back(std::make_unique<panel>());        
            arrayFlex.add(juce::FlexItem(200, 300, *holder.back()));
            addAndMakeVisible(*holder.back());
            resized();
        }
        else {
            DBG("in remove");
            removeChildComponent(holder.size());
            arrayFlex.remove(holder.size());
            holder.pop_back();

        }
        counter = addBox.getValue();
    };
    setSize (800, 900);
}
MainComponent::~MainComponent()
{
}
//==============================================================================
void MainComponent::paint (juce::Graphics& g)
{}

void MainComponent::resized()
{
    addBox.setBounds(0, 500, 120, 120);
    fl.flexDirection = juce::FlexBox::Direction::row;
    fl.flexWrap = juce::FlexBox::Wrap::wrap;
    fl.alignContent = juce::FlexBox::AlignContent::flexStart;
    fl.alignItems = juce::FlexBox::AlignItems::flexStart;
    fl.items = arrayFlex;
    
    fl.performLayout(getLocalBounds().toFloat());
}

class MainComponent  : public juce::Component
{
public:

    MainComponent();
    ~MainComponent() override;
    juce::Array<juce::FlexItem> arrayFlex;
    juce::FlexBox fl;
    juce::Slider addBox{};

    int counter = 0;

    void paint (juce::Graphics&) override;
    void resized() override;
    std::vector<std::unique_ptr<panel>> holder;


    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainComponent)
};

Panel is Juce Guigenerated simple component

You’re not calling resized() when you remove the child components so your layout isn’t updating!

Yes I know when you call it it will crash.
This is working after first gapp I dont know why.

I guess because an outdated FlexItem referencing a deleted component is used.
When arrayFlex has 5 items, the last index is 4.
The removeChildCOmponent does nothing (for the same reason), but by deleting it in holder.pop_back() it is deleted automatically. Therefore best remove that removeChildComponent call.

1 Like

Yes! That was the case the index of array. Thank you daniel for good catch. I did not aware that pop_back() will also destroy it from render.

After change it start to working:
arrayFlex.remove(holder.size() -1);

Thanks all

The flexbox system is really designed to be reconstructed from scratch each time. This discussions has come up in the past and complex displays using 100s and 1000s of flexbox elements can be created in no time at all, so managing them in this way is not necessary.

2 Likes

What do you propose instead?

just rebuild the whole structure from scratch each time, using logic to determine what elements are added for display

Is it not rebuild already in resized() ?
Can you give some example?

Sorry, I meant rebuild the FlexBox data itself.

Get rid of your arrayflex and fl members.

then

resized() {
    juce::FlexBox layout;

    for( auto panel : holder ) 
        layout.items.add( juce::FlexItem( panel.get() ).withFlex( 1 ) )

    layout.performLayout( getLocalBounds() );
}

OOO, ok thats make sense. Thank you for that :wink: