Component Listening and broadcasting newbie question


#1

Hello to all,

I'm a beginner at both Juce and C++, learning C++ thru Stroustrup's "PPP", and Juce thru Robinson's "Getting started with Juce".
So far so good, I slowly progress, and it's very rewarding.

Learning the basics of GUI, I train by creating small GUIs, and I have a problem when I try to break in several custom subcomponent a GUI.
To simplify:
Let's say I have two custom classes of components:
-FirstComponant (inherits Component), has a private Button member: button
-SecondComponent (inherits Component), has a private Label member: label

I'd like that the button member of FirstComponent, when clicked, would write "button from FirstComponent was clicked" on the label member of SecondComponent, regardless of the hierachy of the above components themselves, i.e.:
case A: ThirdComponent contain one instance of FirstComponent, and one instance of SecondComponent
case B: FirstComponent contain an instance of SecondComponent
case C: SecondComponent contain an instance of FirstComponent.

 

If I have both a Button and a Label members of the same FourthComponent, It's pretty clear how to proceed:
-1- FourthComponent should inherit from Button::Listener. It can now listen to a Button.
-2- button needs to broadcast toward the FourthComponent class: this is the method addListener(this)
(which works because FourthComponent is a Button::Listener)
-3- FourthComponent needs to implement at least the pure virtual function that came from Button::Listener:

void buttonClicked(Button * buttonReceived)
{
    if (button == buttonReceived) label->setText("button from FourthComponent was clicked", dontSendNotification);
​}

done.

 

 

In my more general situation, here is what I think should be done but I don't know if it makes sense:

-1- SecondComponent should inherit Button::Listener (so it can listen to the button)
-2- FirstComponent member button should know it needs to broadcast towards the SecondComponent instance.
button->addListener(...)
case A: I don't know how to do it. I need to be able to describe the another class member of the parent Class, most probably a private member.
case B: simply the member of type SecondComponent. But that member is most probably private, right?
case C: I don't know: I need to be able to describe the parent class

-3- SecondComponent should implement the needed pure virtual function:
void buttonClicked(Button* buttonReceived)
problem is: how do I test that buttonReceived is indeed the right one?
case A: I don't know how to do it. I need to be able to describe another class member of the parent Class.
case B: I don't know either: I need to be able to describe the parent class
case C: simply the member of the FirstComponent. But that member is private.

In all cases I'm stuck, I might be totally on the wrong track here, so let me know,

Best regards.

 


#2

If you want to be able to do it without connecting components directly then you'll need to work off of some sort of data model/event mechanism that the relevant components are made aware of.  Your button will need to be able to change the model and the label will need to be able to respond to the model changing.  This can be done in all sorts of different ways but you might want to check out the Value and ValueTree classes, ChangeListeners and Changebroadcasters and other similar things in JUCE.  Some JUCE widgets already have support for the Value class and I think there are some examples of it's usage in the JUCE demo.


#3

Thanks for your answer.

by "connecting components directly", I guess you refer to my FourthComponent case?
I'll look into your suggestions and read about the Value, ValueTree classes etc...

Meanwhile, I was wondering:
ii seems it boils down to the fact that I want to build custom component (such as FirstComponent and SecondComponent) that behave (communication wise) like a TextButton or a Label.

In the case of SecondComponent, which contains a Label member, first I need roughly to add a public function member such as:

void writeToLabel(const String &message);

 and use it to give acces to the SecondComponent private Label member.

But I also need to make a listener of FirstComponent, so to create a class FirstComponent::Listener, with it's associated pure (or not) virtual methods.
In this way,  any class (such as SecondComponent or any other) that contain an instance of FirstComponent could react to FirstComponent broadcasting.
It also means that FirstComponent needs to have a addListener() method.
And all that I don't know how to do (but I'll look into it).

Again if I am completely off tracks let me know.

 

 


#4

All your scenarios fall under what I mean by connecting directly but I can see how it's a bit confusing.  What I'm saying is if FirstComponent needs to know about SecondComponent or ThirdComponent needs to know about First and Second and so on.  If any component needs to know about the another in order to get the job done then I deem that connecting directly.

That said, connecting directly makes complete sense for some scenarios.  Typically this is a parent/child relationship, where the child components are pieces on the parent component.  For example, a table component might have row components inside it and would directly manipulate them.  In your scenarious, the relationship between FirstComponent and it's button could be an example of this, and the same could go for SecondComponent and it's label.  Your writeToLabel method is good for when SecondComponent is a child to a component that want's to directly manage it.  Creating listener interfaces for widgets is good, so you're on the right track for that, but in your examples it still falls into this category of interaction.

For what your goal is, to have a button change the text of a label which could be anywhere on the screen, you aren't in a scenario where the button and the label constitute parts of a greater widget.  So for this type of thing you want to be able to provide the communication between them in a decoupled manner.  For a contrived educational scenario it would seem to make sense to directly call one component from another, but if you look at the button press from a more application specific sense, for example say the button opened a file and the label displayed the name of the currently opened file then likely what you'd want to do is have the button execute a command (not necessary, but typical), the command open's the file and changes the application state which is stored in a ValueTree and the value tree then notifies anyone listening to it that the "currentFileName" property has changed.  In this case the Label has been setup to listen to it so it gets notified and changes it's value.

 

 

 


#5

Ok, now I understand what you mean! And you are spot on.

Indeed my example needs connecting directly. 
And indeed, my example is a contrived educationnal scenario, which failed at pointing my real goal that you described perfectly:

if you look at the button press from a more application specific sense, for example say the button opened a file and the label displayed the name of the currently opened file then likely what you'd want to do is have the button execute a command (not necessary, but typical), the command open's the file and changes the application state which is stored in a ValueTree and the value tree then notifies anyone listening to it that the "currentFileName" property has changed.  In this case the Label has been setup to listen to it so it gets notified and changes it's value.


 

Your above example is exactly the exercice I intended to do: having a FileBrowserComponent to select a file, and display some file related info in in child components of a TabbedComponent. (hence the separation in differents Component classes).

 

I'm too new to all this, and got too UI only focused. I knew there would be a next step were the UI would indeed be "hooked" onto "something" that would do some treatment, and give back results, but since that part is still mysterious, I didn't include it in my scenario.

I'll look into all this, and also refine my general C++ skills...

Thanks a lot!