Files
allwpilib/wpilibNewCommands/src/main/native/include/frc2/command/button/Trigger.h
Oblarg cba21a768f Fix C++ JoystickButton and POVButton (#2259)
C++ JoystickButton and POVButton were both nonfunctional due to slicing when trigger passes itself by value to the button scheduler it creates.

Fix is to remove the virtual Get() method entirely and use only the m_isActive functor; since the subclass now passes the button condition back as a functor to the base class, in which it's stored as a member, it will now still work after being sliced.
2020-01-12 14:57:28 -08:00

355 lines
12 KiB
C++

/*----------------------------------------------------------------------------*/
/* Copyright (c) 2019-2020 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#pragma once
#include <functional>
#include <initializer_list>
#include <memory>
#include <utility>
#include <wpi/ArrayRef.h>
#include "frc2/command/Command.h"
#include "frc2/command/CommandScheduler.h"
namespace frc2 {
class Command;
/**
* A class used to bind command scheduling to events. The
* Trigger class is a base for all command-event-binding classes, and so the
* methods are named fairly abstractly; for purpose-specific wrappers, see
* Button.
*
* @see Button
*/
class Trigger {
public:
/**
* Create a new trigger that is active when the given condition is true.
*
* @param isActive Whether the trigger is active.
*/
explicit Trigger(std::function<bool()> isActive)
: m_isActive{std::move(isActive)} {}
/**
* Create a new trigger that is never active (default constructor) - activity
* can be further determined by subclass code.
*/
Trigger() {
m_isActive = [] { return false; };
}
Trigger(const Trigger& other);
/**
* Binds a command to start when the trigger becomes active. Takes a
* raw pointer, and so is non-owning; users are responsible for the lifespan
* of the command.
*
* @param command The command to bind.
* @param interruptible Whether the command should be interruptible.
* @return The trigger, for chained calls.
*/
Trigger WhenActive(Command* command, bool interruptible = true);
/**
* Binds a command to start when the trigger becomes active. Transfers
* command ownership to the button scheduler, so the user does not have to
* worry about lifespan - rvalue refs will be *moved*, lvalue refs will be
* *copied.*
*
* @param command The command to bind.
* @param interruptible Whether the command should be interruptible.
* @return The trigger, for chained calls.
*/
template <class T, typename = std::enable_if_t<std::is_base_of_v<
Command, std::remove_reference_t<T>>>>
Trigger WhenActive(T&& command, bool interruptible = true) {
CommandScheduler::GetInstance().AddButton(
[pressedLast = m_isActive(), *this,
command = std::make_unique<std::remove_reference_t<T>>(
std::forward<T>(command)),
interruptible]() mutable {
bool pressed = m_isActive();
if (!pressedLast && pressed) {
command->Schedule(interruptible);
}
pressedLast = pressed;
});
return *this;
}
/**
* Binds a runnable to execute when the trigger becomes active.
*
* @param toRun the runnable to execute.
* @paaram requirements the required subsystems.
*/
Trigger WhenActive(std::function<void()> toRun,
std::initializer_list<Subsystem*> requirements);
/**
* Binds a runnable to execute when the trigger becomes active.
*
* @param toRun the runnable to execute.
* @paaram requirements the required subsystems.
*/
Trigger WhenActive(std::function<void()> toRun,
wpi::ArrayRef<Subsystem*> requirements = {});
/**
* Binds a command to be started repeatedly while the trigger is active, and
* cancelled when it becomes inactive. Takes a raw pointer, and so is
* non-owning; users are responsible for the lifespan of the command.
*
* @param command The command to bind.
* @param interruptible Whether the command should be interruptible.
* @return The trigger, for chained calls.
*/
Trigger WhileActiveContinous(Command* command, bool interruptible = true);
/**
* Binds a command to be started repeatedly while the trigger is active, and
* cancelled when it becomes inactive. Transfers command ownership to the
* button scheduler, so the user does not have to worry about lifespan -
* rvalue refs will be *moved*, lvalue refs will be *copied.*
*
* @param command The command to bind.
* @param interruptible Whether the command should be interruptible.
* @return The trigger, for chained calls.
*/
template <class T, typename = std::enable_if_t<std::is_base_of_v<
Command, std::remove_reference_t<T>>>>
Trigger WhileActiveContinous(T&& command, bool interruptible = true) {
CommandScheduler::GetInstance().AddButton(
[pressedLast = m_isActive(), *this,
command = std::make_unique<std::remove_reference_t<T>>(
std::forward<T>(command)),
interruptible]() mutable {
bool pressed = m_isActive();
if (pressed) {
command->Schedule(interruptible);
} else if (pressedLast && !pressed) {
command->Cancel();
}
pressedLast = pressed;
});
return *this;
}
/**
* Binds a runnable to execute repeatedly while the trigger is active.
*
* @param toRun the runnable to execute.
* @param requirements the required subsystems.
*/
Trigger WhileActiveContinous(std::function<void()> toRun,
std::initializer_list<Subsystem*> requirements);
/**
* Binds a runnable to execute repeatedly while the trigger is active.
*
* @param toRun the runnable to execute.
* @param requirements the required subsystems.
*/
Trigger WhileActiveContinous(std::function<void()> toRun,
wpi::ArrayRef<Subsystem*> requirements = {});
/**
* Binds a command to be started when the trigger becomes active, and
* cancelled when it becomes inactive. Takes a raw pointer, and so is
* non-owning; users are responsible for the lifespan of the command.
*
* @param command The command to bind.
* @param interruptible Whether the command should be interruptible.
* @return The trigger, for chained calls.
*/
Trigger WhileActiveOnce(Command* command, bool interruptible = true);
/**
* Binds a command to be started when the trigger becomes active, and
* cancelled when it becomes inactive. Transfers command ownership to the
* button scheduler, so the user does not have to worry about lifespan -
* rvalue refs will be *moved*, lvalue refs will be *copied.*
*
* @param command The command to bind.
* @param interruptible Whether the command should be interruptible.
* @return The trigger, for chained calls.
*/
template <class T, typename = std::enable_if_t<std::is_base_of_v<
Command, std::remove_reference_t<T>>>>
Trigger WhileActiveOnce(T&& command, bool interruptible = true) {
CommandScheduler::GetInstance().AddButton(
[pressedLast = m_isActive(), *this,
command = std::make_unique<std::remove_reference_t<T>>(
std::forward<T>(command)),
interruptible]() mutable {
bool pressed = m_isActive();
if (!pressedLast && pressed) {
command->Schedule(interruptible);
} else if (pressedLast && !pressed) {
command->Cancel();
}
pressedLast = pressed;
});
return *this;
}
/**
* Binds a command to start when the trigger becomes inactive. Takes a
* raw pointer, and so is non-owning; users are responsible for the lifespan
* of the command.
*
* @param command The command to bind.
* @param interruptible Whether the command should be interruptible.
* @return The trigger, for chained calls.
*/
Trigger WhenInactive(Command* command, bool interruptible = true);
/**
* Binds a command to start when the trigger becomes inactive. Transfers
* command ownership to the button scheduler, so the user does not have to
* worry about lifespan - rvalue refs will be *moved*, lvalue refs will be
* *copied.*
*
* @param command The command to bind.
* @param interruptible Whether the command should be interruptible.
* @return The trigger, for chained calls.
*/
template <class T, typename = std::enable_if_t<std::is_base_of_v<
Command, std::remove_reference_t<T>>>>
Trigger WhenInactive(T&& command, bool interruptible = true) {
CommandScheduler::GetInstance().AddButton(
[pressedLast = m_isActive(), *this,
command = std::make_unique<std::remove_reference_t<T>>(
std::forward<T>(command)),
interruptible]() mutable {
bool pressed = m_isActive();
if (pressedLast && !pressed) {
command->Schedule(interruptible);
}
pressedLast = pressed;
});
return *this;
}
/**
* Binds a runnable to execute when the trigger becomes inactive.
*
* @param toRun the runnable to execute.
* @param requirements the required subsystems.
*/
Trigger WhenInactive(std::function<void()> toRun,
std::initializer_list<Subsystem*> requirements);
/**
* Binds a runnable to execute when the trigger becomes inactive.
*
* @param toRun the runnable to execute.
* @param requirements the required subsystems.
*/
Trigger WhenInactive(std::function<void()> toRun,
wpi::ArrayRef<Subsystem*> requirements = {});
/**
* Binds a command to start when the trigger becomes active, and be cancelled
* when it again becomes active. Takes a raw pointer, and so is non-owning;
* users are responsible for the lifespan of the command.
*
* @param command The command to bind.
* @param interruptible Whether the command should be interruptible.
* @return The trigger, for chained calls.
*/
Trigger ToggleWhenActive(Command* command, bool interruptible = true);
/**
* Binds a command to start when the trigger becomes active, and be cancelled
* when it again becomes active. Transfers command ownership to the button
* scheduler, so the user does not have to worry about lifespan - rvalue refs
* will be *moved*, lvalue refs will be *copied.*
*
* @param command The command to bind.
* @param interruptible Whether the command should be interruptible.
* @return The trigger, for chained calls.
*/
template <class T, typename = std::enable_if_t<std::is_base_of_v<
Command, std::remove_reference_t<T>>>>
Trigger ToggleWhenActive(T&& command, bool interruptible = true) {
CommandScheduler::GetInstance().AddButton(
[pressedLast = m_isActive(), *this,
command = std::make_unique<std::remove_reference_t<T>>(
std::forward<T>(command)),
interruptible]() mutable {
bool pressed = m_isActive();
if (!pressedLast && pressed) {
if (command->IsScheduled()) {
command->Cancel();
} else {
command->Schedule(interruptible);
}
}
pressedLast = pressed;
});
return *this;
}
/**
* Binds a command to be cancelled when the trigger becomes active. Takes a
* raw pointer, and so is non-owning; users are responsible for the lifespan
* and scheduling of the command.
*
* @param command The command to bind.
* @return The trigger, for chained calls.
*/
Trigger CancelWhenActive(Command* command);
/**
* Composes two triggers with logical AND.
*
* @return A trigger which is active when both component triggers are active.
*/
Trigger operator&&(Trigger rhs) {
return Trigger([*this, rhs] { return m_isActive() && rhs.m_isActive(); });
}
/**
* Composes two triggers with logical OR.
*
* @return A trigger which is active when either component trigger is active.
*/
Trigger operator||(Trigger rhs) {
return Trigger([*this, rhs] { return m_isActive() || rhs.m_isActive(); });
}
/**
* Composes a trigger with logical NOT.
*
* @return A trigger which is active when the component trigger is inactive,
* and vice-versa.
*/
Trigger operator!() {
return Trigger([*this] { return !m_isActive(); });
}
private:
std::function<bool()> m_isActive;
};
} // namespace frc2