LambdaMouseListener


#1

Hello @jules @ed95 @fabian and the rest of the JUCE team. Here is a suggested addition to allow lambdas as the callbacks for mouseEvents on any/all Components. If the lambda doesn’t exist, it defaults to the regular MouseListener methods. To make it work, just modify juce::Component so that it inherits from LambdaMouseListener instead of MouseListener and every component in the API will now have access to this lambda ability

class LambdaMouseListener : public juce::MouseListener
{
public:
    LambdaMouseListener() {}
    ~LambdaMouseListener() {}
    //these are the mouse event lambdas
    std::function<void(const MouseEvent& e)> mouseMoveHandler;
    std::function<void(const MouseEvent& e)> mouseEnterHandler;
    std::function<void(const MouseEvent& e)> mouseExitHandler;
    std::function<void(const MouseEvent& e)> mouseDownHandler;
    std::function<void(const MouseEvent& e)> mouseDragHandler;
    std::function<void(const MouseEvent& e)> mouseUpHandler;
    std::function<void(const MouseEvent& e)> mouseDoubleClickHandler;
    std::function<void(const MouseEvent& e, const MouseWheelDetails& wheel)> mouseWheelMoveHandler;
protected:
    //these are the mouse event handlers. if the lambda exists, use it.  if not, use the default mouse event handler for the component
    void mouseMove(const MouseEvent& e) override
    {
        if( mouseMoveHandler ) { mouseMoveHandler(e); }
        else { MouseListener::mouseMove(e); }
    }
    void mouseEnter(const MouseEvent& e) override
    {
        if( mouseEnterHandler ) { mouseEnterHandler(e); }
        else { MouseListener::mouseEnter(e); }
    }
    void mouseExit(const MouseEvent& e) override
    {
        if( mouseExitHandler ) { mouseExitHandler(e); }
        else { MouseListener::mouseExit(e); }
    }
    void mouseDown(const MouseEvent& e) override
    {
        if( mouseDownHandler ) { mouseDownHandler(e); }
        else { MouseListener::mouseDown(e); }
    }
    void mouseDrag(const MouseEvent& e) override
    {
        if( mouseDragHandler ) { mouseDragHandler(e); }
        else { MouseListener::mouseDrag(e); }
    }
    void mouseUp(const MouseEvent& e) override
    {
        if( mouseUpHandler ) { mouseUpHandler(e); }
        else { MouseListener::mouseUp(e); }
    }
    void mouseDoubleClick(const MouseEvent& e) override
    {
        if( mouseDoubleClickHandler ) { mouseDoubleClickHandler(e); }
        else { MouseListener::mouseDoubleClick(e); }
    }
    void mouseWheelMove(const MouseEvent& e, const MouseWheelDetails& wheel) override
    {
        if( mouseWheelMoveHandler ) { mouseWheelMoveHandler(e, wheel); }
        else { MouseListener::mouseWheelMove(e, wheel); }
    }
};

I’ve also created a similar tool that lets you use Lambdas as the callbacks for Buttons. I’ll share that in a separate post.

usage:

Component c;
c.mouseMoveHandler = [this](const MouseEvent&) { DBG( "you moved the mouse over " + this->c.getName() );};

#2

Couldn’t this be done simpler by using lambdas for the normal case as well? In the constructor you could just set them up to call the regular method and people could assign different ones if necessary. That would save all those checks in the the methods and be faster.


#3

Look at what Component calls internally when a mouse event occurs inside
it. If you know a way around that without breaking everything that
inherits component, by all means, share a working version.


#4

I like your use of the word “just” there! I’m not sure exactly how large a std::function object is, but they’re pretty hefty and adding 8 of them would probably double the size of the Component class, as well as adding a whole bunch of construction/destruction overhead to everyone’s code. So nope!

I mean, it’s a solid suggestion, and this class looks like it’d work just fine, but inheritance is the wrong pattern, even if it’s just something you inherit in your own class. Like my suggestion on the other thread, the best way to do this would be to add one of these as a member - perhaps it could take a reference in its constructor to a component, so it could attach itself automatically as a listener.


#5

ok, but the goal is to not have a bunch of static methods to add features to library elements that should probably already exist. Perhaps there is a middle ground solution? Or perhaps it is time for a LambdaJuce fork?


#6

@jules I learned!!!

however, I’m not sure if it’s necessary to remove the componentListener in the destructor. that’s the only thing I’m not sure of, based on your example in the other thread.


LambdaButton
Button::onClick
#7

If you’ve used the same pattern that I did in the other thread then the internal helper objects should cleanly self-delete when the target component is deleted - that’s what the ComponentListener stuff inside them is for.


#8

Yes I understand that. But you didn’t removeComponentListener in the
destructor like you did with the button.removeListener(this). So I’m asking
if it’s necessary to.