"Listened" component in event callback (mouse event essentially)


#1

Hey,
i tried to search on the forum if this issue has already been posted but i can’t find something similar.

I have a fairly complex setup of nested components that are all interactable in their own way.
I need now to be able to add a a mouselistener to a child component from a parent component (the parent component being the listener) and be able to retrieve a pointer to the object on which i put the “addMouseListener” callback in the “mouseDown” or “mouseDrag” callbacks.
Currently, the accessible members are “originalComponent” and “eventComponent”, but none of them seem to represent the object that i wanted to listen, because my listened child component contains components as well and the eventComponent and originalComponent properties point to those component that originally cast the event.

It would be great to get a “listenedComponent” property that represent the object on which we put the mouselistener.
I imagine that the mouseEvent is currently being passed from child to parent, this property “listenedComponent” would be altered at each level to reflect the current component invoking the callback on its listeners.

It actually may be a general behavior of event dispatchers to be able to pass their pointer during the callbacks.

Am i making sense ?


#2

perhaps the MouseEvent members eventComponent and originalComponent can fulfill your needs?

void mouseDown (const MouseEvent& e)
{
    if (e.eventComponent == oneOfYourChildrenComp)
    {
        ...
    }
}

#3

thanks for the quick reply,
i’m actually in a case where neither eventComponent nor originalComponent seem to represent the component i need, which is the one i chose to listen to in the first place.
I would guess it is the e.eventComponent but this one actually represents the component that throwed the event right ?

To keep it simple, i have “myComp.addMouseListener(this)” and in the mouseDown() i would expect to be able to retreive a pointer to “myComp”, regardless of the children of myComp has thrown the event.
And i can’t use the “setInterceptMouseClicks(true,false)” in myComp because i still need this component to have information about its children being clicked.


#4

My guts says you’re over complicating something here. When I have to reread an explanation over and over again alarm bells tend to go off. Can you not simply provide a pointer to the parent when you’re creating your children? Perhaps some pseudo code might help explain things a little better?


#5

…you even have a parent pointer: Component::getParentComponent() It is automatically set when you call addAndMakeVisible().
And you can use a dynamic_cast to get the actual type of the component:

