Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: ✨ add onLongRelease and onRepeatPress events #20

Merged
merged 8 commits into from
Oct 25, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 54 additions & 2 deletions include/gamepad/button.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#pragma once

#include <cstdint>
#include <functional>
#include <string>

#include "event_handler.hpp"
Expand All @@ -10,6 +12,8 @@ enum EventType {
ON_LONG_PRESS,
ON_RELEASE,
ON_SHORT_RELEASE,
ON_LONG_RELEASE,
ON_REPEAT_PRESS,
};

class Button {
Expand All @@ -25,8 +29,12 @@ class Button {
uint32_t time_held = 0;
/// How long the button has been released
uint32_t time_released = 0;
// How many times the button has been repeat-pressed
PA055 marked this conversation as resolved.
Show resolved Hide resolved
uint32_t repeat_iterations = 0;
/// How long the threshold should be for the longPress and shortRelease events
uint32_t long_press_threshold = 500;
// How often the repeatPress is called
PA055 marked this conversation as resolved.
Show resolved Hide resolved
uint32_t repeat_cooldown = 50;
/**
* @brief Register a function to run when the button is pressed.
*
Expand Down Expand Up @@ -103,10 +111,50 @@ class Button {
* // Use a function...
* gamepad::master.A.onShortRelease("raiseLiftOneLevel", raiseLiftOneLevel);
* // ...or a lambda
* gamepad::master.B.onShortRelease("intakeOnePicce", []() { intake.move_relative(600, 100); });
* gamepad::master.B.onShortRelease("intakeOnePiece", []() { intake.move_relative(600, 100); });
* @endcode
*/
bool onShortRelease(std::string listenerName, std::function<void(void)> func) const;
/**
* @brief Register a function to run when the button is long released.
* By default, longRelease will fire when the button has been released after 500ms, this threshold can be
* adjusted by changing long_press_threshold.
PA055 marked this conversation as resolved.
Show resolved Hide resolved
*
* @param listenerName The name of the listener, this must be a unique name
* @param func The function to run when the button is long released, the function MUST NOT block
* @return true The listener was successfully registered
* @return false The listener was not successfully registered (there is already a listener with this name)
*
* @b Example:
* @code {.cpp}
* // Use a function...
* gamepad::master.A.onLongRelease("moveLiftToGround", moveLiftToGround);
* // ...or a lambda
* gamepad::master.B.onLongRelease("spinIntake", []() { intake.move(127); });
* @endcode
*
*/
bool onLongRelease(std::string listenerName, std::function<void(void)> func) const;
/**
* @brief Register a function to run periodically after its been held
* By default repeatPress will start repeating after 500ms and repeat every 50ms, this can be changed by
* adjusting long_press_threshold and repeat_cooldown respectivly
PA055 marked this conversation as resolved.
Show resolved Hide resolved
*
* @param listenerName The name of the listener, this must be a unique name
* @param func the function to run periodically when the button is held, the function MUST NOT block
* @return true The listener was successfully registered
* @return false The listener was not successfully registered (there is already a listener with this name)
*
* @b Example:
* @code {.cpp}
* // Use a function...
* gamepad::master.A.onRepeatPress("shootDisk", shootOneDisk);
* // ...or a lambda
* gamepad::master.B.onRepeatPress("scoreOneRing", []() { intake.move_relative(200, 100); });
* @endcode
*
*/
bool onRepeatPress(std::string listenerName, std::function<void(void)> func) const;
/**
* @brief Register a function to run for a given event.
*
Expand Down Expand Up @@ -157,13 +205,17 @@ class Button {
* @param is_held Whether or not the button is currently held down
*/
void update(bool is_held);
/// he last time the update function was called
/// The last time the update function was called
uint32_t last_update_time = pros::millis();
/// The last time the long press event was fired
uint32_t last_long_press_time = 0;
// The last time the repeat event was called
uint32_t last_repeat_time = 0;
mutable _impl::EventHandler<std::string> onPressEvent {};
mutable _impl::EventHandler<std::string> onLongPressEvent {};
mutable _impl::EventHandler<std::string> onReleaseEvent {};
mutable _impl::EventHandler<std::string> onShortReleaseEvent {};
mutable _impl::EventHandler<std::string> onLongReleaseEvent {};
mutable _impl::EventHandler<std::string> onRepeatPressEvent {};
};
} // namespace gamepad
21 changes: 20 additions & 1 deletion src/gamepad/button.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,23 @@ bool Button::onShortRelease(std::string listenerName, std::function<void(void)>
return this->onShortReleaseEvent.add_listener(std::move(listenerName) + "_user", std::move(func));
}

bool Button::onLongRelease(std::string listenerName, std::function<void(void)> func) const {
return this->onLongReleaseEvent.add_listener(std::move(listenerName) + "_user", std::move(func));
}

bool Button::onRepeatPress(std::string listenerName, std::function<void(void)> func) const {
return this->onRepeatPressEvent.add_listener(std::move(listenerName) + "_user", std::move(func));
}

bool Button::addListener(EventType event, std::string listenerName, std::function<void(void)> func) const {
switch (event) {
case gamepad::EventType::ON_PRESS: return this->onPress(std::move(listenerName), std::move(func));
case gamepad::EventType::ON_LONG_PRESS: return this->onLongPress(std::move(listenerName), std::move(func));
case gamepad::EventType::ON_RELEASE: return this->onRelease(std::move(listenerName), std::move(func));
case gamepad::EventType::ON_SHORT_RELEASE:
return this->onShortRelease(std::move(listenerName), std::move(func));
case gamepad::EventType::ON_LONG_RELEASE: return this->onLongRelease(std::move(listenerName), std::move(func));
case gamepad::EventType::ON_REPEAT_PRESS: return this->onRepeatPress(std::move(listenerName), std::move(func));
default:
TODO("add error logging")
errno = EINVAL;
Expand All @@ -37,7 +47,9 @@ bool Button::removeListener(std::string listenerName) const {
return this->onPressEvent.remove_listener(listenerName + "_user") ||
this->onLongPressEvent.remove_listener(listenerName + "_user") ||
this->onReleaseEvent.remove_listener(listenerName + "_user") ||
this->onShortReleaseEvent.remove_listener(listenerName + "_user");
this->onShortReleaseEvent.remove_listener(listenerName + "_user") ||
this->onLongReleaseEvent.remove_listener(listenerName + "_user") ||
this->onRepeatPressEvent.remove_listener(listenerName + "_user");
}

void Button::update(const bool is_held) {
Expand All @@ -53,9 +65,16 @@ void Button::update(const bool is_held) {
this->last_long_press_time <= pros::millis() - this->time_held) {
this->onLongPressEvent.fire();
this->last_long_press_time = pros::millis();
this->last_repeat_time = pros::millis();
this->repeat_iterations = 0;
} else if (this->is_pressed && this->time_held >= this->long_press_threshold && pros::millis() - this->last_repeat_time > this->repeat_cooldown) {
this->repeat_iterations++;
this->onRepeatPressEvent.fire();
this->last_repeat_time = pros::millis();
} else if (this->falling_edge) {
this->onReleaseEvent.fire();
if (this->time_held < this->long_press_threshold) this->onShortReleaseEvent.fire();
else this->onLongReleaseEvent.fire();
}
if (this->rising_edge) this->time_held = 0;
if (this->falling_edge) this->time_released = 0;
Expand Down
Loading