Passing on MouseEvents

Something I run into quite often is the need to filter mouse events according to some logic.

For example, I have a ‘parent’ component, managing multiple child components. Those child components might be complicated objects that are are also hosting child components, etc, and some of them are reusable generic objects (say, a Slider).

For example (pseudo code):

struct ReusableComponent: Component
{
   //The sliders handle their own events:
  Slider slider1, slider2; //etc
};

struct EventFilteringComponent: Component
{
  //Only handles mouse events with the alt modifier
  ReusableComponent altModifierHandler;

  //Handles all events without the alt modifier
  ReusableComponent allOtherEventsHandler;
};

What would be good strategy to implement the event filtering logic in EventFilteringComponent, without changing ReusableComponent?

Ideally, this should be done without touching the internals of ReusableComponent - as it’s possible EventFilteringComponent would not be aware of the concrete type that it’s hosting (it would be a std::unique_ptr<Component>)

Any ideas?

Thanks!
Eyal

What if you disable receiving mouse events on the reusable components, and the event filtering component just forwards the events to the correct child component’s mouse handler functions based on if the alt key is down or not?

Typing from the phone, but:

void mouseMove(e) {
   If( e.isAltKeyDown() ) 
      altModifierHandler.mouseMove(e);
   else
      allOtherEventsHandler.mouseMove(e);
}

Thanks @matkatmusic!
Unfortunately that doesn’t work - calling mouseMove, mouseDown, etc on the component will not trigger the event on the correct nested child.

So in the case of my pseudo example, I’d like the actual nested Slider to pick up the event, and not the ‘handler’ in between Component.

To sum it up, what I’d like is that after the event is filtered, the mouse event on the chosen component would be triggered as if I ‘naturally’ clicked on that component, with all the regular component and children logic.

A little bit of an hack, but maybe you could set up a Timer that looks at the state of the Alt key and toggles the setInterceptMouseClicks() of the two ReusableComponents accordingly:

enables the one that handles the Alt and disables the other when Alt is pressed, and vice versa

@yfede, that’s right (although risky as you could potentially not ‘catch’ the modifier in time) - but the alt thing was just an example. I also need for example to only filter out left/right clicks, only pass the mouse wheel to certain components, etc.

I had to do something like what you’re trying to do with an envelope node editor, and I ended up simply forwarding all the events if they were within the bounds of the child component from the parent’s mouse handler. It was simple and worked, but I also had to implement forwarding in every child component. Probably not ideal for what you’re doing…

Yeah, that’s how I did it so far - but it doesn’t scale so well as it really coupled the parent with the exact structure of the children, and doesn’t work at all when the children types are unknown.

Unknown, meaning you don’t know if they’re a juce::Component?

Well, I do know they’re a Component, but in some cases (generic library stuff) that’s all I know about them.

So going back to the example on the original post - I don’t always know that I need to poke into the ReusableComponent, find the Sliders that are nested in it and call mouseDown, etc on them.

Even when I do know the concrete Component types, it’s very possible that their internal structure would change, which I’d like to maintain as-is when passing the filtered events to them.

For example, if the reusable Component decided to change the z-order of child Components, I don’t want to change the parent mouse behavior to be aware of that change (and in more generic situations, I couldn’t even if I wanted to).

What about some kind of mouseListener registration that happens? I think there is a component listener callback if the child hierarchy changes as well…

Not sure I understand this solution - I can get callbacks about changes in the children, but at some point I need to know which of the children (and their children, etc) is the right candidate for the post-filter mouse down (according to z position, flags like setInterceptsMouseClicks, mouseListeners, etc)

What I’d like is a non intrusive way to re-trigger the mouse event after the filter, so it includes all the known event handling mechanics that the child already has implemented to function correctly.

hmmm. Does this get you anywhere near where you’re trying to get to?
it probably doesn’t, but maybe… :man_shrugging:

struct OwningComp : Component {
    OwningComp();
    void mouseDown(const MouseEvent& e) override;
};

OwningComp::OwningComp() {
  //after adding all of your components...

  //recursively calls getChildren() on every child, building a list.
  auto children = getAllChildrenRecursively(); 

  for( auto child : children )
  {
    child->addMouseListener(this);
  }  
}

void OwningComp::mouseDown(const MouseEvent& e) {
  if( getChildren().contains( e.originalComponent ) 
  {  
     //maybe dynamic_cast e.originalComponent to find out its type and 
     //handle events over that specific type that weren't handled internally?
  }
}

The problem with MouseListener callbacks, as I understand them, is that the event would also be triggered on the “listened” component before the listener is triggered, so I won’t be able to filter them.

Unless I’m missing something in the Listener mechanic that does allow tempering with it? If during OwningComp::mouseDown() I could prevent the original component from triggering the mouse event, that would fix my issue.

Another idea (but I have never attempted it myself):

maybe you could override your ReusableComponents hitTest() method and make it return true only when ModifierKeys::getCurrentModifiers() satisfies a desired condition (could be an exact match, a bitmask or even a predicate specified with a lambda).

Said condition could also be customizable as a part of the features of the ReusableComponent class, so that you don’t have to derive one ReusableComponent for every kind of modifiers that you want to react to.

Disclaimer: maybe this approach causes issues with mouseEnter / mouseExit callbacks, because a Component that’s under the mouse suddenly becomes “hittable” or “non-hittable” even if the mouse didn’t move… but I really don’t know

@yfede Thank you, I really appreciate you putting so much thought into it.

I think what you’re suggesting would work well (didn’t test yet) for the alt modifier use case, but not for left/right click/mouse wheel filtering use case, as you can’t get the information about those until the actual mouseEvent has happened.

But your suggestion has lead me into another thought:
Can I ‘simulate’ the natural behavior of a mouse click if I recursively call hitTest on all nested child components of a particular component? Would that be a solid way to ‘reach’ the right child component that would have processed the event if I hadn’t filtered it?

It gets worse if you consider, that there can be multiple MouseInputSources, e.g. multi touch, virtual mouse (VNC), multiple mice connected…

So you cannot know from a previous mouseEnter, if that is related to the next mouseEvent. Any stateful filter method needs to index the MouseInputSource!

And the system getComponentUnderMouse() is updated in MouseInputSource::setScreenPos(), so I guess it won’t evaluate hitTest on a modifier change, only on a mouse move.

Sorry for only raising problems, not solutions. It seems to me, that this is beyond what the component system can handle. Maybe try to get it all into one component, even though that means that you cannot reuse the child component.