diff --git a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/Trigger.java b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/Trigger.java index d7b1dd08937..c8d16e627ca 100644 --- a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/Trigger.java +++ b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/Trigger.java @@ -24,6 +24,34 @@ *
This class is provided by the NewCommands VendorDep */ public class Trigger implements BooleanSupplier { + /** + * Enum specifying the initial state to use for a binding. This impacts whether or not the binding + * will be triggered immediately. + */ + public enum InitialState { + /** + * Indicates the binding should use false as the initial value. This causes a rising edge at the + * start if and only if the condition starts true. + */ + FALSE, + /** + * Indicates the binding should use true as the initial value. This causes a falling edge at the + * start if and only if the condition starts false. + */ + TRUE, + /** + * Indicates the binding should use the trigger's condition as the initial value. This never + * causes an edge at the start. + */ + CONDITION, + /** + * Indicates the binding should use the negated trigger's condition as the initial value. This + * always causes an edge at the start. Rising or falling depends on if the condition starts true + * or false, respectively. + */ + NEG_CONDITION; + } + private final BooleanSupplier m_condition; private final EventLoop m_loop; @@ -50,16 +78,42 @@ public Trigger(BooleanSupplier condition) { } /** - * Starts the command when the condition changes. + * Gets the initial state for a binding based on an initial state policy. + * + * @param initialState Initial state policy. + * @return The initial state to use. + */ + private boolean getInitialState(InitialState initialState) { + return switch (initialState) { + case FALSE -> false; + case TRUE -> true; + case CONDITION -> m_condition.getAsBoolean(); + case NEG_CONDITION -> !m_condition.getAsBoolean(); + }; + } + + /** + * Starts the command when the condition changes. The command is never started immediately. * * @param command the command to start * @return this trigger, so calls can be chained */ public Trigger onChange(Command command) { + return onChange(command, InitialState.CONDITION); + } + + /** + * Starts the command when the condition changes. + * + * @param command the command to start + * @param initialState the initial state to use + * @return this trigger, so calls can be chained + */ + public Trigger onChange(Command command, InitialState initialState) { requireNonNullParam(command, "command", "onChange"); m_loop.bind( new Runnable() { - private boolean m_pressedLast = m_condition.getAsBoolean(); + private boolean m_pressedLast = getInitialState(initialState); @Override public void run() { @@ -76,16 +130,28 @@ public void run() { } /** - * Starts the given command whenever the condition changes from `false` to `true`. + * Starts the given command whenever the condition changes from `false` to `true`. The command is + * never started immediately. * * @param command the command to start * @return this trigger, so calls can be chained */ public Trigger onTrue(Command command) { + return onTrue(command, InitialState.CONDITION); + } + + /** + * Starts the given command whenever the condition changes from `false` to `true`. + * + * @param command the command to start + * @param initialState the initial state to use + * @return this trigger, so calls can be chained + */ + public Trigger onTrue(Command command, InitialState initialState) { requireNonNullParam(command, "command", "onTrue"); m_loop.bind( new Runnable() { - private boolean m_pressedLast = m_condition.getAsBoolean(); + private boolean m_pressedLast = getInitialState(initialState); @Override public void run() { @@ -102,16 +168,28 @@ public void run() { } /** - * Starts the given command whenever the condition changes from `true` to `false`. + * Starts the given command whenever the condition changes from `true` to `false`. The command is + * never started immediately. * * @param command the command to start * @return this trigger, so calls can be chained */ public Trigger onFalse(Command command) { + return onFalse(command, InitialState.CONDITION); + } + + /** + * Starts the given command whenever the condition changes from `true` to `false`. + * + * @param command the command to start + * @param initialState the initial state to use + * @return this trigger, so calls can be chained + */ + public Trigger onFalse(Command command, InitialState initialState) { requireNonNullParam(command, "command", "onFalse"); m_loop.bind( new Runnable() { - private boolean m_pressedLast = m_condition.getAsBoolean(); + private boolean m_pressedLast = getInitialState(initialState); @Override public void run() { @@ -129,7 +207,7 @@ public void run() { /** * Starts the given command when the condition changes to `true` and cancels it when the condition - * changes to `false`. + * changes to `false`. The command is never started immediately. * *
Doesn't re-start the command if it ends while the condition is still `true`. If the command * should restart, see {@link edu.wpi.first.wpilibj2.command.RepeatCommand}. @@ -138,10 +216,25 @@ public void run() { * @return this trigger, so calls can be chained */ public Trigger whileTrue(Command command) { + return whileTrue(command, InitialState.CONDITION); + } + + /** + * Starts the given command when the condition changes to `true` and cancels it when the condition + * changes to `false`. + * + *
Doesn't re-start the command if it ends while the condition is still `true`. If the command + * should restart, see {@link edu.wpi.first.wpilibj2.command.RepeatCommand}. + * + * @param command the command to start + * @param initialState the initial state to use + * @return this trigger, so calls can be chained + */ + public Trigger whileTrue(Command command, InitialState initialState) { requireNonNullParam(command, "command", "whileTrue"); m_loop.bind( new Runnable() { - private boolean m_pressedLast = m_condition.getAsBoolean(); + private boolean m_pressedLast = getInitialState(initialState); @Override public void run() { @@ -161,7 +254,7 @@ public void run() { /** * Starts the given command when the condition changes to `false` and cancels it when the - * condition changes to `true`. + * condition changes to `true`. The command is never started immediately. * *
Doesn't re-start the command if it ends while the condition is still `false`. If the command * should restart, see {@link edu.wpi.first.wpilibj2.command.RepeatCommand}. @@ -170,10 +263,25 @@ public void run() { * @return this trigger, so calls can be chained */ public Trigger whileFalse(Command command) { + return whileFalse(command, InitialState.CONDITION); + } + + /** + * Starts the given command when the condition changes to `false` and cancels it when the + * condition changes to `true`. + * + *
Doesn't re-start the command if it ends while the condition is still `false`. If the command
+ * should restart, see {@link edu.wpi.first.wpilibj2.command.RepeatCommand}.
+ *
+ * @param command the command to start
+ * @param initialState the initial state to use
+ * @return this trigger, so calls can be chained
+ */
+ public Trigger whileFalse(Command command, InitialState initialState) {
requireNonNullParam(command, "command", "whileFalse");
m_loop.bind(
new Runnable() {
- private boolean m_pressedLast = m_condition.getAsBoolean();
+ private boolean m_pressedLast = getInitialState(initialState);
@Override
public void run() {
@@ -192,16 +300,28 @@ public void run() {
}
/**
- * Toggles a command when the condition changes from `false` to `true`.
+ * Toggles a command when the condition changes from `false` to `true`. The command is never
+ * toggled immediately.
*
* @param command the command to toggle
* @return this trigger, so calls can be chained
*/
public Trigger toggleOnTrue(Command command) {
+ return toggleOnTrue(command, InitialState.CONDITION);
+ }
+
+ /**
+ * Toggles a command when the condition changes from `false` to `true`.
+ *
+ * @param command the command to toggle
+ * @param initialState the initial state to use
+ * @return this trigger, so calls can be chained
+ */
+ public Trigger toggleOnTrue(Command command, InitialState initialState) {
requireNonNullParam(command, "command", "toggleOnTrue");
m_loop.bind(
new Runnable() {
- private boolean m_pressedLast = m_condition.getAsBoolean();
+ private boolean m_pressedLast = getInitialState(initialState);
@Override
public void run() {
@@ -222,16 +342,28 @@ public void run() {
}
/**
- * Toggles a command when the condition changes from `true` to `false`.
+ * Toggles a command when the condition changes from `true` to `false`. The command is never
+ * toggled immediately.
*
* @param command the command to toggle
* @return this trigger, so calls can be chained
*/
public Trigger toggleOnFalse(Command command) {
+ return toggleOnFalse(command, InitialState.CONDITION);
+ }
+
+ /**
+ * Toggles a command when the condition changes from `true` to `false`.
+ *
+ * @param command the command to toggle
+ * @param initialState the initial state to use
+ * @return this trigger, so calls can be chained
+ */
+ public Trigger toggleOnFalse(Command command, InitialState initialState) {
requireNonNullParam(command, "command", "toggleOnFalse");
m_loop.bind(
new Runnable() {
- private boolean m_pressedLast = m_condition.getAsBoolean();
+ private boolean m_pressedLast = getInitialState(initialState);
@Override
public void run() {
diff --git a/wpilibNewCommands/src/main/native/cpp/frc2/command/button/Trigger.cpp b/wpilibNewCommands/src/main/native/cpp/frc2/command/button/Trigger.cpp
index a3b02d18f3a..420ff488d19 100644
--- a/wpilibNewCommands/src/main/native/cpp/frc2/command/button/Trigger.cpp
+++ b/wpilibNewCommands/src/main/native/cpp/frc2/command/button/Trigger.cpp
@@ -15,22 +15,23 @@ using namespace frc2;
Trigger::Trigger(const Trigger& other) = default;
-Trigger Trigger::OnChange(Command* command) {
- m_loop->Bind(
- [condition = m_condition, previous = m_condition(), command]() mutable {
- bool current = condition();
+Trigger Trigger::OnChange(Command* command, InitialState initialState) {
+ m_loop->Bind([condition = m_condition,
+ previous = GetInitialState(initialState), command]() mutable {
+ bool current = condition();
- if (previous != current) {
- command->Schedule();
- }
+ if (previous != current) {
+ command->Schedule();
+ }
- previous = current;
- });
+ previous = current;
+ });
return *this;
}
-Trigger Trigger::OnChange(CommandPtr&& command) {
- m_loop->Bind([condition = m_condition, previous = m_condition(),
+Trigger Trigger::OnChange(CommandPtr&& command, InitialState initialState) {
+ m_loop->Bind([condition = m_condition,
+ previous = GetInitialState(initialState),
command = std::move(command)]() mutable {
bool current = condition();
@@ -43,22 +44,23 @@ Trigger Trigger::OnChange(CommandPtr&& command) {
return *this;
}
-Trigger Trigger::OnTrue(Command* command) {
- m_loop->Bind(
- [condition = m_condition, previous = m_condition(), command]() mutable {
- bool current = condition();
+Trigger Trigger::OnTrue(Command* command, InitialState initialState) {
+ m_loop->Bind([condition = m_condition,
+ previous = GetInitialState(initialState), command]() mutable {
+ bool current = condition();
- if (!previous && current) {
- command->Schedule();
- }
+ if (!previous && current) {
+ command->Schedule();
+ }
- previous = current;
- });
+ previous = current;
+ });
return *this;
}
-Trigger Trigger::OnTrue(CommandPtr&& command) {
- m_loop->Bind([condition = m_condition, previous = m_condition(),
+Trigger Trigger::OnTrue(CommandPtr&& command, InitialState initialState) {
+ m_loop->Bind([condition = m_condition,
+ previous = GetInitialState(initialState),
command = std::move(command)]() mutable {
bool current = condition();
@@ -71,22 +73,23 @@ Trigger Trigger::OnTrue(CommandPtr&& command) {
return *this;
}
-Trigger Trigger::OnFalse(Command* command) {
- m_loop->Bind(
- [condition = m_condition, previous = m_condition(), command]() mutable {
- bool current = condition();
+Trigger Trigger::OnFalse(Command* command, InitialState initialState) {
+ m_loop->Bind([condition = m_condition,
+ previous = GetInitialState(initialState), command]() mutable {
+ bool current = condition();
- if (previous && !current) {
- command->Schedule();
- }
+ if (previous && !current) {
+ command->Schedule();
+ }
- previous = current;
- });
+ previous = current;
+ });
return *this;
}
-Trigger Trigger::OnFalse(CommandPtr&& command) {
- m_loop->Bind([condition = m_condition, previous = m_condition(),
+Trigger Trigger::OnFalse(CommandPtr&& command, InitialState initialState) {
+ m_loop->Bind([condition = m_condition,
+ previous = GetInitialState(initialState),
command = std::move(command)]() mutable {
bool current = condition();
@@ -99,24 +102,25 @@ Trigger Trigger::OnFalse(CommandPtr&& command) {
return *this;
}
-Trigger Trigger::WhileTrue(Command* command) {
- m_loop->Bind(
- [condition = m_condition, previous = m_condition(), command]() mutable {
- bool current = condition();
+Trigger Trigger::WhileTrue(Command* command, InitialState initialState) {
+ m_loop->Bind([condition = m_condition,
+ previous = GetInitialState(initialState), command]() mutable {
+ bool current = condition();
- if (!previous && current) {
- command->Schedule();
- } else if (previous && !current) {
- command->Cancel();
- }
+ if (!previous && current) {
+ command->Schedule();
+ } else if (previous && !current) {
+ command->Cancel();
+ }
- previous = current;
- });
+ previous = current;
+ });
return *this;
}
-Trigger Trigger::WhileTrue(CommandPtr&& command) {
- m_loop->Bind([condition = m_condition, previous = m_condition(),
+Trigger Trigger::WhileTrue(CommandPtr&& command, InitialState initialState) {
+ m_loop->Bind([condition = m_condition,
+ previous = GetInitialState(initialState),
command = std::move(command)]() mutable {
bool current = condition();
@@ -131,24 +135,25 @@ Trigger Trigger::WhileTrue(CommandPtr&& command) {
return *this;
}
-Trigger Trigger::WhileFalse(Command* command) {
- m_loop->Bind(
- [condition = m_condition, previous = m_condition(), command]() mutable {
- bool current = condition();
+Trigger Trigger::WhileFalse(Command* command, InitialState initialState) {
+ m_loop->Bind([condition = m_condition,
+ previous = GetInitialState(initialState), command]() mutable {
+ bool current = condition();
- if (previous && !current) {
- command->Schedule();
- } else if (!previous && current) {
- command->Cancel();
- }
+ if (previous && !current) {
+ command->Schedule();
+ } else if (!previous && current) {
+ command->Cancel();
+ }
- previous = current;
- });
+ previous = current;
+ });
return *this;
}
-Trigger Trigger::WhileFalse(CommandPtr&& command) {
- m_loop->Bind([condition = m_condition, previous = m_condition(),
+Trigger Trigger::WhileFalse(CommandPtr&& command, InitialState initialState) {
+ m_loop->Bind([condition = m_condition,
+ previous = GetInitialState(initialState),
command = std::move(command)]() mutable {
bool current = condition();
@@ -163,8 +168,9 @@ Trigger Trigger::WhileFalse(CommandPtr&& command) {
return *this;
}
-Trigger Trigger::ToggleOnTrue(Command* command) {
- m_loop->Bind([condition = m_condition, previous = m_condition(),
+Trigger Trigger::ToggleOnTrue(Command* command, InitialState initialState) {
+ m_loop->Bind([condition = m_condition,
+ previous = GetInitialState(initialState),
command = command]() mutable {
bool current = condition();
@@ -181,8 +187,9 @@ Trigger Trigger::ToggleOnTrue(Command* command) {
return *this;
}
-Trigger Trigger::ToggleOnTrue(CommandPtr&& command) {
- m_loop->Bind([condition = m_condition, previous = m_condition(),
+Trigger Trigger::ToggleOnTrue(CommandPtr&& command, InitialState initialState) {
+ m_loop->Bind([condition = m_condition,
+ previous = GetInitialState(initialState),
command = std::move(command)]() mutable {
bool current = condition();
@@ -199,8 +206,9 @@ Trigger Trigger::ToggleOnTrue(CommandPtr&& command) {
return *this;
}
-Trigger Trigger::ToggleOnFalse(Command* command) {
- m_loop->Bind([condition = m_condition, previous = m_condition(),
+Trigger Trigger::ToggleOnFalse(Command* command, InitialState initialState) {
+ m_loop->Bind([condition = m_condition,
+ previous = GetInitialState(initialState),
command = command]() mutable {
bool current = condition();
@@ -217,8 +225,10 @@ Trigger Trigger::ToggleOnFalse(Command* command) {
return *this;
}
-Trigger Trigger::ToggleOnFalse(CommandPtr&& command) {
- m_loop->Bind([condition = m_condition, previous = m_condition(),
+Trigger Trigger::ToggleOnFalse(CommandPtr&& command,
+ InitialState initialState) {
+ m_loop->Bind([condition = m_condition,
+ previous = GetInitialState(initialState),
command = std::move(command)]() mutable {
bool current = condition();
diff --git a/wpilibNewCommands/src/main/native/include/frc2/command/button/Trigger.h b/wpilibNewCommands/src/main/native/include/frc2/command/button/Trigger.h
index a2382631334..067317b4d7c 100644
--- a/wpilibNewCommands/src/main/native/include/frc2/command/button/Trigger.h
+++ b/wpilibNewCommands/src/main/native/include/frc2/command/button/Trigger.h
@@ -30,6 +30,34 @@ class Command;
*/
class Trigger {
public:
+ /**
+ * Enum specifying the initial state to use for a binding. This impacts
+ * whether or not the binding will be triggered immediately.
+ */
+ enum struct InitialState {
+ /**
+ * Indicates the binding should use false as the initial value. This causes
+ * a rising edge at the start if and only if the condition starts true.
+ */
+ FALSE,
+ /**
+ * Indicates the binding should use true as the initial value. This causes a
+ * falling edge at the start if and only if the condition starts false.
+ */
+ TRUE,
+ /**
+ * Indicates the binding should use the trigger's condition as the initial
+ * value. This never causes an edge at the start.
+ */
+ CONDITION,
+ /**
+ * Indicates the binding should use the negated trigger's condition as the
+ * initial value. This always causes an edge at the start. Rising or falling
+ * depends on if the condition starts true or false, respectively.
+ */
+ NEG_CONDITION
+ };
+
/**
* Creates a new trigger based on the given condition.
*
@@ -61,18 +89,22 @@ class Trigger {
* Starts the command when the condition changes.
*
* @param command the command to start
+ * @param initialState the initial state to use
* @return this trigger, so calls can be chained
*/
- Trigger OnChange(Command* command);
+ Trigger OnChange(Command* command,
+ InitialState initialState = InitialState::CONDITION);
/**
* Starts the command when the condition changes. Moves command ownership to
* the button scheduler.
*
* @param command the command to start
+ * @param initialState the initial state to use
* @return this trigger, so calls can be chained
*/
- Trigger OnChange(CommandPtr&& command);
+ Trigger OnChange(CommandPtr&& command,
+ InitialState initialState = InitialState::CONDITION);
/**
* Starts the given command whenever the condition changes from `false` to
@@ -82,18 +114,22 @@ class Trigger {
* lifespan of the command.
*
* @param command the command to start
+ * @param initialState the initial state to use
* @return this trigger, so calls can be chained
*/
- Trigger OnTrue(Command* command);
+ Trigger OnTrue(Command* command,
+ InitialState initialState = InitialState::CONDITION);
/**
* Starts the given command whenever the condition changes from `false` to
* `true`. Moves command ownership to the button scheduler.
*
* @param command The command to bind.
+ * @param initialState the initial state to use
* @return The trigger, for chained calls.
*/
- Trigger OnTrue(CommandPtr&& command);
+ Trigger OnTrue(CommandPtr&& command,
+ InitialState initialState = InitialState::CONDITION);
/**
* Starts the given command whenever the condition changes from `true` to
@@ -103,18 +139,22 @@ class Trigger {
* lifespan of the command.
*
* @param command the command to start
+ * @param initialState the initial state to use
* @return this trigger, so calls can be chained
*/
- Trigger OnFalse(Command* command);
+ Trigger OnFalse(Command* command,
+ InitialState initialState = InitialState::CONDITION);
/**
* Starts the given command whenever the condition changes from `true` to
* `false`.
*
* @param command The command to bind.
+ * @param initialState the initial state to use
* @return The trigger, for chained calls.
*/
- Trigger OnFalse(CommandPtr&& command);
+ Trigger OnFalse(CommandPtr&& command,
+ InitialState initialState = InitialState::CONDITION);
/**
* Starts the given command when the condition changes to `true` and cancels
@@ -127,9 +167,11 @@ class Trigger {
* lifespan of the command.
*
* @param command the command to start
+ * @param initialState the initial state to use
* @return this trigger, so calls can be chained
*/
- Trigger WhileTrue(Command* command);
+ Trigger WhileTrue(Command* command,
+ InitialState initialState = InitialState::CONDITION);
/**
* Starts the given command when the condition changes to `true` and cancels
@@ -140,9 +182,11 @@ class Trigger {
* `true`. If the command should restart, see RepeatCommand.
*
* @param command The command to bind.
+ * @param initialState the initial state to use
* @return The trigger, for chained calls.
*/
- Trigger WhileTrue(CommandPtr&& command);
+ Trigger WhileTrue(CommandPtr&& command,
+ InitialState initialState = InitialState::CONDITION);
/**
* Starts the given command when the condition changes to `false` and cancels
@@ -155,9 +199,11 @@ class Trigger {
* lifespan of the command.
*
* @param command the command to start
+ * @param initialState the initial state to use
* @return this trigger, so calls can be chained
*/
- Trigger WhileFalse(Command* command);
+ Trigger WhileFalse(Command* command,
+ InitialState initialState = InitialState::CONDITION);
/**
* Starts the given command when the condition changes to `false` and cancels
@@ -168,9 +214,11 @@ class Trigger {
* `false`. If the command should restart, see RepeatCommand.
*
* @param command The command to bind.
+ * @param initialState the initial state to use
* @return The trigger, for chained calls.
*/
- Trigger WhileFalse(CommandPtr&& command);
+ Trigger WhileFalse(CommandPtr&& command,
+ InitialState initialState = InitialState::CONDITION);
/**
* Toggles a command when the condition changes from `false` to `true`.
@@ -179,9 +227,11 @@ class Trigger {
* lifespan of the command.
*
* @param command the command to toggle
+ * @param initialState the initial state to use
* @return this trigger, so calls can be chained
*/
- Trigger ToggleOnTrue(Command* command);
+ Trigger ToggleOnTrue(Command* command,
+ InitialState initialState = InitialState::CONDITION);
/**
* Toggles a command when the condition changes from `false` to `true`.
@@ -190,9 +240,11 @@ class Trigger {
* lifespan of the command.
*
* @param command the command to toggle
+ * @param initialState the initial state to use
* @return this trigger, so calls can be chained
*/
- Trigger ToggleOnTrue(CommandPtr&& command);
+ Trigger ToggleOnTrue(CommandPtr&& command,
+ InitialState initialState = InitialState::CONDITION);
/**
* Toggles a command when the condition changes from `true` to the low
@@ -202,9 +254,11 @@ class Trigger {
* lifespan of the command.
*
* @param command the command to toggle
+ * @param initialState the initial state to use
* @return this trigger, so calls can be chained
*/
- Trigger ToggleOnFalse(Command* command);
+ Trigger ToggleOnFalse(Command* command,
+ InitialState initialState = InitialState::CONDITION);
/**
* Toggles a command when the condition changes from `true` to `false`.
@@ -213,9 +267,11 @@ class Trigger {
* lifespan of the command.
*
* @param command the command to toggle
+ * @param initialState the initial state to use
* @return this trigger, so calls can be chained
*/
- Trigger ToggleOnFalse(CommandPtr&& command);
+ Trigger ToggleOnFalse(CommandPtr&& command,
+ InitialState initialState = InitialState::CONDITION);
/**
* Composes two triggers with logical AND.
@@ -291,6 +347,20 @@ class Trigger {
bool Get() const;
private:
+ bool GetInitialState(InitialState initialState) const {
+ switch (initialState) {
+ case InitialState::FALSE:
+ return false;
+ case InitialState::TRUE:
+ return true;
+ case InitialState::CONDITION:
+ return m_condition();
+ case InitialState::NEG_CONDITION:
+ return !m_condition();
+ }
+ return false;
+ }
+
frc::EventLoop* m_loop;
std::function