How to make a parent component a listener to its children?

Sorry, i know this is pretty much a deficiency in my c++ knowledge, but i'm stuck here:

 

I've got a GUI component class "ParentGui", and inside it are 12 child components consisting of sliders and labels. 

so, the chain looks like:  parentGui -> childComponent -> childSlider.

in my child component i have this:
 


void ChildComponent::sliderValueChanged (Slider* sliderThatWasMoved)
{
    //[UsersliderValueChanged_Pre]
    //[/UsersliderValueChanged_Pre]
    if (sliderThatWasMoved == childSlider)
    {
        //[UserSliderCode_childSlider] -- add your slider handling code here..
        //[/UserSliderCode_childSlider]
    }
}

but, i want to process all 12 sliders in the parent.

I tried making the parent a SliderListener,  but couldn't figure out how to make that work. 

any ideas???

any help appreciated!

If your problem is, that the "Slider* childSlider" is unknown in the parentGui, try using a Component::getComponentID(), so you can identify which slider was moved (the Slider IS a Component). Simply set a ID when the slider is created with childSlider.setComponentID("any name").

The rest is straight forward, inherit parentGui from SliderListener, in the childComponent constructor hand over a backlink to the parentGui and register the listener with "childSlider.addListener (parentGui);" there.

But maybe there are other ideas out there...

1 Like

Once you create in your parent component the children you must do the parent component a listener. Example:

class ParentComponent: public Component, public Slider::Listener
{
public:

ParentComponent()
{
childComponent1 = new Slider();
childComponent1->addListener(this);
addAndMakeVisible(childComponent1);


childComponent2 = new Slider();
childComponent2->addListener(this);
addAndMakeVisible(childComponent2);
}

virtual void sliderValueChanged (Slider *slider)
{
if (slider == childComponent1)
{
// do your stuff for slider 1
}
else if (slider == childComponent2)
{
// same for slider 2
}

}


ScopedPointer<Slider> childComponent1;
ScopedPointer<Slider> childComponent2;

};

 

 

>childSlider.setComponentID("any name")

 

yep, that was the bit i missed, i reckon.  cheers!

1 Like

hmmm, still not working.  

 

problem seems to be that my child component contains a slider and a label.  so the component itself is not actually a slider. 

 

i'm still not getting this.

 

i have a parent component with 12 child components.

the child components each have a slider and a label.

 

i want the parent component to access the child's child (ie. grandchild)
"if (sliderThatWasMoved == childSlider)" 

 

i know it's something pretty obvious i'm missing, but i can't work it out. 

it's this accessing a grandchild component that i can't work out.  

pleeeassse!  

 

the problem is that my child component is NOT just a slider.  it's a component, consisting of a slider and a label. 

os, i actually want to access the child of the child (ie, the grandchild)

 

this is making me crazy!

Perhaps it could be as simple as making your slider public in the child Component and adding the parent as a Listener in its constructor? So something like this:

child.slider.addListener(this)

Or in your child Component you could provide a function that returns a pointer (or possibly a reference) and basically do the same as above, but just replace "slider" with getSlider(). Just an idea. 

By the way, I think you should be using Slider::Listener instead of SliderListener because the second is deprecated. Although, it is just an alias of the first.

Ok, in code it's easier to understand:

class ChildComponent : public Component
{
public:
    ChildComponent (SliderListener* parent, const String name)
    {
        mySlider.addListener (parent);
        mySlider.setComponentID(name);
        addAndMakeVisible(mySlider);
    }
private:
    Slider mySlider;
    
};

class ParentComponent : public Component, public SliderListener
{
public:
    ParentComponent()
    {
        for (int i=0; i<12; ++i) {
            ChildComponent* child = children.add(new ChildComponent(this, String (i)));
            addAndMakeVisible(child);
        }
    }
    ~ParentComponent() {}
    
    void sliderValueChanged (Slider* sliderThatWasMoved)
    {
        String name = sliderThatWasMoved->getComponentID();
        // do something
    }
private:
    OwnedArray<ChildComponent> children;
};

Hope that helps...

3 Likes

Or ... 


class Child 
{
public:
    Value & getSliderValueObject() { return slider.getValueObject(); }
private:
    Slider slider;
};

And then work with the Value object?  Potentially having a matching one in your parent class and using Slider::referTo(...) 

It sounds like you need to learn to use pointers… The parent needs a pointer to the Slider and the child class needs a pointer to the parent so it can m_yourSlider.addListener (pParent)

This is basic C.

Rail

Couldn't you just do something like this in your child Component too?

