Button::onClick


#1

Just a heads-up that I’m adding a couple of new helper objects: Button::onClick and Button::onStateChange, which were an experiment in finding an easy way to attach lambdas to event callbacks. I did a quick whizz through the codebase to change a load of example code to use them, so you can take a look at how much it simplifies the code - in almost every case it was shorter, cleaner and nicer to use than Button::Listener.

Since I’m pleased with the result, I’ll roll out some more use of this style for other classes as we go on - requests for which ones you want added are welcome!

Should all be up on the develop branch shortly…


#2

Wait, was this quote of mine meant to go here?


#3

oh, how odd… I had two windows open… must have got mixed up!


#4

And sorry - scratch the bit about a new class called EventHandler… Just spotted a flaw in that idea which makes it no easier than using std::function directly, which I will do…


#5

we love lambdas!


#6

You can add lambda for Timer and AsyncUpdater as well so it remove the need to inherit and allow multiple of those in a single class :slight_smile:

Thanks !


#7

Timer lambda exists as callAfterDelay:

https://juce.com/doc/classTimer#ae7100e8b84d2a8d6adac3d14cbbd27d6

AsyncUpdater doesn’t really make sense when it comes to lambdas because the point is to have a single method (handleAsyncUpdate) that gets marked as needing to be called. If you’re looking for a method that is “call this lambda on the message thread at some async point in the future” look at MessageManager::callAsync. Just be aware that unlike AsyncUpdater it allocates on the message queue, so it’s not safe to call from a high priority thread.


#8

I think the two use cases requested here are different from Timer:: callAfterDelay and MessageManager::callAsync. Both of these are single trigger, deferred callbacks.

A LambdaTimer would be used to repeatedly call a lambda at a specific interval, a LambdaAsyncUpdater would be used to coalesce multiple calls in to a single callback. I gave examples with code of both in my ADC 2016 presentation: on pages 19 and 22 respectively.

class LambdaTimer   : public juce::Timer
{
public:
    LambdaTimer() = default;

    LambdaTimer (std::function<void()> newCallback)
    {
        callback = std::move (newCallback);
    }

    LambdaTimer& setCallback (std::function<void()> newCallback)
    {
        callback = std::move (newCallback);
        return *this;
    }

    void timerCallback() override
    {
        if (callback)
            callback();
    }

private:
    std::function<void()> callback;
};


//==============================================================================
/**
    Asyncronously call a function.
 */
struct LambdaAsyncUpdater  : public juce::AsyncUpdater
{
    /** Creates an empty LambdaAsyncUpdater. */
    LambdaAsyncUpdater() = default;

    /** Destructor. */
    ~LambdaAsyncUpdater()
    {
        cancelPendingUpdate();
    }

    /** Sets the function to call. */
    LambdaAsyncUpdater& setFunction (std::function<void()> f)
    {
        function = std::move (f);
        return *this;
    }

    /** @internal. */
    void handleAsyncUpdate() override
    {
        jassert (function);
        function();
    }

    std::function<void()> function;
};

#9

So, does this mark the death of the whole Listener design being used in JUCE?
relevant threads:



#10

Aha, similar to the class I posted the other day:


#11

No - we’ll leave the listeners in there because sometimes you need to attach multiple listeners or to a component, or manage adding/removing them in more complex situations. But for the 95% of normal situations where you just need to attach one quick, simple callback to something like a button, this pattern makes the code much simpler.


#12

an interesting design for this would be that the lambda can be a proxy listener list if you require multiple listeners. The lambda can capture the previous lambda and call it as well as the new one.


#13

Here is a design which combines Listeners & Lambdas, best of two worlds:


#14

perhaps we could attach a lambda to the ChangeBroadcaster, so that we don’t have to inherits ChangeListener every time?


#15

And with multiple listeners? You may override accidentally other “listeners” with this approach.

Here is another approach: