Passing data or events to a parent component


#1

I have a Parent component.
Within that is a Child component, it’s full screen, I think of it like being a page.
The Child contains a button.

User clicks the button, which calls a function within my Child component, sets a variable.

What’s the best way to pass that variable, and the fact that the button has been clicked, back to the parent?

In my case, after clicking the button within the Child, I want the parent to destroy the Child and load a new different Child in its place, but keep the variable that was set in the Child.

Many thanks!


#2
struct Child : public Component
{
    void resized() override { b.setBounds(getLocalBounds()); }
    Button b;
    int data;
};

struct Parent : public Component
{
    std::unique_ptr<Child> child;
    void resized() override 
    { 
         if( child.get() ) child->setBounds(getLocalBounds() ); 
    }

    void changeChild(Child*, int);

    Parent();
};
Parent::Parent()
{
    changeChild( new Child(), 3 );
}

void Parent::changeChild(Child* ch, int data)
{
    if( ch == nullptr ) { child = std::make_unique<Child>(); }
    else                { child.reset( ch ); }

    child->b.onClick = [this]() 
    { 
        //do stuff with child first
        changeChild(nullptr, child->data); //now alert parent
    };
    child->data = data;
    addAndMakeVisible(child.get());
}

untested, but you get the idea. consider it a starting point…


#3

As always there are more than one way to skin a cat (not a kat though! :joy:).

To notify the parent of the click and pass data you could:

have a std::function<void(int data)> childClicked defined publicly in the child class which you set from the parent like so :

childComponent.childClicked = [&](int data) { // store your variable and change the child };

(or if you want to keep it private, have a setter function for childClicked in the child class)

Or, you could make the child component also inherit ChangeBroadcaster and then have it sendChangeMessage() when the button is clicked. The parent would inherit also ChangeListener, register itself as a listener of the child, and then in it’s changeMessageCallback you could call some getter function on the child to retrieve the data you needed (or put the data you needed available public if not that bothered about “best practises”!) and then swap the child component for new one.

Of those two the 2nd option is probably better; have the parent be a simple state machine so it knows which child component to change to in the changeMessageCallback (especially if you’re going to have more than 2 “pages” that you want to cycle through).

Advice may or may not be good, but those are some extra options available to you. :slight_smile:


#4

it’s probably best to just show a complete working snippet with a suggestion as complex as that. It confused me lol


#5

Did come with a warranty about potentially not being good advice! :joy:

The ChangeBroadcaster/Listener case:

<Parent.h>

class Parent : public Component, ChangeListener
{
public:
    Parent();
    ~Parent();
    void changeListenerCallback(ChangeBroadcaster* source);
    // other public stuff for the parent
private:
    ChildA childComponentA;
    ChildB childComponentB;
    int saveVariableFromChildA;
    // other private stuff for the parent
}

<ChildA.h>

class ChildA : public Component, ChangeBroadcaster
{
public:
    // other public stuff for the child
    int getVariable();
private:
    int variable;
    // other private stuff for the child
}

<Parent.cpp>

Parent::Parent()
{
    childComponentA.addListener(this);
    childComponentB.addListener(this);
    // the rest of Parent constructor
}

Parent::~Parent()
{
    childComponentA.removeListener(this);
    childComponentB.removeListener(this);
    // the rest of Parent destructor
}

Parent::changeListenerCallback(ChangeBroadcaster* source)
{
    if(source == &childComponentA)
    {
        saveVariableFromChildA = childComponentA.getVariable();
        // now remove ChildA and replace with the other
    }
    else if(source == &childComponentB)
    {
        // react to message from B child
    }
    // etc.
}

<ChildA.cpp>
// I don't think I need to explain how to write a getter ;)

In the child components, calling sendChangeMessage(); will trigger the callback in the parent.

I think for the use case given, the std::function callback option would be messy, so I won’t explain that one, but looking at how onClick in a Button is implemented will give some clues :slight_smile:

Again, comes with no warranty, but I think I got it right. :joy:


#6

Thank you all very much! I ended up using this method…

have a std::function<void(int data)> childClicked defined publicly in the child class which you set from the parent like so :

childComponent.childClicked = [&](int data) { // store your variable and change the child };

This forum is always super helpful, I’ve yet to have a question unanswered.