Merge branch 'main' into 2027

This commit is contained in:
Peter Johnson
2024-12-19 20:40:37 -08:00
46 changed files with 1512 additions and 431 deletions

View File

@@ -34,8 +34,7 @@ public class ProxyCommand extends Command {
* @deprecated This constructor's similarity to {@link DeferredCommand} is confusing and opens
* potential footguns for users who do not fully understand the semantics and implications of
* proxying, but who simply want runtime construction. Users who do know what they are doing
* and need a supplier-constructed proxied command should instead proxy a DeferredCommand
* using the <code>asProxy</code> decorator.
* and need a supplier-constructed proxied command should instead defer a proxy command.
* @see DeferredCommand
*/
@Deprecated(since = "2025", forRemoval = true)

View File

@@ -24,6 +24,18 @@ import java.util.function.BooleanSupplier;
* <p>This class is provided by the NewCommands VendorDep
*/
public class Trigger implements BooleanSupplier {
/** Functional interface for the body of a trigger binding. */
@FunctionalInterface
private interface BindingBody {
/**
* Executes the body of the binding.
*
* @param previous The previous state of the condition.
* @param current The current state of the condition.
*/
void run(boolean previous, boolean current);
}
private final BooleanSupplier m_condition;
private final EventLoop m_loop;
@@ -49,6 +61,27 @@ public class Trigger implements BooleanSupplier {
this(CommandScheduler.getInstance().getDefaultButtonLoop(), condition);
}
/**
* Adds a binding to the EventLoop.
*
* @param body The body of the binding to add.
*/
private void addBinding(BindingBody body) {
m_loop.bind(
new Runnable() {
private boolean m_previous = m_condition.getAsBoolean();
@Override
public void run() {
boolean current = m_condition.getAsBoolean();
body.run(m_previous, current);
m_previous = current;
}
});
}
/**
* Starts the command when the condition changes.
*
@@ -57,19 +90,10 @@ public class Trigger implements BooleanSupplier {
*/
public Trigger onChange(Command command) {
requireNonNullParam(command, "command", "onChange");
m_loop.bind(
new Runnable() {
private boolean m_pressedLast = m_condition.getAsBoolean();
@Override
public void run() {
boolean pressed = m_condition.getAsBoolean();
if (m_pressedLast != pressed) {
command.schedule();
}
m_pressedLast = pressed;
addBinding(
(previous, current) -> {
if (previous != current) {
command.schedule();
}
});
return this;
@@ -83,19 +107,10 @@ public class Trigger implements BooleanSupplier {
*/
public Trigger onTrue(Command command) {
requireNonNullParam(command, "command", "onTrue");
m_loop.bind(
new Runnable() {
private boolean m_pressedLast = m_condition.getAsBoolean();
@Override
public void run() {
boolean pressed = m_condition.getAsBoolean();
if (!m_pressedLast && pressed) {
command.schedule();
}
m_pressedLast = pressed;
addBinding(
(previous, current) -> {
if (!previous && current) {
command.schedule();
}
});
return this;
@@ -109,19 +124,10 @@ public class Trigger implements BooleanSupplier {
*/
public Trigger onFalse(Command command) {
requireNonNullParam(command, "command", "onFalse");
m_loop.bind(
new Runnable() {
private boolean m_pressedLast = m_condition.getAsBoolean();
@Override
public void run() {
boolean pressed = m_condition.getAsBoolean();
if (m_pressedLast && !pressed) {
command.schedule();
}
m_pressedLast = pressed;
addBinding(
(previous, current) -> {
if (previous && !current) {
command.schedule();
}
});
return this;
@@ -139,21 +145,12 @@ public class Trigger implements BooleanSupplier {
*/
public Trigger whileTrue(Command command) {
requireNonNullParam(command, "command", "whileTrue");
m_loop.bind(
new Runnable() {
private boolean m_pressedLast = m_condition.getAsBoolean();
@Override
public void run() {
boolean pressed = m_condition.getAsBoolean();
if (!m_pressedLast && pressed) {
command.schedule();
} else if (m_pressedLast && !pressed) {
command.cancel();
}
m_pressedLast = pressed;
addBinding(
(previous, current) -> {
if (!previous && current) {
command.schedule();
} else if (previous && !current) {
command.cancel();
}
});
return this;
@@ -171,21 +168,12 @@ public class Trigger implements BooleanSupplier {
*/
public Trigger whileFalse(Command command) {
requireNonNullParam(command, "command", "whileFalse");
m_loop.bind(
new Runnable() {
private boolean m_pressedLast = m_condition.getAsBoolean();
@Override
public void run() {
boolean pressed = m_condition.getAsBoolean();
if (m_pressedLast && !pressed) {
command.schedule();
} else if (!m_pressedLast && pressed) {
command.cancel();
}
m_pressedLast = pressed;
addBinding(
(previous, current) -> {
if (previous && !current) {
command.schedule();
} else if (!previous && current) {
command.cancel();
}
});
return this;
@@ -199,23 +187,14 @@ public class Trigger implements BooleanSupplier {
*/
public Trigger toggleOnTrue(Command command) {
requireNonNullParam(command, "command", "toggleOnTrue");
m_loop.bind(
new Runnable() {
private boolean m_pressedLast = m_condition.getAsBoolean();
@Override
public void run() {
boolean pressed = m_condition.getAsBoolean();
if (!m_pressedLast && pressed) {
if (command.isScheduled()) {
command.cancel();
} else {
command.schedule();
}
addBinding(
(previous, current) -> {
if (!previous && current) {
if (command.isScheduled()) {
command.cancel();
} else {
command.schedule();
}
m_pressedLast = pressed;
}
});
return this;
@@ -229,23 +208,14 @@ public class Trigger implements BooleanSupplier {
*/
public Trigger toggleOnFalse(Command command) {
requireNonNullParam(command, "command", "toggleOnFalse");
m_loop.bind(
new Runnable() {
private boolean m_pressedLast = m_condition.getAsBoolean();
@Override
public void run() {
boolean pressed = m_condition.getAsBoolean();
if (m_pressedLast && !pressed) {
if (command.isScheduled()) {
command.cancel();
} else {
command.schedule();
}
addBinding(
(previous, current) -> {
if (previous && !current) {
if (command.isScheduled()) {
command.cancel();
} else {
command.schedule();
}
m_pressedLast = pressed;
}
});
return this;

View File

@@ -15,159 +15,117 @@ using namespace frc2;
Trigger::Trigger(const Trigger& other) = default;
void Trigger::AddBinding(wpi::unique_function<void(bool, bool)>&& body) {
m_loop->Bind([condition = m_condition, previous = m_condition(),
body = std::move(body)]() mutable {
bool current = condition();
body(previous, current);
previous = current;
});
}
Trigger Trigger::OnChange(Command* command) {
m_loop->Bind(
[condition = m_condition, previous = m_condition(), command]() mutable {
bool current = condition();
if (previous != current) {
command->Schedule();
}
previous = current;
});
AddBinding([command](bool previous, bool current) {
if (previous != current) {
command->Schedule();
}
});
return *this;
}
Trigger Trigger::OnChange(CommandPtr&& command) {
m_loop->Bind([condition = m_condition, previous = m_condition(),
command = std::move(command)]() mutable {
bool current = condition();
AddBinding([command = std::move(command)](bool previous, bool current) {
if (previous != current) {
command.Schedule();
}
previous = current;
});
return *this;
}
Trigger Trigger::OnTrue(Command* command) {
m_loop->Bind(
[condition = m_condition, previous = m_condition(), command]() mutable {
bool current = condition();
if (!previous && current) {
command->Schedule();
}
previous = current;
});
AddBinding([command](bool previous, bool current) {
if (!previous && current) {
command->Schedule();
}
});
return *this;
}
Trigger Trigger::OnTrue(CommandPtr&& command) {
m_loop->Bind([condition = m_condition, previous = m_condition(),
command = std::move(command)]() mutable {
bool current = condition();
AddBinding([command = std::move(command)](bool previous, bool current) {
if (!previous && current) {
command.Schedule();
}
previous = current;
});
return *this;
}
Trigger Trigger::OnFalse(Command* command) {
m_loop->Bind(
[condition = m_condition, previous = m_condition(), command]() mutable {
bool current = condition();
if (previous && !current) {
command->Schedule();
}
previous = current;
});
AddBinding([command](bool previous, bool current) {
if (previous && !current) {
command->Schedule();
}
});
return *this;
}
Trigger Trigger::OnFalse(CommandPtr&& command) {
m_loop->Bind([condition = m_condition, previous = m_condition(),
command = std::move(command)]() mutable {
bool current = condition();
AddBinding([command = std::move(command)](bool previous, bool current) {
if (previous && !current) {
command.Schedule();
}
previous = current;
});
return *this;
}
Trigger Trigger::WhileTrue(Command* command) {
m_loop->Bind(
[condition = m_condition, previous = m_condition(), command]() mutable {
bool current = condition();
if (!previous && current) {
command->Schedule();
} else if (previous && !current) {
command->Cancel();
}
previous = current;
});
AddBinding([command](bool previous, bool current) {
if (!previous && current) {
command->Schedule();
} else if (previous && !current) {
command->Cancel();
}
});
return *this;
}
Trigger Trigger::WhileTrue(CommandPtr&& command) {
m_loop->Bind([condition = m_condition, previous = m_condition(),
command = std::move(command)]() mutable {
bool current = condition();
AddBinding([command = std::move(command)](bool previous, bool current) {
if (!previous && current) {
command.Schedule();
} else if (previous && !current) {
command.Cancel();
}
previous = current;
});
return *this;
}
Trigger Trigger::WhileFalse(Command* command) {
m_loop->Bind(
[condition = m_condition, previous = m_condition(), command]() mutable {
bool current = condition();
if (previous && !current) {
command->Schedule();
} else if (!previous && current) {
command->Cancel();
}
previous = current;
});
AddBinding([command](bool previous, bool current) {
if (previous && !current) {
command->Schedule();
} else if (!previous && current) {
command->Cancel();
}
});
return *this;
}
Trigger Trigger::WhileFalse(CommandPtr&& command) {
m_loop->Bind([condition = m_condition, previous = m_condition(),
command = std::move(command)]() mutable {
bool current = condition();
AddBinding([command = std::move(command)](bool previous, bool current) {
if (!previous && current) {
command.Schedule();
} else if (previous && !current) {
command.Cancel();
}
previous = current;
});
return *this;
}
Trigger Trigger::ToggleOnTrue(Command* command) {
m_loop->Bind([condition = m_condition, previous = m_condition(),
command = command]() mutable {
bool current = condition();
AddBinding([command](bool previous, bool current) {
if (!previous && current) {
if (command->IsScheduled()) {
command->Cancel();
@@ -175,17 +133,12 @@ Trigger Trigger::ToggleOnTrue(Command* command) {
command->Schedule();
}
}
previous = current;
});
return *this;
}
Trigger Trigger::ToggleOnTrue(CommandPtr&& command) {
m_loop->Bind([condition = m_condition, previous = m_condition(),
command = std::move(command)]() mutable {
bool current = condition();
AddBinding([command = std::move(command)](bool previous, bool current) {
if (!previous && current) {
if (command.IsScheduled()) {
command.Cancel();
@@ -193,17 +146,12 @@ Trigger Trigger::ToggleOnTrue(CommandPtr&& command) {
command.Schedule();
}
}
previous = current;
});
return *this;
}
Trigger Trigger::ToggleOnFalse(Command* command) {
m_loop->Bind([condition = m_condition, previous = m_condition(),
command = command]() mutable {
bool current = condition();
AddBinding([command](bool previous, bool current) {
if (previous && !current) {
if (command->IsScheduled()) {
command->Cancel();
@@ -211,17 +159,12 @@ Trigger Trigger::ToggleOnFalse(Command* command) {
command->Schedule();
}
}
previous = current;
});
return *this;
}
Trigger Trigger::ToggleOnFalse(CommandPtr&& command) {
m_loop->Bind([condition = m_condition, previous = m_condition(),
command = std::move(command)]() mutable {
bool current = condition();
AddBinding([command = std::move(command)](bool previous, bool current) {
if (previous && !current) {
if (command.IsScheduled()) {
command.Cancel();
@@ -229,8 +172,6 @@ Trigger Trigger::ToggleOnFalse(CommandPtr&& command) {
command.Schedule();
}
}
previous = current;
});
return *this;
}

View File

@@ -41,12 +41,11 @@ class ProxyCommand : public CommandHelper<Command, ProxyCommand> {
* confusing and opens potential footguns for users who do not fully
* understand the semantics and implications of proxying, but who simply want
* runtime construction. Users who do know what they are doing and need a
* supplier-constructed proxied command should instead proxy a DeferredCommand
* using the <code>AsProxy</code> decorator.
* supplier-constructed proxied command should instead defer a proxy command.
* @see DeferredCommand
*/
WPI_IGNORE_DEPRECATED
[[deprecated("Proxy a DeferredCommand instead")]]
[[deprecated("Defer a proxy command instead.")]]
explicit ProxyCommand(wpi::unique_function<Command*()> supplier);
/**
@@ -62,11 +61,10 @@ class ProxyCommand : public CommandHelper<Command, ProxyCommand> {
* confusing and opens potential footguns for users who do not fully
* understand the semantics and implications of proxying, but who simply want
* runtime construction. Users who do know what they are doing and need a
* supplier-constructed proxied command should instead proxy a DeferredCommand
* using the <code>AsProxy</code> decorator.
* supplier-constructed proxied command should instead defer a proxy command.
* @see DeferredCommand
*/
[[deprecated("Proxy a DeferredCommand instead")]]
[[deprecated("Defer a proxy command instead.")]]
explicit ProxyCommand(wpi::unique_function<CommandPtr()> supplier);
WPI_UNIGNORE_DEPRECATED

View File

@@ -11,6 +11,7 @@
#include <frc/event/EventLoop.h>
#include <frc/filter/Debouncer.h>
#include <units/time.h>
#include <wpi/FunctionExtras.h>
#include "frc2/command/Command.h"
#include "frc2/command/CommandScheduler.h"
@@ -291,6 +292,13 @@ class Trigger {
bool Get() const;
private:
/**
* Adds a binding to the EventLoop.
*
* @param body The body of the binding to add.
*/
void AddBinding(wpi::unique_function<void(bool, bool)>&& body);
frc::EventLoop* m_loop;
std::function<bool()> m_condition;
};