Duplicate events when using addMouseListener(this)

Hello,
I’m updating to JUCE 8, and in the process trying to remove as much as possible the mods that I had done on my fork on previous JUCE versions.

One that I can’t figure out how to deal with without modifying source code (and wondering how people deal with that as well), is the duplicate mouse events when you want a component to listen to itself as well as all children components (without having to add a listener to every single child and their nested children as well).

On the picture above, there is Component “Parent” with 2 children “A” and “B”
Each output a DBG with when their mouseDown is called

Here is the code of ParentComponent constructor :

addAndMakeVisible(childA);
addMouseListener(this, true);
addAndMakeVisible(childB);

What you see happening in the Output Debug when I click on A (red rectangle in output) is what I would expect and want :
Child A mouseDown called
Parent mouseDown called

but when clicking on parent (green rectangle in output), mouseDown is called twice :
once from the component’s internal mechanism, calling mouseDown(me) and then once again from the addMouseListener(this, true)

The problem is that there is no way to differentiate when it comes from the internal component call or from the mouseListener call, the MouseEvent passed in the mouseDown callback is exactly the same.

I had made a modification to add a “disableInternalMouseEvents” flag in the Component, but I’d like to know if other people are doing things differently before doing it again for juce 8.

Thanks !

without having to add a listener to every single child and their nested children as well

Why is this a concern?

Hello, thanks for replying.

This is a concern because I have complex components with unknown amount of nested children that are dynamically added and removed, and I’d like on my parent component to be able to identify what has been clicked inside the component, without having to track all the hierarchy.

addMouseListener(this, true) is exactly made for that imho, but the double event is definitely a problem.

Or would you have another way ? I wouldn’t find it proper to have to listen to all children, and their children, and their children…

Take a component like this for instance :

There is a header with 5 buttons, an editable text, a spacer (to be able to drag the component), and in the main part, a search box, a variable amount of child items that again can hold children…

In this scenario, I’d want in my parent component to know what has been clicked so I can properly define if i want it to select the item or not, activate a drag on move or not… and yes I could add listeners manually but that is not a future-proof solution as I have right now a generic way of dealing with different children, so I can add new buttons and texts without having to change my mouse events code. Otherwise i’d have to always add a new listener and a new check for every new part, which would make the code less efficient, less readable and less maintainable.

IIRC, my approach is:

  1. Childs contain a pointer to the top parent component.
  2. Events are handled locally in each component.
  3. If required events are passed to the top parent.

Not sure it is the best/cleanest, but it’s simple.

In my approach, child component should not know about their parent. They’re handling themselves yes, but the parent knows that it has children. the children should need to know all their parents, and certainly not control them.

Also, this would just mean a totally alternative event handling flow on top of JUCE’s workflow, which is great except this caveat imho. Again, I don’t find it efficient or future-proof to just create an alternate system where the goal here is to find how to improve the main one so it fits good practices and more use cases (especially more complex / nested hierarchy scenarios).

Thanks for providing opinions and feedback though !

Perhaps you could add a global mouse listener to the parent and set it to not intercept mouse clicks.

Desktop::getInstance().addGlobalMouseListener (this); 
setInterceptsMouseClicks (false, true);

This gets rid of the double events. You will just need to “filter” the global mouse events, but I guess this not much work.

I don’t think addMouseListener(this, true) is made for this.

I would recommend

  • let each child component intercept mouse event
  • the main component listen to mouse event of each child component
  • in the main component mouseDown(), identify the source of the mouse event by event.originalComponent (with isParentOf(), etc)

Thank you for the replies :slight_smile:

I’d say yes, that would work ok on a 1 level system.

So if I have let’s say a 10-level child nesting, with each level item having 3 or 4 components let’s say, I’d have to have 1-level-down non-recursive mouse listeners for each child of each item, and manually call a custom function (for each mouse event type) ?
So if I’d have to add a mouse listener to each children, which in turn would also have mouse listener to all of them ?

The data flow is already bubbling up the component hierarchy in JUCE, and what I’m trying to do is optimize both the code by not duplicating functions that are already there by avoid having to manually track down all component changes with manual listeners on all of them, and performance as this would induce double processing (JUCE internal + custom).

I understand that in a minimal example or in a project like a VST, this does not make so much sense as the hierarchy would not be so complex most of the time.
if you’re interested, this is one of the projects that i’m working on and finding this issue : Chataigne | Chataigne

You will see that we’re talking about a potential 10,15 hierarchy level, hundreds / thousands of dynamically generated nested components (like keys in a timeline).

The mod that I’ve done on JUCE right now allows me to only add one line of code to have the expected behaviour, without any custom listeners / parent reaching, and without risking performance cost, as it’s only using juce’s data flow.

It may seem that I asked for help and don’t want to listen to insight, but I’m honestly happy to read about other point of views and be able to discuss pros and cons !

I understand your concerns. However, I do think you have to modify the source code. For example, in Component::internalMouseDown, it calls both mouseDown (me) and MouseListenerList::sendMouseEvent (checker, &MouseListener::mouseDown). If you use addMouseListener(this, true), both of them should send a mouseDown event to the main component.

I think adding a disableInternalMouseEvents flag might be the best way to go. A dirty solution is using a counter so that we can react to the mouseDown of the main component once per two event.

Ok thanks for this, so this is what I had before and what I did again.

This is the PR for it if you’re interested :
Add disableInternalMouseEvents flag in Component by benkuper · Pull Request #1470 · juce-framework/JUCE

Cheers

1 Like

I’ve run into this issue a few times. I wrote a class LambdaMouseListener to make it easy to work around.

Do the following:

gin::LambdaMouseListener listener;
addMouseListener ( &listener, true );
listener.onMouseMove = [ this ] ( const juce::MouseEvent& e )
{
    // blah
};

That is indeed a nice workaround !
Thanks for this, I might use it. It’s adding one more call to pass on but it’s definitely the closest to a clean no-mod solution.

Cheers !

Yes, that’s a nice solution! But should the problem exist in the first place?

There’s an assert within Component::addMouseListener which warns that registering a component as a mouselistener for itself will result in duplicate events, so it’s a known problem. But the duplicate events can easily be avoided by setting and checking a flag internally and with no changes to the API. I think this would make a sensible feature request. Or are there drawbacks which I am missing?