How to intercept some mouse events in the parent component?

#1

Hi everyone,

I’m currently running into an issue which I believe is pretty simple. For some reason I can’t find a simple solution though, so I was hoping one of you could. Basically I have a component with a couple child components (standard buttons and comboboxes). I want people to be able to right-click the parent, while of course I’d still want the child components to respond to left-click (what else would a button do?). So is there a simple way for right-clicks to go to the parent component, while to left-clicks go to the child-components?

Thanks!

0 Likes

#2

The parent could be a MouseListener of the child components in order to react to mouse events as it wishes, and the child components will still receive mouse events to do as they wish too.

0 Likes

#3

Thank you for your (very) quick reply! I’ve considered that, but the problem is that the event is still sent to both components, right? Seeing that normal buttons also respond to right-clicks, this does not seem like the “perfect” solution.

0 Likes

#4

They are, but within the methods you should be able to do something like if (event.mods.isRightButtonDown()) or if (event.mods.isLeftButtonDown()) to check which button was pressed. You could probably invent some sort of RightButtonMouseListener if you wanted to be really clever, but all you really need to do is just check which button was pressed in each of the callbacks that requires it.

0 Likes

#5

re: components click handlers responding to all mouse clicks, I made myself a little wrapper template to only respond to left clicks

template <typename ComponentType>
class LeftClickOnly : public ComponentType
{
public:
    using ComponentType::ComponentType;

    void mouseUp(const MouseEvent& e) override
    {
        if(e.mods.isLeftButtonDown())
        {
            ComponentType::mouseUp(e);
        }
    }
    void mouseDown(const MouseEvent& e) override
    {
        if(e.mods.isLeftButtonDown())
        {
            ComponentType::mouseDown(e);
        }
    }
    void mouseDrag(const MouseEvent& e) override
    {
        if(e.mods.isLeftButtonDown())
        {
            ComponentType::mouseDrag(e);
        }
    }
};

and use like

LeftClickOnly<Button> myButton;
2 Likes

#6

Hmm, is there no (simple) way for the parent component to basically consume the mouse event or pass it along to one of the children? Sort of like how KeyPressed() is handled for Components?

The template could be a solution otherwise, it actually looks quite elegant.

0 Likes

#7

A mouse click will traverse using Component::hitTest().
You can call setInterceptMouseClicks (true, false); which will call hitTest() on the parent, but not descend to it’s children.
This makes all events ending up on the parent, and you can do with the events, whatever you see fit.

0 Likes

#8

It’s also worth noting that if you add the parent as a mouse listener to the child and set setInterceptsMouseClicks(true, false) for the parent, MouseEvent::originalComponent will actually point to the child. To use your example:

// whichever mouse callback(s) you need
void mouseDown(const MouseEvent &e)
{
    if (e.mods.isRightButtonDown())
        // handle right click
    else if (e.originalComponent != this)
        e.originalComponent->mouseDown(e);
}
1 Like

#9

@TonyAtHarrison, that sounds perfect! Unfortunately, whenever I call setInterceptsMouseClicks(true, false) in the parent constructor, the child still responds to right-clicks. So I’m guessing the events actually arrive twice at the child, once at the original click and once from the parent call. Any idea what I could be doing wrong?

0 Likes

#10

Is this setup now with an additional mouseListener? Per default an event is delivered to exactly one Component. To figure out which one, the event starts at the top level component and will call hitTest on all children. This goes on recursively with the first one returning true, until a component is found, that has no children. This one will get the mouseEvent delivered.
The setInterceptsMouseClicks allows you to interrupt this flow. The first flag will make this component returning false in all cases, but with true on the second argument, it will still try all child components.
MouseDrag events work differently, they will always be delivered to the component, where the mouse down occurred.

Hope that clears it up a bit

0 Likes

#11

This may be a silly question, but you are calling that on the child component, yes?

0 Likes