Skip to content

Implementing listeners

Vladimir Zahradnik edited this page Aug 13, 2019 · 3 revisions

In order to use our library, user needs to implement a listener and register for events. When an event occurs, our library will call one of the functions implemented by an user to do some specific action.

Create a listener

As a part of our library, we've created several abstract classes, representing interfaces to be implemented by an user. These interfaces represent an API (a contract), so that there's a guarantee that an object registered as a listener does provide all the required member functions.

Create an OnClickListener

To create an listener, which is able to receive OnClick events, create a class, which inherits from IOnClickListener interface and provide implementation for an onClick(...) callback function, i.e.:

#include <ObjectButton.h>
#include <interfaces/IOnClickListener.h>

class OnClickListener : private virtual IOnClickListener {
public:
    OnClickListener() = default;
    ....

private:
    void onClick(ObjectButton &button) override;
    ...
};

void OnClickListener::onClick(ObjectButton &button) {
  // React to button click
}

Create an OnDoubleClickListener

Very similarily, to create an listener, which is able to receive OnDoubleClick events, create a class, which inherits from IOnDoubleClickListener interface and provide implementation for an onDoubleClick(...) callback function, i.e.:

#include <ObjectButton.h>
#include <interfaces/IOnClickListener.h>

class OnDoubleClickListener : private virtual IOnDoubleClickListener {
public:
    OnDoubleClickListener() = default;
    ....

private:
    void onDoubleClick(ObjectButton &button) override;
    ...
};

void OnDoubleClickListener::onDoubleClick(ObjectButton &button) {
  // React to button click
}

Create an OnPressListener

Finally, to create an listener, which is able to receive OnPress, OnRelease, OnLongPressStart and OnLongPressEnd events, create a class, which inherits from IOnPressListener interface and provide implementation for member functions defined in the OnPressListener.

Note: If you are not interested in some of the events received by this listener, you could provide void implementation of a specific callback function. See an example below.

#include <ObjectButton.h>
#include <interfaces/IOnPressListener.h>

class OnPressListener : private virtual IOnPressListener {
public:
    OnPressListener() = default;
    ...

private:
    // Void implementation, which does nothing
    void onPress(ObjectButton &button) override {};

    // Void implementation, which does nothing
    void onRelease(ObjectButton &button) override {};

    void onLongPressStart(ObjectButton &button) override;

    void onLongPressEnd(ObjectButton &button) override;
    ...
};

void OnPressListener::onLongPressStart(ObjectButton &button) {
    // Do some action after long press has started
}

void OnPressListener::onLongPressEnd(ObjectButton &button) {
    // Do some action after long press has started
}

Register a listener

Now when you have a listener, you can finally register it to receive events.

// Create an ObjectButton instance
ObjectButton button = ObjectButton(...);

/* 
 * Assign some listeners to the button
 * If you don't assign a listener, specific event won't be reported.
 */
OnClickListener onClickListener = OnClickListener();
button.setOnClickListener(onClickListener);

OnDoubleClickListener onDoubleClickListener = OnDoubleClickListener();
button.setOnDoubleClickListener(onDoubleClickListener);

OnPressListener onPressListener = OnPressListener();
button.setOnPressListener(onPressListener);

// Optionally, set additional attributes for the button
button.setDebounceTicks(10);
...

Object composition

Having an listener as a C++ object allows us to initialize ObjectButton instance internally inside this listener and encapsulate the button in internal logic of the listener. Here is a recommended approach to use.

#include <ObjectButton.h>
#include <interfaces/IOnClickListener.h>

class OnClickListener : private virtual IOnClickListener {
public:
    // Initialize the listener
    void init();

    // Update the listener
    void update();
    ....

private:
    // Store button object internally
    ObjectButton button = ObjectButton(INPUT_PIN);
    ...
};

void OnClickListener::init() {
    button.setDebounceTicks(10);
    button.setOnClickListener(this);
}

void OnClickListener::update() {
    button.tick();
}

/* Create a listener instance, which will internally
   initialize a button */
OnClickListener onClickListener = OnClickListener();

void setup() {
    // Setup logic encapsulated inside object
    onClickListener.init();
}

void loop() {
    /* Update logic encapsulated inside object;
       internally button.onTick() function is called */
    onClickListener.update();
}

Create a composite listener

If you want to receive onClick events, but also onPress events, you could implement just one listener. This will inherit both from IOnClickListener and IOnPressListener classes. Here is an example of an listener, which is able to receive all kinds of events.

#include <ObjectButton.h>
#include <interfaces/IOnClickListener.h>
#include <interfaces/IOnDoubleClickListener.h>
#include <interfaces/IOnPressListener.h>

class CompositeListener : private virtual IOnClickListener,
                          private virtual IOnDoubleClickListener, private virtual IOnPressListener {
public:
    CompositeListener() = default;
    ...

private:
    // Implement functions from all inherited classes
    void onClick(ObjectButton &button) override;

    void onDoubleClick(ObjectButton &button) override;

    void onPress(ObjectButton &button) override;
    ....
};

...

// Register a listener for all events
ObjectButton button = ObjectButton(...);
CompositeListener listener = CompositeListener();

button.setOnClickListener(this);
button.setOnDoubleClickListener(this);
button.setOnPressListener(this);
Clone this wiki locally