Capturing child mouseEnter and mouseExit

I have a Component control which is composed of several child Component controls (buttons, a knob, and a label that all work together).

I want to perform special things when the mouse enters this composite control. So in other words, I want to capture mouseEnter and mouseExit for the enclosing component only. How would I do that? Do I need to add a mouse listener to each child? How would that work?

Have a look at the addMouseListener method - it has an option to catch events for all child components too, so you could use that to add a listener to the parent that will get all the events that happen inside it.

1 Like

I’m using this but when I go into a child component, I get a mouseExit for the parent. I just one one mouseEnter/mouseExit for the whole set Components. Here’s what happens:

Parent -> contains Child

Mouse moves into parent -> Parent::mouseEnter()
mouse then moves into child -> Parent::mouseExit, Child::mouseEnter()
mouse leaves completely: Child::mouseExit(), Parent::mouseEnter(), Parent::mouseExit()

So, when I get Parent::mouseExit(), I have no way of knowing if it is because I went into a child, or if I am leaving the control. I can’t look into the future to know if I am going to get a mouseEnter() for the child.

What I would prefer is something like this:

Mouse moves into parent -> Parent::mouseEnter()
mouse then moves into child -> (no messages)
mouse leaves completely: Parent::mouseExit()

Or, some way to know in the mouseEnter() message callback, that I am still inside the parent?

Any ideas Jules?

You can just check to see if the mouse event location lies inside that component (i.e. thisComp->contains(Point(e.x,e.y))
[I believe the mouse event coordinates will already be in the correct space]

If it returns true, then it’s obviously still within its bounds, thus it must have entered a child.

1 Like

I’m just getting this.
If there’s a child component is on the edge of the parent border, mouseExit won’t be called on the parent when the mouse goes outside its bounds.
I’m trying to dismiss a popped up component when the cursor goes outside it.
So now I need a timer and continually watch the mouse when the component is open. Which is OK, but it goes against the functional logic of mouseExit().

Might be a bit hacky but you could do something like:

void ChildComponent::mouseExit(const juce::MouseEvent& event)
{
    if (auto* parent = getParentComponent())
    {
        if (!parent->isMouseOver())
            parent->mouseExit(event);
    }
}

Thanks, yes, that would require overriding all the child exit functions.
I’ve got it sorted now with a really slow timer checking for the bounds. And any child combo box open, or dragging a slider does not dismiss.
Sorry to reintroduce a really old thread. :flushed:

True, yeah, if the children are JUCE components it’s maybe not worth overriding just for that…

I think the best option would still be to register yourself as a mouse listener to the children, as @jules suggested 11 years ago :stuck_out_tongue:

You could even create a little utility function to get callbacks:

class MouseEventNotifier : private juce::MouseListener
{
public:
    explicit MouseEventNotifier (const juce::Component& component)
    {
        component.addMouseListener(this);
    }

    std::function<void (const juce::MouseEvent& event)> onMouseExit = nullptr;

private:
    void mouseExit(const juce::MouseEvent& event) override
    {
        if (onMouseExit != nullptr)
            onMouseExit (event);
    }
};

MouseEventNotifier notifier {myWidget};
notifier.onMouseExit = [this](const juce::MouseEvent& event) {
    handleChildMouseExit(event);
};
1 Like

I have it sorted now, thanks again.