mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-25 01:41:43 +00:00
[commands] Revert to original Trigger implementation (#4673)
Trigger was refactored to use BooleanEvent when it was introduced in #4104. This reverts to the original implementation until edge-based BooleanEvents can be fixed.
This commit is contained in:
@@ -158,7 +158,7 @@ public final class CommandScheduler implements NTSendable, AutoCloseable {
|
||||
*/
|
||||
@Deprecated(since = "2023")
|
||||
public void addButton(Runnable button) {
|
||||
m_activeButtonLoop.bind(() -> true, requireNonNullParam(button, "button", "addButton"));
|
||||
m_activeButtonLoop.bind(requireNonNullParam(button, "button", "addButton"));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -159,8 +159,7 @@ public class Button extends Trigger {
|
||||
*
|
||||
* @param command the command to start
|
||||
* @return this button, so calls can be chained
|
||||
* @deprecated Instead, pass {@link #rising()} as an end condition to {@link
|
||||
* Command#until(BooleanSupplier)}.
|
||||
* @deprecated Instead, pass this as an end condition to {@link Command#until(BooleanSupplier)}.
|
||||
*/
|
||||
@Deprecated
|
||||
public Button cancelWhenPressed(final Command command) {
|
||||
|
||||
@@ -13,7 +13,6 @@ import edu.wpi.first.wpilibj2.command.Command;
|
||||
import edu.wpi.first.wpilibj2.command.CommandScheduler;
|
||||
import edu.wpi.first.wpilibj2.command.InstantCommand;
|
||||
import edu.wpi.first.wpilibj2.command.Subsystem;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.BooleanSupplier;
|
||||
|
||||
/**
|
||||
@@ -28,7 +27,8 @@ import java.util.function.BooleanSupplier;
|
||||
* <p>This class is provided by the NewCommands VendorDep
|
||||
*/
|
||||
public class Trigger implements BooleanSupplier {
|
||||
private final BooleanEvent m_event;
|
||||
private final BooleanSupplier m_condition;
|
||||
private final EventLoop m_loop;
|
||||
|
||||
/**
|
||||
* Creates a new trigger based on the given condition.
|
||||
@@ -37,18 +37,8 @@ public class Trigger implements BooleanSupplier {
|
||||
* @param condition the condition represented by this trigger
|
||||
*/
|
||||
public Trigger(EventLoop loop, BooleanSupplier condition) {
|
||||
m_event = new BooleanEvent(loop, condition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the BooleanEvent into a Trigger object.
|
||||
*
|
||||
* @param toCast the BooleanEvent
|
||||
* @return a Trigger wrapping the given BooleanEvent
|
||||
* @see BooleanEvent#castTo(BiFunction)
|
||||
*/
|
||||
public static Trigger cast(BooleanEvent toCast) {
|
||||
return toCast.castTo(Trigger::new);
|
||||
m_loop = requireNonNullParam(loop, "loop", "Trigger");
|
||||
m_condition = requireNonNullParam(condition, "condition", "Trigger");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -73,11 +63,24 @@ public class Trigger implements BooleanSupplier {
|
||||
*
|
||||
* @param command the command to start
|
||||
* @return this trigger, so calls can be chained
|
||||
* @see #rising()
|
||||
*/
|
||||
public Trigger onTrue(Command command) {
|
||||
requireNonNullParam(command, "command", "onRising");
|
||||
m_event.rising().ifHigh(command::schedule);
|
||||
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;
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -86,11 +89,24 @@ public class Trigger implements BooleanSupplier {
|
||||
*
|
||||
* @param command the command to start
|
||||
* @return this trigger, so calls can be chained
|
||||
* @see #falling()
|
||||
*/
|
||||
public Trigger onFalse(Command command) {
|
||||
requireNonNullParam(command, "command", "onFalling");
|
||||
m_event.falling().ifHigh(command::schedule);
|
||||
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;
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -106,8 +122,23 @@ public class Trigger implements BooleanSupplier {
|
||||
*/
|
||||
public Trigger whileTrue(Command command) {
|
||||
requireNonNullParam(command, "command", "whileHigh");
|
||||
m_event.rising().ifHigh(command::schedule);
|
||||
m_event.falling().ifHigh(command::cancel);
|
||||
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;
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -123,8 +154,23 @@ public class Trigger implements BooleanSupplier {
|
||||
*/
|
||||
public Trigger whileFalse(Command command) {
|
||||
requireNonNullParam(command, "command", "whileLow");
|
||||
m_event.falling().ifHigh(command::schedule);
|
||||
m_event.rising().ifHigh(command::cancel);
|
||||
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;
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -136,16 +182,25 @@ public class Trigger implements BooleanSupplier {
|
||||
*/
|
||||
public Trigger toggleOnTrue(Command command) {
|
||||
requireNonNullParam(command, "command", "toggleOnRising");
|
||||
m_event
|
||||
.rising()
|
||||
.ifHigh(
|
||||
() -> {
|
||||
if (!command.isScheduled()) {
|
||||
command.schedule();
|
||||
} else {
|
||||
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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
m_pressedLast = pressed;
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -157,16 +212,25 @@ public class Trigger implements BooleanSupplier {
|
||||
*/
|
||||
public Trigger toggleOnFalse(Command command) {
|
||||
requireNonNullParam(command, "command", "toggleOnFalling");
|
||||
m_event
|
||||
.falling()
|
||||
.ifHigh(
|
||||
() -> {
|
||||
if (!command.isScheduled()) {
|
||||
command.schedule();
|
||||
} else {
|
||||
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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
m_pressedLast = pressed;
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -181,7 +245,21 @@ public class Trigger implements BooleanSupplier {
|
||||
public Trigger whenActive(final Command command) {
|
||||
requireNonNullParam(command, "command", "whenActive");
|
||||
|
||||
m_event.rising().ifHigh(command::schedule);
|
||||
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;
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -215,8 +293,23 @@ public class Trigger implements BooleanSupplier {
|
||||
public Trigger whileActiveContinuous(final Command command) {
|
||||
requireNonNullParam(command, "command", "whileActiveContinuous");
|
||||
|
||||
m_event.ifHigh(command::schedule);
|
||||
m_event.falling().ifHigh(command::cancel);
|
||||
m_loop.bind(
|
||||
new Runnable() {
|
||||
private boolean m_pressedLast = m_condition.getAsBoolean();
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
boolean pressed = m_condition.getAsBoolean();
|
||||
|
||||
if (pressed) {
|
||||
command.schedule();
|
||||
} else if (m_pressedLast) {
|
||||
command.cancel();
|
||||
}
|
||||
|
||||
m_pressedLast = pressed;
|
||||
}
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -246,9 +339,23 @@ public class Trigger implements BooleanSupplier {
|
||||
public Trigger whileActiveOnce(final Command command) {
|
||||
requireNonNullParam(command, "command", "whileActiveOnce");
|
||||
|
||||
m_event.rising().ifHigh(command::schedule);
|
||||
m_event.falling().ifHigh(command::cancel);
|
||||
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;
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -263,7 +370,21 @@ public class Trigger implements BooleanSupplier {
|
||||
public Trigger whenInactive(final Command command) {
|
||||
requireNonNullParam(command, "command", "whenInactive");
|
||||
|
||||
m_event.falling().ifHigh(command::schedule);
|
||||
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;
|
||||
}
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -292,16 +413,25 @@ public class Trigger implements BooleanSupplier {
|
||||
public Trigger toggleWhenActive(final Command command) {
|
||||
requireNonNullParam(command, "command", "toggleWhenActive");
|
||||
|
||||
m_event
|
||||
.rising()
|
||||
.ifHigh(
|
||||
() -> {
|
||||
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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
m_pressedLast = pressed;
|
||||
}
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -311,57 +441,94 @@ public class Trigger implements BooleanSupplier {
|
||||
*
|
||||
* @param command the command to cancel
|
||||
* @return this trigger, so calls can be chained
|
||||
* @deprecated Instead, pass {@link #rising()} as an end condition to {@link
|
||||
* Command#until(BooleanSupplier)}.
|
||||
* @deprecated Instead, pass this as an end condition to {@link Command#until(BooleanSupplier)}.
|
||||
*/
|
||||
@Deprecated
|
||||
public Trigger cancelWhenActive(final Command command) {
|
||||
requireNonNullParam(command, "command", "cancelWhenActive");
|
||||
|
||||
m_event.rising().ifHigh(command::cancel);
|
||||
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.cancel();
|
||||
}
|
||||
|
||||
m_pressedLast = pressed;
|
||||
}
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wrapped BooleanEvent.
|
||||
*
|
||||
* @return the wrapped BooleanEvent instance.
|
||||
*/
|
||||
public BooleanEvent getEvent() {
|
||||
return m_event;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getAsBoolean() {
|
||||
return m_event.getAsBoolean();
|
||||
return m_condition.getAsBoolean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Composes two triggers with logical OR.
|
||||
*
|
||||
* @param trigger the condition to compose with
|
||||
* @return A trigger which is active when either component trigger is active.
|
||||
*/
|
||||
public Trigger and(BooleanSupplier trigger) {
|
||||
return cast(m_event.and(trigger));
|
||||
return new Trigger(() -> m_condition.getAsBoolean() && trigger.getAsBoolean());
|
||||
}
|
||||
|
||||
/**
|
||||
* Composes two triggers with logical OR.
|
||||
*
|
||||
* @param trigger the condition to compose with
|
||||
* @return A trigger which is active when either component trigger is active.
|
||||
*/
|
||||
public Trigger or(BooleanSupplier trigger) {
|
||||
return cast(m_event.or(trigger));
|
||||
return new Trigger(() -> m_condition.getAsBoolean() || trigger.getAsBoolean());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new trigger that is active when this trigger is inactive, i.e. that acts as the
|
||||
* negation of this trigger.
|
||||
*
|
||||
* @return the negated trigger
|
||||
*/
|
||||
public Trigger negate() {
|
||||
return cast(m_event.negate());
|
||||
return new Trigger(() -> !m_condition.getAsBoolean());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new debounced trigger from this trigger - it will become active when this trigger has
|
||||
* been active for longer than the specified period.
|
||||
*
|
||||
* @param seconds The debounce period.
|
||||
* @return The debounced trigger (rising edges debounced only)
|
||||
*/
|
||||
public Trigger debounce(double seconds) {
|
||||
return debounce(seconds, Debouncer.DebounceType.kRising);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new debounced trigger from this trigger - it will become active when this trigger has
|
||||
* been active for longer than the specified period.
|
||||
*
|
||||
* @param seconds The debounce period.
|
||||
* @param type The debounce type.
|
||||
* @return The debounced trigger.
|
||||
*/
|
||||
public Trigger debounce(double seconds, Debouncer.DebounceType type) {
|
||||
return cast(m_event.debounce(seconds, type));
|
||||
}
|
||||
return new Trigger(
|
||||
new BooleanSupplier() {
|
||||
final Debouncer m_debouncer = new Debouncer(seconds, type);
|
||||
|
||||
public Trigger rising() {
|
||||
return cast(m_event.rising());
|
||||
}
|
||||
|
||||
public Trigger falling() {
|
||||
return cast(m_event.falling());
|
||||
@Override
|
||||
public boolean getAsBoolean() {
|
||||
return m_debouncer.calculate(m_condition.getAsBoolean());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,101 +14,200 @@ using namespace frc2;
|
||||
Trigger::Trigger(const Trigger& other) = default;
|
||||
|
||||
Trigger Trigger::OnTrue(Command* command) {
|
||||
m_event.Rising().IfHigh([command] { command->Schedule(); });
|
||||
m_loop->Bind(
|
||||
[condition = m_condition, previous = m_condition(), command]() mutable {
|
||||
bool current = condition();
|
||||
|
||||
if (!previous && current) {
|
||||
command->Schedule();
|
||||
}
|
||||
|
||||
previous = current;
|
||||
});
|
||||
return *this;
|
||||
}
|
||||
|
||||
Trigger Trigger::OnTrue(CommandPtr&& command) {
|
||||
m_event.Rising().IfHigh(
|
||||
[command = std::move(command)] { command.Schedule(); });
|
||||
m_loop->Bind([condition = m_condition, previous = m_condition(),
|
||||
command = std::move(command)]() mutable {
|
||||
bool current = condition();
|
||||
|
||||
if (!previous && current) {
|
||||
command.Schedule();
|
||||
}
|
||||
|
||||
previous = current;
|
||||
});
|
||||
return *this;
|
||||
}
|
||||
|
||||
Trigger Trigger::OnFalse(Command* command) {
|
||||
m_event.Falling().IfHigh([command] { command->Schedule(); });
|
||||
m_loop->Bind(
|
||||
[condition = m_condition, previous = m_condition(), command]() mutable {
|
||||
bool current = condition();
|
||||
|
||||
if (previous && !current) {
|
||||
command->Schedule();
|
||||
}
|
||||
|
||||
previous = current;
|
||||
});
|
||||
return *this;
|
||||
}
|
||||
|
||||
Trigger Trigger::OnFalse(CommandPtr&& command) {
|
||||
m_event.Falling().IfHigh(
|
||||
[command = std::move(command)] { command.Schedule(); });
|
||||
m_loop->Bind([condition = m_condition, previous = m_condition(),
|
||||
command = std::move(command)]() mutable {
|
||||
bool current = condition();
|
||||
|
||||
if (previous && !current) {
|
||||
command.Schedule();
|
||||
}
|
||||
|
||||
previous = current;
|
||||
});
|
||||
return *this;
|
||||
}
|
||||
|
||||
Trigger Trigger::WhileTrue(Command* command) {
|
||||
m_event.Rising().IfHigh([command] { command->Schedule(); });
|
||||
m_event.Falling().IfHigh([command] { command->Cancel(); });
|
||||
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;
|
||||
});
|
||||
return *this;
|
||||
}
|
||||
|
||||
Trigger Trigger::WhileTrue(CommandPtr&& command) {
|
||||
auto ptr = std::make_shared<CommandPtr>(std::move(command));
|
||||
m_event.Rising().IfHigh([ptr] { ptr->Schedule(); });
|
||||
m_event.Falling().IfHigh([ptr] { ptr->Cancel(); });
|
||||
m_loop->Bind([condition = m_condition, previous = m_condition(),
|
||||
command = std::move(command)]() mutable {
|
||||
bool current = condition();
|
||||
|
||||
if (!previous && current) {
|
||||
command.Schedule();
|
||||
} else if (previous && !current) {
|
||||
command.Cancel();
|
||||
}
|
||||
|
||||
previous = current;
|
||||
});
|
||||
return *this;
|
||||
}
|
||||
|
||||
Trigger Trigger::WhileFalse(Command* command) {
|
||||
m_event.Falling().IfHigh([command] { command->Schedule(); });
|
||||
m_event.Rising().IfHigh([command] { command->Cancel(); });
|
||||
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;
|
||||
});
|
||||
return *this;
|
||||
}
|
||||
|
||||
Trigger Trigger::WhileFalse(CommandPtr&& command) {
|
||||
auto ptr = std::make_shared<CommandPtr>(std::move(command));
|
||||
m_event.Falling().IfHigh([ptr] { ptr->Schedule(); });
|
||||
m_event.Rising().IfHigh([ptr] { ptr->Cancel(); });
|
||||
m_loop->Bind([condition = m_condition, previous = m_condition(),
|
||||
command = std::move(command)]() mutable {
|
||||
bool current = condition();
|
||||
|
||||
if (!previous && current) {
|
||||
command.Schedule();
|
||||
} else if (previous && !current) {
|
||||
command.Cancel();
|
||||
}
|
||||
|
||||
previous = current;
|
||||
});
|
||||
return *this;
|
||||
}
|
||||
|
||||
Trigger Trigger::ToggleOnTrue(Command* command) {
|
||||
m_event.Rising().IfHigh([command] {
|
||||
if (command->IsScheduled()) {
|
||||
command->Cancel();
|
||||
} else {
|
||||
command->Schedule();
|
||||
m_loop->Bind([condition = m_condition, previous = m_condition(),
|
||||
command = command]() mutable {
|
||||
bool current = condition();
|
||||
|
||||
if (!previous && current) {
|
||||
if (command->IsScheduled()) {
|
||||
command->Cancel();
|
||||
} else {
|
||||
command->Schedule();
|
||||
}
|
||||
}
|
||||
|
||||
previous = current;
|
||||
});
|
||||
return *this;
|
||||
}
|
||||
|
||||
Trigger Trigger::ToggleOnTrue(CommandPtr&& command) {
|
||||
m_event.Rising().IfHigh([command = std::move(command)] {
|
||||
if (command.IsScheduled()) {
|
||||
command.Cancel();
|
||||
} else {
|
||||
command.Schedule();
|
||||
m_loop->Bind([condition = m_condition, previous = m_condition(),
|
||||
command = std::move(command)]() mutable {
|
||||
bool current = condition();
|
||||
|
||||
if (!previous && current) {
|
||||
if (command.IsScheduled()) {
|
||||
command.Cancel();
|
||||
} else {
|
||||
command.Schedule();
|
||||
}
|
||||
}
|
||||
|
||||
previous = current;
|
||||
});
|
||||
return *this;
|
||||
}
|
||||
|
||||
Trigger Trigger::ToggleOnFalse(Command* command) {
|
||||
m_event.Falling().IfHigh([command] {
|
||||
if (command->IsScheduled()) {
|
||||
command->Cancel();
|
||||
} else {
|
||||
command->Schedule();
|
||||
m_loop->Bind([condition = m_condition, previous = m_condition(),
|
||||
command = command]() mutable {
|
||||
bool current = condition();
|
||||
|
||||
if (previous && !current) {
|
||||
if (command->IsScheduled()) {
|
||||
command->Cancel();
|
||||
} else {
|
||||
command->Schedule();
|
||||
}
|
||||
}
|
||||
|
||||
previous = current;
|
||||
});
|
||||
return *this;
|
||||
}
|
||||
|
||||
Trigger Trigger::ToggleOnFalse(CommandPtr&& command) {
|
||||
m_event.Falling().IfHigh([command = std::move(command)] {
|
||||
if (command.IsScheduled()) {
|
||||
command.Cancel();
|
||||
} else {
|
||||
command.Schedule();
|
||||
m_loop->Bind([condition = m_condition, previous = m_condition(),
|
||||
command = std::move(command)]() mutable {
|
||||
bool current = condition();
|
||||
|
||||
if (previous && !current) {
|
||||
if (command.IsScheduled()) {
|
||||
command.Cancel();
|
||||
} else {
|
||||
command.Schedule();
|
||||
}
|
||||
}
|
||||
|
||||
previous = current;
|
||||
});
|
||||
return *this;
|
||||
}
|
||||
|
||||
WPI_IGNORE_DEPRECATED
|
||||
Trigger Trigger::WhenActive(Command* command) {
|
||||
m_event.Rising().IfHigh([command] { command->Schedule(); });
|
||||
return *this;
|
||||
return OnTrue(command);
|
||||
}
|
||||
|
||||
Trigger Trigger::WhenActive(std::function<void()> toRun,
|
||||
@@ -123,8 +222,18 @@ Trigger Trigger::WhenActive(std::function<void()> toRun,
|
||||
}
|
||||
|
||||
Trigger Trigger::WhileActiveContinous(Command* command) {
|
||||
m_event.IfHigh([command] { command->Schedule(); });
|
||||
m_event.Falling().IfHigh([command] { command->Cancel(); });
|
||||
m_loop->Bind([condition = m_condition, previous = m_condition(),
|
||||
command = std::move(command)]() mutable {
|
||||
bool current = condition();
|
||||
|
||||
if (current) {
|
||||
command->Schedule();
|
||||
} else if (previous && !current) {
|
||||
command->Cancel();
|
||||
}
|
||||
|
||||
previous = current;
|
||||
});
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -141,13 +250,32 @@ Trigger Trigger::WhileActiveContinous(
|
||||
}
|
||||
|
||||
Trigger Trigger::WhileActiveOnce(Command* command) {
|
||||
m_event.Rising().IfHigh([command] { command->Schedule(); });
|
||||
m_event.Falling().IfHigh([command] { command->Cancel(); });
|
||||
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;
|
||||
});
|
||||
return *this;
|
||||
}
|
||||
|
||||
Trigger Trigger::WhenInactive(Command* command) {
|
||||
m_event.Falling().IfHigh([command] { command->Schedule(); });
|
||||
m_loop->Bind(
|
||||
[condition = m_condition, previous = m_condition(), command]() mutable {
|
||||
bool current = condition();
|
||||
|
||||
if (previous && !current) {
|
||||
command->Schedule();
|
||||
}
|
||||
|
||||
previous = current;
|
||||
});
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -163,22 +291,42 @@ Trigger Trigger::WhenInactive(std::function<void()> toRun,
|
||||
}
|
||||
|
||||
Trigger Trigger::ToggleWhenActive(Command* command) {
|
||||
m_event.Rising().IfHigh([command] {
|
||||
if (command->IsScheduled()) {
|
||||
command->Cancel();
|
||||
} else {
|
||||
command->Schedule();
|
||||
m_loop->Bind([condition = m_condition, previous = m_condition(),
|
||||
command = command]() mutable {
|
||||
bool current = condition();
|
||||
|
||||
if (!previous && current) {
|
||||
if (command->IsScheduled()) {
|
||||
command->Cancel();
|
||||
} else {
|
||||
command->Schedule();
|
||||
}
|
||||
}
|
||||
|
||||
previous = current;
|
||||
});
|
||||
return *this;
|
||||
}
|
||||
|
||||
Trigger Trigger::CancelWhenActive(Command* command) {
|
||||
m_event.Rising().IfHigh([command] { command->Cancel(); });
|
||||
m_loop->Bind([condition = m_condition, previous = m_condition(),
|
||||
command = std::move(command)]() mutable {
|
||||
bool current = condition();
|
||||
|
||||
if (!previous && current) {
|
||||
command->Cancel();
|
||||
}
|
||||
|
||||
previous = current;
|
||||
});
|
||||
return *this;
|
||||
}
|
||||
WPI_UNIGNORE_DEPRECATED
|
||||
|
||||
BooleanEvent Trigger::GetEvent() const {
|
||||
return m_event;
|
||||
Trigger Trigger::Debounce(units::second_t debounceTime,
|
||||
frc::Debouncer::DebounceType type) {
|
||||
return Trigger(m_loop, [debouncer = frc::Debouncer(debounceTime, type),
|
||||
condition = m_condition]() mutable {
|
||||
return debouncer.Calculate(condition());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -266,10 +266,9 @@ class Button : public Trigger {
|
||||
*
|
||||
* @param command The command to bind.
|
||||
* @return The button, for chained calls.
|
||||
* @deprecated Use Rising() as a command end condition with Until() instead.
|
||||
* @deprecated Pass this as a command end condition with Until() instead.
|
||||
*/
|
||||
WPI_DEPRECATED(
|
||||
"Use Rising() as a command end condition with Until() instead.")
|
||||
WPI_DEPRECATED("Pass this as a command end condition with Until() instead.")
|
||||
Button CancelWhenPressed(Command* command);
|
||||
};
|
||||
} // namespace frc2
|
||||
|
||||
@@ -42,7 +42,7 @@ class Trigger {
|
||||
* @param condition the condition represented by this trigger
|
||||
*/
|
||||
explicit Trigger(std::function<bool()> condition)
|
||||
: m_event{CommandScheduler::GetInstance().GetDefaultButtonLoop(),
|
||||
: Trigger{CommandScheduler::GetInstance().GetDefaultButtonLoop(),
|
||||
std::move(condition)} {}
|
||||
|
||||
/**
|
||||
@@ -52,7 +52,7 @@ class Trigger {
|
||||
* @param condition the condition represented by this trigger
|
||||
*/
|
||||
Trigger(frc::EventLoop* loop, std::function<bool()> condition)
|
||||
: m_event{loop, std::move(condition)} {}
|
||||
: m_loop{loop}, m_condition{std::move(condition)} {}
|
||||
|
||||
/**
|
||||
* Create a new trigger that is always `false`.
|
||||
@@ -70,7 +70,6 @@ class Trigger {
|
||||
*
|
||||
* @param command the command to start
|
||||
* @return this trigger, so calls can be chained
|
||||
* @see #Rising()
|
||||
*/
|
||||
Trigger OnTrue(Command* command);
|
||||
|
||||
@@ -92,7 +91,6 @@ class Trigger {
|
||||
*
|
||||
* @param command the command to start
|
||||
* @return this trigger, so calls can be chained
|
||||
* @see #Falling()
|
||||
*/
|
||||
Trigger OnFalse(Command* command);
|
||||
|
||||
@@ -233,10 +231,17 @@ class Trigger {
|
||||
Command, std::remove_reference_t<T>>>>
|
||||
WPI_DEPRECATED("Use OnTrue(Command) instead")
|
||||
Trigger WhenActive(T&& command) {
|
||||
m_event.Rising().IfHigh(
|
||||
[command = std::make_unique<std::remove_reference_t<T>>(
|
||||
std::forward<T>(command))] { command->Schedule(); });
|
||||
m_loop->Bind([condition = m_condition, previous = m_condition(),
|
||||
command = std::make_unique<std::remove_reference_t<T>>(
|
||||
std::forward<T>(command))]() mutable {
|
||||
bool current = condition();
|
||||
|
||||
if (!previous && current) {
|
||||
command->Schedule();
|
||||
}
|
||||
|
||||
previous = current;
|
||||
});
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -298,10 +303,19 @@ class Trigger {
|
||||
"Use WhileTrue(Command) with RepeatCommand, or bind command::Schedule "
|
||||
"with IfHigh(std::function<void()>).")
|
||||
Trigger WhileActiveContinous(T&& command) {
|
||||
std::shared_ptr<T> ptr =
|
||||
std::make_shared<std::remove_reference_t<T>>(std::forward<T>(command));
|
||||
m_event.IfHigh([ptr] { ptr->Schedule(); });
|
||||
m_event.Falling().IfHigh([ptr] { ptr->Cancel(); });
|
||||
m_loop->Bind([condition = m_condition, previous = m_condition(),
|
||||
command = std::make_unique<std::remove_reference_t<T>>(
|
||||
std::forward<T>(command))]() mutable {
|
||||
bool current = condition();
|
||||
|
||||
if (current) {
|
||||
command->Schedule();
|
||||
} else if (previous && !current) {
|
||||
command->Cancel();
|
||||
}
|
||||
|
||||
previous = current;
|
||||
});
|
||||
|
||||
return *this;
|
||||
}
|
||||
@@ -354,12 +368,19 @@ class Trigger {
|
||||
Command, std::remove_reference_t<T>>>>
|
||||
WPI_DEPRECATED("Use WhileTrue(Command) instead.")
|
||||
Trigger WhileActiveOnce(T&& command) {
|
||||
std::shared_ptr<T> ptr =
|
||||
std::make_shared<std::remove_reference_t<T>>(std::forward<T>(command));
|
||||
m_loop->Bind([condition = m_condition, previous = m_condition(),
|
||||
command = std::make_unique<std::remove_reference_t<T>>(
|
||||
std::forward<T>(command))]() mutable {
|
||||
bool current = condition();
|
||||
|
||||
m_event.Rising().IfHigh([ptr] { ptr->Schedule(); });
|
||||
m_event.Falling().IfHigh([ptr] { ptr->Cancel(); });
|
||||
if (!previous && current) {
|
||||
command->Schedule();
|
||||
} else if (previous && !current) {
|
||||
command->Cancel();
|
||||
}
|
||||
|
||||
previous = current;
|
||||
});
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -389,10 +410,17 @@ class Trigger {
|
||||
Command, std::remove_reference_t<T>>>>
|
||||
WPI_DEPRECATED("Use OnFalse(Command) instead.")
|
||||
Trigger WhenInactive(T&& command) {
|
||||
m_event.Falling().IfHigh(
|
||||
[command = std::make_unique<std::remove_reference_t<T>>(
|
||||
std::forward<T>(command))] { command->Schedule(); });
|
||||
m_loop->Bind([condition = m_condition, previous = m_condition(),
|
||||
command = std::make_unique<std::remove_reference_t<T>>(
|
||||
std::forward<T>(command))]() mutable {
|
||||
bool current = condition();
|
||||
|
||||
if (previous && !current) {
|
||||
command->Schedule();
|
||||
}
|
||||
|
||||
previous = current;
|
||||
});
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -448,15 +476,21 @@ class Trigger {
|
||||
Command, std::remove_reference_t<T>>>>
|
||||
WPI_DEPRECATED("Use ToggleOnTrue(Command) instead.")
|
||||
Trigger ToggleWhenActive(T&& command) {
|
||||
m_event.Rising().IfHigh(
|
||||
[command = std::make_unique<std::remove_reference_t<T>>(
|
||||
std::forward<T>(command))] {
|
||||
if (!command->IsScheduled()) {
|
||||
command->Schedule();
|
||||
} else {
|
||||
command->Cancel();
|
||||
}
|
||||
});
|
||||
m_loop->Bind([condition = m_condition, previous = m_condition(),
|
||||
command = std::make_unique<std::remove_reference_t<T>>(
|
||||
std::forward<T>(command))]() mutable {
|
||||
bool current = condition();
|
||||
|
||||
if (!previous && current) {
|
||||
if (command->IsScheduled()) {
|
||||
command->Cancel();
|
||||
} else {
|
||||
command->Schedule();
|
||||
}
|
||||
}
|
||||
|
||||
previous = current;
|
||||
});
|
||||
|
||||
return *this;
|
||||
}
|
||||
@@ -468,33 +502,20 @@ class Trigger {
|
||||
*
|
||||
* @param command The command to bind.
|
||||
* @return The trigger, for chained calls.
|
||||
* @deprecated Use Rising() as a command end condition with Until() instead.
|
||||
* @deprecated Pass this as a command end condition with Until() instead.
|
||||
*/
|
||||
WPI_DEPRECATED(
|
||||
"Use Rising() as a command end condition with Until() instead.")
|
||||
WPI_DEPRECATED("Pass this as a command end condition with Until() instead.")
|
||||
Trigger CancelWhenActive(Command* command);
|
||||
|
||||
/**
|
||||
* Get a new event that events only when this one newly changes to true.
|
||||
*
|
||||
* @return a new event representing when this one newly changes to true.
|
||||
*/
|
||||
Trigger Rising() { return m_event.Rising().CastTo<Trigger>(); }
|
||||
|
||||
/**
|
||||
* Get a new event that triggers only when this one newly changes to false.
|
||||
*
|
||||
* @return a new event representing when this one newly changes to false.
|
||||
*/
|
||||
Trigger Falling() { return m_event.Falling().CastTo<Trigger>(); }
|
||||
|
||||
/**
|
||||
* Composes two triggers with logical AND.
|
||||
*
|
||||
* @return A trigger which is active when both component triggers are active.
|
||||
*/
|
||||
Trigger operator&&(std::function<bool()> rhs) {
|
||||
return m_event.operator&&(rhs).CastTo<Trigger>();
|
||||
return Trigger(m_loop, [condition = m_condition, rhs = std::move(rhs)] {
|
||||
return condition() && rhs();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -502,8 +523,10 @@ class Trigger {
|
||||
*
|
||||
* @return A trigger which is active when both component triggers are active.
|
||||
*/
|
||||
Trigger operator&&(Trigger& rhs) {
|
||||
return (m_event && rhs.m_event).CastTo<Trigger>();
|
||||
Trigger operator&&(Trigger rhs) {
|
||||
return Trigger(m_loop, [condition = m_condition, rhs] {
|
||||
return condition() && rhs.m_condition();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -512,7 +535,9 @@ class Trigger {
|
||||
* @return A trigger which is active when either component trigger is active.
|
||||
*/
|
||||
Trigger operator||(std::function<bool()> rhs) {
|
||||
return m_event.operator||(rhs).CastTo<Trigger>();
|
||||
return Trigger(m_loop, [condition = m_condition, rhs = std::move(rhs)] {
|
||||
return condition() || rhs();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -520,8 +545,10 @@ class Trigger {
|
||||
*
|
||||
* @return A trigger which is active when either component trigger is active.
|
||||
*/
|
||||
Trigger operator||(Trigger& rhs) {
|
||||
return (m_event || rhs.m_event).CastTo<Trigger>();
|
||||
Trigger operator||(Trigger rhs) {
|
||||
return Trigger(m_loop, [condition = m_condition, rhs] {
|
||||
return condition() || rhs.m_condition();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -530,7 +557,9 @@ class Trigger {
|
||||
* @return A trigger which is active when the component trigger is inactive,
|
||||
* and vice-versa.
|
||||
*/
|
||||
Trigger operator!() { return m_event.operator!().CastTo<Trigger>(); }
|
||||
Trigger operator!() {
|
||||
return Trigger(m_loop, [condition = m_condition] { return !condition(); });
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new debounced trigger from this trigger - it will become active
|
||||
@@ -542,16 +571,10 @@ class Trigger {
|
||||
*/
|
||||
Trigger Debounce(units::second_t debounceTime,
|
||||
frc::Debouncer::DebounceType type =
|
||||
frc::Debouncer::DebounceType::kRising) {
|
||||
return m_event.Debounce(debounceTime, type).CastTo<Trigger>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wrapped BooleanEvent instance.
|
||||
*/
|
||||
frc::BooleanEvent GetEvent() const;
|
||||
frc::Debouncer::DebounceType::kRising);
|
||||
|
||||
private:
|
||||
frc::BooleanEvent m_event;
|
||||
frc::EventLoop* m_loop;
|
||||
std::function<bool()> m_condition;
|
||||
};
|
||||
} // namespace frc2
|
||||
|
||||
@@ -178,7 +178,7 @@ class TriggerTest extends CommandTestBase {
|
||||
InternalButton button = new InternalButton();
|
||||
Command command1 =
|
||||
new StartEndCommand(startCounter::incrementAndGet, endCounter::incrementAndGet)
|
||||
.until(button.rising());
|
||||
.until(button);
|
||||
|
||||
button.setPressed(false);
|
||||
command1.schedule();
|
||||
|
||||
@@ -17,8 +17,12 @@ bool BooleanEvent::GetAsBoolean() const {
|
||||
return m_condition();
|
||||
}
|
||||
|
||||
void BooleanEvent::IfHigh(wpi::unique_function<void()> action) {
|
||||
m_loop->Bind(m_condition, std::move(action));
|
||||
void BooleanEvent::IfHigh(std::function<void()> action) {
|
||||
m_loop->Bind([condition = m_condition, action = std::move(action)] {
|
||||
if (condition()) {
|
||||
action();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
BooleanEvent BooleanEvent::operator!() {
|
||||
|
||||
@@ -8,20 +8,13 @@ using namespace frc;
|
||||
|
||||
EventLoop::EventLoop() {}
|
||||
|
||||
void EventLoop::Binding::Poll() {
|
||||
if (condition()) {
|
||||
action();
|
||||
}
|
||||
}
|
||||
|
||||
void EventLoop::Bind(std::function<bool()> condition,
|
||||
wpi::unique_function<void()> action) {
|
||||
m_bindings.emplace_back(Binding{condition, std::move(action)});
|
||||
void EventLoop::Bind(wpi::unique_function<void()> action) {
|
||||
m_bindings.emplace_back(std::move(action));
|
||||
}
|
||||
|
||||
void EventLoop::Poll() {
|
||||
for (Binding& binding : m_bindings) {
|
||||
binding.Poll();
|
||||
for (wpi::unique_function<void()>& action : m_bindings) {
|
||||
action();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace frc {
|
||||
/**
|
||||
* This class provides an easy way to link actions to inputs. Each object
|
||||
* represents a boolean condition to which callback actions can be bound using
|
||||
* {@link #IfHigh(wpi::unique_function<void()>)}.
|
||||
* {@link #IfHigh(std::function<void()>)}.
|
||||
*
|
||||
* <p>These events can easily be composed using factories such as {@link
|
||||
* #operator!},
|
||||
@@ -51,7 +51,7 @@ class BooleanEvent {
|
||||
*
|
||||
* @param action the action to run if this event is active.
|
||||
*/
|
||||
void IfHigh(wpi::unique_function<void()> action);
|
||||
void IfHigh(std::function<void()> action);
|
||||
|
||||
operator std::function<bool()>(); // NOLINT
|
||||
|
||||
|
||||
@@ -20,13 +20,11 @@ class EventLoop {
|
||||
EventLoop& operator=(const EventLoop&) = delete;
|
||||
|
||||
/**
|
||||
* Bind a new action to run whenever the condition is true.
|
||||
* Bind a new action to run.
|
||||
*
|
||||
* @param condition the condition to listen to.
|
||||
* @param action the action to run.
|
||||
*/
|
||||
void Bind(std::function<bool()> condition,
|
||||
wpi::unique_function<void()> action);
|
||||
void Bind(wpi::unique_function<void()> action);
|
||||
|
||||
/**
|
||||
* Poll all bindings.
|
||||
@@ -39,12 +37,6 @@ class EventLoop {
|
||||
void Clear();
|
||||
|
||||
private:
|
||||
struct Binding {
|
||||
std::function<bool()> condition;
|
||||
wpi::unique_function<void()> action;
|
||||
|
||||
void Poll();
|
||||
};
|
||||
std::vector<Binding> m_bindings;
|
||||
std::vector<wpi::unique_function<void()>> m_bindings;
|
||||
};
|
||||
} // namespace frc
|
||||
|
||||
@@ -54,7 +54,12 @@ public class BooleanEvent implements BooleanSupplier {
|
||||
* @param action the action to run if this event is active.
|
||||
*/
|
||||
public final void ifHigh(Runnable action) {
|
||||
m_loop.bind(m_signal, action);
|
||||
m_loop.bind(
|
||||
() -> {
|
||||
if (m_signal.getAsBoolean()) {
|
||||
action.run();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,45 +6,27 @@ package edu.wpi.first.wpilibj.event;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.function.BooleanSupplier;
|
||||
|
||||
/** The loop polling {@link BooleanEvent} objects and executing the actions bound to them. */
|
||||
public final class EventLoop {
|
||||
private final Collection<Binding> m_bindings = new LinkedHashSet<>();
|
||||
private final Collection<Runnable> m_bindings = new LinkedHashSet<>();
|
||||
|
||||
/**
|
||||
* Bind a new action to run whenever the condition is true.
|
||||
*
|
||||
* @param condition the condition to listen to.
|
||||
* @param action the action to run.
|
||||
*/
|
||||
public void bind(BooleanSupplier condition, Runnable action) {
|
||||
m_bindings.add(new Binding(condition, action));
|
||||
public void bind(Runnable action) {
|
||||
m_bindings.add(action);
|
||||
}
|
||||
|
||||
/** Poll all bindings. */
|
||||
public void poll() {
|
||||
m_bindings.forEach(Binding::poll);
|
||||
m_bindings.forEach(Runnable::run);
|
||||
}
|
||||
|
||||
/** Clear all bindings. */
|
||||
public void clear() {
|
||||
m_bindings.clear();
|
||||
}
|
||||
|
||||
private static class Binding {
|
||||
private final BooleanSupplier m_condition;
|
||||
private final Runnable m_action;
|
||||
|
||||
private Binding(BooleanSupplier condition, Runnable action) {
|
||||
this.m_condition = condition;
|
||||
this.m_action = action;
|
||||
}
|
||||
|
||||
void poll() {
|
||||
if (m_condition.getAsBoolean()) {
|
||||
m_action.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@ class EventLoopTest {
|
||||
var counterTrue = new AtomicInteger(0);
|
||||
var counterFalse = new AtomicInteger(0);
|
||||
var loop = new EventLoop();
|
||||
loop.bind(() -> true, counterTrue::incrementAndGet);
|
||||
loop.bind(() -> false, counterFalse::incrementAndGet);
|
||||
new BooleanEvent(loop, () -> true).ifHigh(counterTrue::incrementAndGet);
|
||||
new BooleanEvent(loop, () -> false).ifHigh(counterFalse::incrementAndGet);
|
||||
|
||||
assertEquals(0, counterTrue.get());
|
||||
assertEquals(0, counterFalse.get());
|
||||
@@ -40,7 +40,7 @@ class EventLoopTest {
|
||||
var loop = new EventLoop();
|
||||
|
||||
// first ensure binding works
|
||||
loop.bind(condition::get, counter::incrementAndGet);
|
||||
new BooleanEvent(loop, condition::get).ifHigh(counter::incrementAndGet);
|
||||
|
||||
condition.set(false);
|
||||
loop.poll();
|
||||
|
||||
Reference in New Issue
Block a user