MouseEvents and Lambdas


#1

So, over in the thread about Signals and Slots here I said that it would be great if component::mouseDown() could be implemented this way:
component.mouseDown = []() { DBG("component mouseDown"); };

So, digging a little bit deeper into how mouse events are sent out from the mouseEventSource to the listeners, i ended up at this method:

static void sendMouseEvent (Component& comp, 
   Component::BailOutChecker& checker,
   void (MouseListener::*eventMethod) (const MouseEvent&), 
   const MouseEvent& e)

Is there a way to modify it so that the 3rd and 4th arguments could be a lambda instead or std::function? somethin’ like:

static void sendMouseEvent( Component& comp, Component::BailOutChecker& checker,
 std::function<void(const MouseEvent& e)> f );

I realize the tricky part is that the sendMouseEvent() function calls void (MouseListener::*eventMethod)(const MouseEvent& e) for every component that added themselves as a listener to this particular mouseEvent. Perhaps I am just requesting a pipe dream…
But it is 2017, juce5 is using C++11, so shouldn’t we be able to write easier to read code without writing massive if(button == &myButton) switches inside the buttonClicked() callback when our GUI has lots of buttons?

I began my search here for understanding how mouseEvents make their way to a component

void Component::internalMouseDown (MouseInputSource source, Point<float> relativePos, Time time, float pressure)

where a mouseDown(me) is called, followed by
MouseListenerList::sendMouseEvent (*this, checker, &MouseListener::mouseDown, me);


#2

nothin? not a single response?


#3

Now you have two likes :slight_smile:


#4

@jules @fabian trying to get this request noticed!


#5

Yeah, it’s a good request, but you could also roll your own implementation in a few lines of code without any changes to juce itself. Just create a class that inherits from MouseListener, and contains lambdas to call for each callback, then add that to your component, e.g.

struct MyComponent  : public Component
{
    MyComponent()
    {
        addMouseListener (&invoker);
        invoker.mouseDown = [] (const MouseEvent&) { ...etc };
    }

    MouseEventInvoker invoker;

#6

@jules wait.

how do you even override

virtual void mouseDown( const MouseEvent& event);

so it’s a

std::function<void( const MouseEvent& )> mouseDown;

in the inheriting class?!?!


#7

@Jules can you show the MouseEventInvoker class you definitely teased and omitted in your example? PLEEEEEAAAASSSSEEE?!?!?!


#8

This one works:

class Invoker : public MouseListener
{
public:

    std::function<void(const MouseEvent&)> onMouseDown;

    void mouseDown (const MouseEvent& e) override
    {
        if (onMouseDown) onMouseDown (e);
    }
    
};

MainContentComponent::MainContentComponent()
{
    addMouseListener (&invoker, true);

    invoker.onMouseDown = [](const MouseEvent& e) {
        DBG ("You clicked me: " << e.getPosition().toString());
    };

    setSize (600, 400);
}

private:
    Invoker invoker;

Addition: capture this:

    setComponentID ("Foo");
    addMouseListener (&invoker, true);

    WeakReference<Component> myself (this);
    invoker.onMouseDown = [myself](const MouseEvent& e) {
        if (myself) {
            DBG ("Hello I'm " << myself->getComponentID());
        }
        DBG ("You clicked me: " << e.getPosition().toString());
    };

Next step to have that templated for Invoker<Button> maybe?
:slight_smile:


#9

Thanks Daniel, yep, that’s the idea!

(Sorry @matkatmusic, wasn’t trying to tease, just thought the implementation would be pretty obvious!)


#10

@jules it wasn’t obvious!


#11

Fair enough - I was assuming a certain level of fluency with lambdas, based on the fact that you were asking for them! :slight_smile: