Main component needs to listen for buttons and sliders in child component

Yep, was up late late last night fixing this error (meant to post an update). Wasn’t due to me not inheriting juce::Button::Listener, as that had been there from the beginning. Far as I can tell, it was due to me either not listing includes correctly (looping them?) or just from not having my classes split between .cpp and .h files (I was doing them all inline). Still not entirely sure WHY that affects it, but splitting them up between definitions and code helped find the includes problem, which then I think fixed the issue.

Definitely, splitting all classes into .cpp/.h pairs is the way to go. Makes it so much easier to include files as the project grows without getting circular references and weird compiler errors that often don’t make any sense.

Indeed - but I find it so much faster to prototype in a single file!

As usual on a thread where somebody is advising beginners to always split up classes into .cpp/.h, I’m just going to jump in and say that that’s not a good rule of thumb nowadays.

The real answer is of course “it’s complicated”, but I prefer to leave a class in one piece until there’s a reason not to (e.g. size, circular dependencies, implementation hiding, etc).

1 Like

I am a beginner (with JUCE - not C/C++). And JUCE is fantastic! The ability with which I have been able to construct the framework for my project, in a short period of time, is amazing. But with the project I have been slowly developing for many weeks now, there is no way it would have continued to develop without splitting it into .cpp/.h. The massive number of incomprehensible compiler errors alone made that clear. The endless failed attempts at forward declarations, or “just the perfect order of includes in each file” also made it clear. After splitting everything into .cpp/.h - no problems at all. Development moving forwards!

And why do you say it’s not a good rule of thumb? What exactly are the great benefits of putting everything into a .h file? Assuming one can even get it to work… How does it save time and effort?

If you write a bunch of classes which are decoupled from each other - i.e. don’t have dependencies - then clearly the simplest way to structure that would be as self-contained header files, no cpps to worry about, you just include the ones you need, etc. And writing maximally decoupled code is what you want to be doing, so if all your code fits into separate headers, then awesome, you’ve won.

Obviously in real life, most classes do have to know about other ones, so you need to pre-declare some stuff. However, the more intertwined and interdependent your classes are, the worse the structure of your code. So if every trivial class you write is a .h/cpp pair, it’s just too damned easy for inexperienced coders to randomly reference whatever classes they want to from any bit of cpp code, and you end up with tangled, undisciplined code. If you make it a bit harder so that you have to think about the order in which your dependencies lie, then it helps add some friction to stop that slide.

And TBH inline classes tend to be the best solution at both ends of the spectrum: for entirely independent classes, it keeps the whole thing in one lump and everything’s simple. And where you have large, complex, highly interconnected sets of classes (I’m thinking here about our SOUL syntax tree classes where we have many dozens of completely tangled, tiny, constantly evolving classes), then really the only practical way to handle them is as inline classes inside a parent class (which is a trick to work around the annoying C++ order-of-inclusion stuff).

Sure, when you have a few big classes with lots of implementation that’s best hidden from a casual reader, then splitting them up is usually a good policy. But I find that in most of my cpp files these days you’ll find a whole scattering of small inline classes used locally to do various helper tasks, and it’d be crazy to start pulling them out into separate files, or moving them away from the one place they get used.

1 Like

OK, I get you there. I guess I wasn’t clear when I said “break everything into cpp/h” pairs, because I do have some small inline classes that I would not try to extract. But for the most part (I’m working on porting an existing project with lots of code), the classes are so large that cpp/h is the way to go - for me. YMMV. :slight_smile:

As I just started my next project, it suddenly dawn on me that the solution for my original question was much simpler.

So to listen for buttons and sliders from one Component into MainComponent, there is no need to pass “this” at all. So the revised solution, mainly adding listener in MainComponent, instead of other component(s), is the following;

MainComponent.h contains, besides the “default” code;

#include "MenuComponent.h"
...
class MainComponent : public Button::Listener,
	                  public Slider::Listener
...
public:
    void sliderValueChanged (Slider* slider) override;
    void buttonClicked (Button* button) override;
private:
    MenuComponent menu;

MainComponent.cpp;

addAndMakeVisible (menu);
menu.startButton.addListener (this);
 ...
void MainComponent::sliderValueChanged (Slider* slider)
{
	String id = slider->getComponentID ();
	if (id == "appSize")
    {
    // Obtain slider value from other component, to use for whatever
      myLocalVar = menu.appSize.getValue();
}
...
void MainComponent::buttonClicked (Button* button)
{
	String id = button->getComponentID ();
	if (id == "start")
	{
       // Do something...
    }

MenuComponent.cpp;

MenuComponent::MenuComponent ()
{
  // Create your buttons and sliders here as normal, but addListener in MainComponent
  // Also set ComponentID.
  // Example;    
  startButton.setComponentID ("start");
  startButton.setColour (TextButton::buttonColourId, Colours::green);
  startButton.setButtonText ("START NEW GAME");
  addAndMakeVisible (startButton);

I can see how that can work, depending on the app’s needs. In my actual use case (not the demo I posted), the additional components may or may not be created (think additional editors that may never be opened). In that case, I think passing “this” and adding the listeners in the other components would probably be the better way to do it.

Thank you so much for this! I’m new to JUCE and have been trying for literally over 3 hours trying to get one component to be able to reach out to MainComponent and this worked perfectly.