if (MyComp* myComp = dynamic_cast<MyComp*> (getParentComponent()) {
    myComp->heyDaddy();
}

I think Rory is right, when you use addMouseListener very often the calling flow gets messy…


#6

I’m sorry, the problem is actually quite simple but i did not do a good job explaining it.

The setup is this one (a bit simplified) :

Component Parent

  • Child Component C1 with a Label L1 and a TextButton B1
  • Child Component C2 with a Label L2 and a TextButton B2
  • Child Component Cn

Each child component C1,C2,Cn has it’s own internal behavior, handling of the buttons, etc.

i created dynamically the components C1,C2,C3 (it’s a user generated list) and i need from the component Parent to retrieve events for any of its children, with knowing which child it is.
Example : ideally, i would get in the same mouseDown callback a click on B1 or a click on L2, and be able in that call back to get a member C1 if i clicked on B1 and C2 if i clicked on L2

So basically, if i do
C1.addMouseListener(this,true);

I would expect to be able to do, if i click on B1 for instance

void mouseDown(const MouseEvent &e)
{
           DBG(e.eventComponent); //the component that triggered the event so i expect **B1**
           DBG(e.originalComponent); //it seems that it is the same as eventComponent
           DBG(e.listenedComponent); //it would be the component that is calling the mouseDown callback (because i am a direct listener of it) so in this case **C1**
}

`

Is that more clear ?


#7

I understand more clearly now what you’re trying to do but it still sounds like you have backed yourself into a corner. The JUCE framework is designed to make coding software relatively easy and straight forward. The most valuable lesson I’ve learned from using it over the years is this: if you find yourself struggling with something for an extended amount of time, it’s usually because you haven’t given enough thought to the design of your software.

I’m not throwing stones. I’m merely speaking from experience. Look through the JUCE code base, or take a look at the Projucer, with it’s GUI layout editor. It’s full of dynamically created children that pass events all over the place. It’s quite complex, but you’ll not see anything as complicated as what you’re trying to do.

Finally, if you do persist down this route, you could just make your parent a ChangeListener, and each of your children a ChangeBroadcaster. You can then send a change message to your parent in any of your children’s methods, including their mouse events. The parent will pick up the message and you can check to see what child sent it.


#8

Thank you very much for the insight :slight_smile:
I’ve actually been designing softwares for quite some time now, but i’m fairly new to JUCE, and as you said i should be (an i am !) aiming for the most adequate workflow.
I’ve been coding with similar event-based frameworks, and the property i’m talking about is most of the time available, and it actually allows for great simplification of the code !
I’ll try to find a more JUCE-y way to do it and let you know how it goes.

Thanks !


#9

I didn’t think for one minute think you were a new to this game, I hope it didn’t come across that way. But yeah, it’s probably worth making things a little JUC i E r.


#10

yes, in a mouseDown it is the same, have a look at the MouseEvent::eventComponent:

This is usually the component that the mouse was over at the time, but for mouse-drag events the mouse could actually be over a different component and the events are still sent to the component that the button was originally pressed on.

So for mouseDown it has to be the same. But while dragging it might be something different. This is handy for e.g. checking if the drag might be dropped on that eventComponent.

For your problem, would this solve your problem?

C::C () {
    setInterceptsMouseClicks (true, false);
}

void C::mouseDown (const MouseEvent &e)
{
    Component* hit = getComponentAt (e.getMouseDownPosition());
    if (hit == l) {
        // label clicked
        doYourStuff ();
        l->mouseDown (e);
    } 
    else if (hit == b) {
        // button clicked
        doOtherStuff ();
        b->mouseDown (e);
    }
}

maybe it is not accurate in terms of coordinate system of getComponentAt and getMouseDownPosition, but at first glance it could work like that…


#11

thank you,
i understand now the uses of eventComponent and originalComponent. So it really seems to be missing the one i’m looking for.
The method you’re doing daniel would return the same as originalComponent, right ?

the way to do it with the currently available properties would be to get the eventComponent and search recursively its parentComponent until i find the one i’m looking for. And it works, but when i get a list with a lot of children components, i would need to check at each recursion level against all the items, and this is definitely not the way to do it.

Having an intermediary event in the items so i can get direct callbacks from them seems the proper way to do it, but i still think that this property would not complicate the current workflow (it would not even change the way things are done) but could simplify it in some cases.


#12

Do you have control over the hierarchy of the components? or is this completely arbitrary?

The Components are already doing this resolution of which component got clicked, so doing this by hand would be reinventing the wheel. When the mouse is clicked, the OS calls the top level windoe (by it’s ComponentPeer, as far as I understand it), and then checks via hitTest all it’s children, which test all their children etc.
You can now stop that behaviour using setInterceptsMouseClicks. First it’s own behaviour, the second to skip the testing of it’s children.
This workflow is million times tested, so you have as little trouble as possible, if you stick with that routine.

To get that information you are looking for, you need to let this procedure finish. Maybe, if you add yourself as mouse listener to ALL your buttons and labels, the originalComponent should be the one you are looking for.
BUT I agree 100% with Rory, that this approach leads you to hells kitchen.

I like the idea with the ChangeListener to the buttons. An additional benefit is, that your mouseClicked callbacks finish fast keeping the app responsive, and the change message (for updating or whatever you want to do) will be called asynchronously.


#13

Why a MouseEvent? Is this just to know when a button was clicked or for any other purpose?

For each child component I’d personally have a member pointer to the parent.

Derive each child component from Button::Listener and have a listener for any button clicks in the child and in the child class’ buttonClicked() call a method in the parent and send it a pointer to the child component or the button which was pressed…

parent->childButtonClicked (this);

I’d probably have a method in the parent for whatever the button triggers – I do this kind of thing in ToolBars where the buttons are on the ToolBar and I want to have the click trigger a method in the parent.

parent->doWhateverThisButtonTriggers();

If all your child components have any shared code… consider a parent ChildComponent class for each of the children to derive from.

Rail


#14

Thanks all for your feedback,
i managed to do what i want while keeping things JUCE-y :slight_smile:

have a nice day


#15

I may be late, but perhaps the template method findParentComponentOfClass () of Component would have been useful here:

If your Components C1, C2, etc, were of the same class, derived from Component (for example, suppose they are all CustomComponent), then l1->findParentComponentOfClass <CustomComponent> () will walk the parents of l1 until one of type CustomComponent is found, and then return it.