slider.addListener(getParentComponent())

I'd much rather do it another way, but that would be an easy way to do it. Obviously, JUCE provides quite a few ways to do something like this. :)

1 Like

That won't work, at least not in the constructor of the ChildComponent, because it is not added to a parent component yet:

"Component* Component::getParentComponent ( ) const : [...] If this is the highest-level component or hasn't yet been added to a parent, this will return null. "

The parent component is set when calling addAndMakeVisible in the parent.

But yes, a lot ways to Rome...

Ahh I see, that makes sense. I didn't think about that. I usually have to figure things like that out the hard way so I don't think I've ever tried using getParentComponent() in the child's constructor before. Thanks for telling me though, so hopefully I won't ever make that mistake.  Yeah I didn't think about the parent being set with the add methods, but again, that makes a lot of sense.

Hi,  

 

this matter has been discussed in different threads but i can't implement it yet. I have tried a lot of thigs with no success.

The thing is if I have a frame, that contains another frame that contains for instance, 2 sliders...how the hell the upper frame gets the callbacks from the slider events??(slidervaluechange, for instance?)

 

I attached a simple example I am trying to implement....i feel myself fool blush

 

I can't get the slidervaluechange callback in the top. I highly appreciate help with this issue. Maybe it is a c++ misunderstanding or whatever, but i can't solve it.

//************************************************************************TOP.h
class GUI  : public Component,
                    private Slider::Listener,
{

public:
    GUI ();
    ~GUI();

void sliderValueChanged(Slider* sliderThatWasMoved) override;

private:
    ScopedPointer<faders> component;

}

GUI (TOP).cpp

//************************************************************************TOP.cpp
GUI::GUI ()
{

    addAndMakeVisible (component = new faders());
    addAndMakeVisible (component2 = new faders());
    addAndMakeVisible (component3 = new buttons());

 

//Here I have tried lot of methods as...and more.

SOLUTION: component->slider->addListener(this); //if child public, else write a public method to return that pointer.

//my error was trying component->addListener(this) or addCOmponentListener(component) or so.  I tried to make the entire child listener instead of single components.

}

 

//I cant't get this callback called!

void GUI::sliderValueChanged(Slider* sliderThatWasMoved)
{
}

 

//*******************************************************children.h
class faders : public Component,
    public SliderListener
    //public Slider
{
public:

    faders ();
    ~faders();
    ScopedPointer<Slider> slider; (i have tried both public and private)
    ScopedPointer<Slider> slider2;

}

//**********************************************************children.cpp
faders::faders ()
{
 
    addAndMakeVisible (slider = new Slider ("new slider"));
    slider->setRange (0, 10, 0);
    slider->setSliderStyle (Slider::LinearHorizontal);
    slider->setTextBoxStyle (Slider::TextBoxLeft, false, 80, 20);
    slider->addListener (this);


    addAndMakeVisible (slider2 = new Slider ("new slider"));
    slider2->setRange (0, 10, 0);
    slider2->setSliderStyle (Slider::LinearHorizontal);
    slider2->setTextBoxStyle (Slider::TextBoxLeft, false, 80, 20);
    slider2->addListener (this);

 

//Here I have tried lot of methods as...and more.

slider->addListener(getParentComponent()); //get error...

 

}


void faders::sliderValueChanged (Slider* sliderThatWasMoved)
{

    if (sliderThatWasMoved == slider)
    {

    }
    else if (sliderThatWasMoved == slider2)
    {
 
    }

}

 

passing in lambdas or pointers to the parent component with a forward declaration is my preference if i need to access a lot of things, similar to how your processor is passed into your editor in the constructor.

 

in this instance though, passing "this" into the constructor of the objects, and setting addlistener to the parent pointer seems clean.

I ve got troubles now.

If I use component->slider->addListener(this); to get the event fromchildren....these have to be declared as public in their own "layer" (juce declares gui components as private....as i guess it should.

On the other hand, by  passing "this"(child object) from the child to the parent to add it as listener and get the notification (in the case of a slider for instance sliderchangevalue()), the parent takes the control over that child pointer...so if I write in the parent.... 

Slider* sliderx  = getchild_slider(); (in the child side I'd have something like: " *Slider getchild (void){return this);"

Slider->addListener(this);

Then I get the child event, but then the child objetc disappears as soon as this new "pointer" ,that has been created to get the children object, also dies. So I would have to create and keep, in the parent component, as many child pointers as children I want to get access to.

Is it how it should be??OR I am misunderstanding something??

Do I explain myself??surprise