bevy/crates/bevy_input/src/button_input.rs

581 lines
22 KiB
Rust
Raw Normal View History

//! The generic input type.
Make `Resource` trait opt-in, requiring `#[derive(Resource)]` V2 (#5577) *This PR description is an edited copy of #5007, written by @alice-i-cecile.* # Objective Follow-up to https://github.com/bevyengine/bevy/pull/2254. The `Resource` trait currently has a blanket implementation for all types that meet its bounds. While ergonomic, this results in several drawbacks: * it is possible to make confusing, silent mistakes such as inserting a function pointer (Foo) rather than a value (Foo::Bar) as a resource * it is challenging to discover if a type is intended to be used as a resource * we cannot later add customization options (see the [RFC](https://github.com/bevyengine/rfcs/blob/main/rfcs/27-derive-component.md) for the equivalent choice for Component). * dependencies can use the same Rust type as a resource in invisibly conflicting ways * raw Rust types used as resources cannot preserve privacy appropriately, as anyone able to access that type can read and write to internal values * we cannot capture a definitive list of possible resources to display to users in an editor ## Notes to reviewers * Review this commit-by-commit; there's effectively no back-tracking and there's a lot of churn in some of these commits. *ira: My commits are not as well organized :')* * I've relaxed the bound on Local to Send + Sync + 'static: I don't think these concerns apply there, so this can keep things simple. Storing e.g. a u32 in a Local is fine, because there's a variable name attached explaining what it does. * I think this is a bad place for the Resource trait to live, but I've left it in place to make reviewing easier. IMO that's best tackled with https://github.com/bevyengine/bevy/issues/4981. ## Changelog `Resource` is no longer automatically implemented for all matching types. Instead, use the new `#[derive(Resource)]` macro. ## Migration Guide Add `#[derive(Resource)]` to all types you are using as a resource. If you are using a third party type as a resource, wrap it in a tuple struct to bypass orphan rules. Consider deriving `Deref` and `DerefMut` to improve ergonomics. `ClearColor` no longer implements `Component`. Using `ClearColor` as a component in 0.8 did nothing. Use the `ClearColorConfig` in the `Camera3d` and `Camera2d` components instead. Co-authored-by: Alice <alice.i.cecile@gmail.com> Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: devil-ira <justthecooldude@gmail.com> Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2022-08-08 21:36:35 +00:00
use bevy_ecs::system::Resource;
use bevy_utils::HashSet;
Add `core` and `alloc` over `std` Lints (#15281) # Objective - Fixes #6370 - Closes #6581 ## Solution - Added the following lints to the workspace: - `std_instead_of_core` - `std_instead_of_alloc` - `alloc_instead_of_core` - Used `cargo +nightly fmt` with [item level use formatting](https://rust-lang.github.io/rustfmt/?version=v1.6.0&search=#Item%5C%3A) to split all `use` statements into single items. - Used `cargo clippy --workspace --all-targets --all-features --fix --allow-dirty` to _attempt_ to resolve the new linting issues, and intervened where the lint was unable to resolve the issue automatically (usually due to needing an `extern crate alloc;` statement in a crate root). - Manually removed certain uses of `std` where negative feature gating prevented `--all-features` from finding the offending uses. - Used `cargo +nightly fmt` with [crate level use formatting](https://rust-lang.github.io/rustfmt/?version=v1.6.0&search=#Crate%5C%3A) to re-merge all `use` statements matching Bevy's previous styling. - Manually fixed cases where the `fmt` tool could not re-merge `use` statements due to conditional compilation attributes. ## Testing - Ran CI locally ## Migration Guide The MSRV is now 1.81. Please update to this version or higher. ## Notes - This is a _massive_ change to try and push through, which is why I've outlined the semi-automatic steps I used to create this PR, in case this fails and someone else tries again in the future. - Making this change has no impact on user code, but does mean Bevy contributors will be warned to use `core` and `alloc` instead of `std` where possible. - This lint is a critical first step towards investigating `no_std` options for Bevy. --------- Co-authored-by: François Mockers <francois.mockers@vleue.com>
2024-09-27 00:59:59 +00:00
use core::hash::Hash;
#[cfg(feature = "bevy_reflect")]
use {
bevy_ecs::reflect::ReflectResource,
bevy_reflect::{std_traits::ReflectDefault, Reflect},
};
/// A "press-able" input of type `T`.
///
/// ## Usage
///
/// This type can be used as a resource to keep the current state of an input, by reacting to
/// events from the input. For a given input value:
///
/// * [`ButtonInput::pressed`] will return `true` between a press and a release event.
/// * [`ButtonInput::just_pressed`] will return `true` for one frame after a press event.
/// * [`ButtonInput::just_released`] will return `true` for one frame after a release event.
///
/// ## Multiple systems
///
/// In case multiple systems are checking for [`ButtonInput::just_pressed`] or [`ButtonInput::just_released`]
/// but only one should react, for example when modifying a
/// [`Resource`], you should consider clearing the input state, either by:
///
/// * Using [`ButtonInput::clear_just_pressed`] or [`ButtonInput::clear_just_released`] instead.
/// * Calling [`ButtonInput::clear`] or [`ButtonInput::reset`] immediately after the state change.
///
/// ## Performance
///
/// For all operations, the following conventions are used:
/// - **n** is the number of stored inputs.
/// - **m** is the number of input arguments passed to the method.
/// - **\***-suffix denotes an amortized cost.
/// - **~**-suffix denotes an expected cost.
///
/// See Rust's [std::collections doc on performance](https://doc.rust-lang.org/std/collections/index.html#performance) for more details on the conventions used here.
///
/// | **[`ButtonInput`] operations** | **Computational complexity** |
/// |-----------------------------------|------------------------------------|
/// | [`ButtonInput::any_just_pressed`] | *O*(m)~ |
/// | [`ButtonInput::any_just_released`] | *O*(m)~ |
/// | [`ButtonInput::any_pressed`] | *O*(m)~ |
/// | [`ButtonInput::get_just_pressed`] | *O*(n) |
/// | [`ButtonInput::get_just_released`] | *O*(n) |
/// | [`ButtonInput::get_pressed`] | *O*(n) |
/// | [`ButtonInput::just_pressed`] | *O*(1)~ |
/// | [`ButtonInput::just_released`] | *O*(1)~ |
/// | [`ButtonInput::pressed`] | *O*(1)~ |
/// | [`ButtonInput::press`] | *O*(1)~* |
/// | [`ButtonInput::release`] | *O*(1)~* |
/// | [`ButtonInput::release_all`] | *O*(n)~* |
/// | [`ButtonInput::clear_just_pressed`] | *O*(1)~ |
/// | [`ButtonInput::clear_just_released`] | *O*(1)~ |
/// | [`ButtonInput::reset_all`] | *O*(n) |
/// | [`ButtonInput::clear`] | *O*(n) |
///
/// ## Window focus
///
/// `ButtonInput<KeyCode>` is tied to window focus. For example, if the user holds a button
/// while the window loses focus, [`ButtonInput::just_released`] will be triggered. Similarly if the window
/// regains focus, [`ButtonInput::just_pressed`] will be triggered.
///
/// `ButtonInput<GamepadButton>` is independent of window focus.
///
/// ## Examples
///
/// Reading and checking against the current set of pressed buttons:
/// ```no_run
/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins, Update};
/// # use bevy_ecs::{prelude::{IntoSystemConfigs, Res, Resource, resource_changed}, schedule::Condition};
Implement gamepads as entities (#12770) # Objective - Significantly improve the ergonomics of gamepads and allow new features Gamepads are a bit unergonomic to work with, they use resources but unlike other inputs, they are not limited to a single gamepad, to get around this it uses an identifier (Gamepad) to interact with anything causing all sorts of issues. 1. There are too many: Gamepads, GamepadSettings, GamepadInfo, ButtonInput<T>, 2 Axis<T>. 2. ButtonInput/Axis generic methods become really inconvenient to use e.g. any_pressed() 3. GamepadButton/Axis structs are unnecessary boilerplate: ```rust for gamepad in gamepads.iter() { if button_inputs.just_pressed(GamepadButton::new(gamepad, GamepadButtonType::South)) { info!("{:?} just pressed South", gamepad); } else if button_inputs.just_released(GamepadButton::new(gamepad, GamepadButtonType::South)) { info!("{:?} just released South", gamepad); } } ``` 4. Projects often need to create resources to store the selected gamepad and have to manually check if their gamepad is still valid anyways. - Previously attempted by #3419 and #12674 ## Solution - Implement gamepads as entities. Using entities solves all the problems above and opens new possibilities. 1. Reduce boilerplate and allows iteration ```rust let is_pressed = gamepads_buttons.iter().any(|buttons| buttons.pressed(GamepadButtonType::South)) ``` 2. ButtonInput/Axis generic methods become ergonomic again ```rust gamepad_buttons.any_just_pressed([GamepadButtonType::Start, GamepadButtonType::Select]) ``` 3. Reduces the number of public components significantly (Gamepad, GamepadSettings, GamepadButtons, GamepadAxes) 4. Components are highly convenient. Gamepad optional features could now be expressed naturally (`Option<Rumble> or Option<Gyro>`), allows devs to attach their own components and filter them, so code like this becomes possible: ```rust fn move_player<const T: usize>( player: Query<&Transform, With<Player<T>>>, gamepads_buttons: Query<&GamepadButtons, With<Player<T>>>, ) { if let Ok(gamepad_buttons) = gamepads_buttons.get_single() { if gamepad_buttons.pressed(GamepadButtonType::South) { // move player } } } ``` --- ## Follow-up - [ ] Run conditions? - [ ] Rumble component # Changelog ## Added TODO ## Changed TODO ## Removed TODO ## Migration Guide TODO --------- Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2024-09-27 20:07:20 +00:00
/// # use bevy_input::{ButtonInput, prelude::{KeyCode, MouseButton}};
///
/// fn main() {
/// App::new()
/// .add_plugins(DefaultPlugins)
/// .add_systems(
/// Update,
/// print_mouse.run_if(resource_changed::<ButtonInput<MouseButton>>),
/// )
/// .add_systems(
/// Update,
/// print_keyboard.run_if(resource_changed::<ButtonInput<KeyCode>>),
/// )
/// .run();
/// }
///
/// fn print_mouse(mouse: Res<ButtonInput<MouseButton>>) {
/// println!("Mouse: {:?}", mouse.get_pressed().collect::<Vec<_>>());
/// }
///
/// fn print_keyboard(keyboard: Res<ButtonInput<KeyCode>>) {
/// if keyboard.any_pressed([KeyCode::ControlLeft, KeyCode::ControlRight])
/// && keyboard.any_pressed([KeyCode::AltLeft, KeyCode::AltRight])
/// && keyboard.any_pressed([KeyCode::ShiftLeft, KeyCode::ShiftRight])
/// && keyboard.any_pressed([KeyCode::SuperLeft, KeyCode::SuperRight])
/// && keyboard.pressed(KeyCode::KeyL)
/// {
/// println!("On Windows this opens LinkedIn.");
/// } else {
/// println!("keyboard: {:?}", keyboard.get_pressed().collect::<Vec<_>>());
/// }
/// }
/// ```
///
/// ## Note
///
/// When adding this resource for a new input type, you should:
///
/// * Call the [`ButtonInput::press`] method for each press event.
/// * Call the [`ButtonInput::release`] method for each release event.
/// * Call the [`ButtonInput::clear`] method at each frame start, before processing events.
///
/// Note: Calling `clear` from a [`ResMut`] will trigger change detection.
/// It may be preferable to use [`DetectChangesMut::bypass_change_detection`]
/// to avoid causing the resource to always be marked as changed.
///
/// [`ResMut`]: bevy_ecs::system::ResMut
/// [`DetectChangesMut::bypass_change_detection`]: bevy_ecs::change_detection::DetectChangesMut::bypass_change_detection
#[derive(Debug, Clone, Resource)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Default, Resource))]
pub struct ButtonInput<T: Copy + Eq + Hash + Send + Sync + 'static> {
/// A collection of every button that is currently being pressed.
pressed: HashSet<T>,
/// A collection of every button that has just been pressed.
just_pressed: HashSet<T>,
/// A collection of every button that has just been released.
just_released: HashSet<T>,
}
impl<T: Copy + Eq + Hash + Send + Sync + 'static> Default for ButtonInput<T> {
fn default() -> Self {
Self {
pressed: Default::default(),
just_pressed: Default::default(),
just_released: Default::default(),
}
}
}
impl<T> ButtonInput<T>
where
T: Copy + Eq + Hash + Send + Sync + 'static,
{
/// Registers a press for the given `input`.
pub fn press(&mut self, input: T) {
// Returns `true` if the `input` wasn't pressed.
if self.pressed.insert(input) {
self.just_pressed.insert(input);
}
}
/// Returns `true` if the `input` has been pressed.
pub fn pressed(&self, input: T) -> bool {
self.pressed.contains(&input)
}
/// Returns `true` if any item in `inputs` has been pressed.
pub fn any_pressed(&self, inputs: impl IntoIterator<Item = T>) -> bool {
inputs.into_iter().any(|it| self.pressed(it))
}
/// Returns `true` if all items in `inputs` have been pressed.
pub fn all_pressed(&self, inputs: impl IntoIterator<Item = T>) -> bool {
inputs.into_iter().all(|it| self.pressed(it))
}
/// Registers a release for the given `input`.
pub fn release(&mut self, input: T) {
// Returns `true` if the `input` was pressed.
if self.pressed.remove(&input) {
self.just_released.insert(input);
}
}
/// Registers a release for all currently pressed inputs.
pub fn release_all(&mut self) {
// Move all items from pressed into just_released
self.just_released.extend(self.pressed.drain());
}
/// Returns `true` if the `input` has been pressed during the current frame.
///
/// Note: This function does not imply information regarding the current state of [`ButtonInput::pressed`] or [`ButtonInput::just_released`].
pub fn just_pressed(&self, input: T) -> bool {
self.just_pressed.contains(&input)
}
/// Returns `true` if any item in `inputs` has been pressed during the current frame.
pub fn any_just_pressed(&self, inputs: impl IntoIterator<Item = T>) -> bool {
inputs.into_iter().any(|it| self.just_pressed(it))
}
/// Clears the `just_pressed` state of the `input` and returns `true` if the `input` has just been pressed.
///
/// Future calls to [`ButtonInput::just_pressed`] for the given input will return false until a new press event occurs.
pub fn clear_just_pressed(&mut self, input: T) -> bool {
self.just_pressed.remove(&input)
}
/// Returns `true` if the `input` has been released during the current frame.
///
/// Note: This function does not imply information regarding the current state of [`ButtonInput::pressed`] or [`ButtonInput::just_pressed`].
pub fn just_released(&self, input: T) -> bool {
self.just_released.contains(&input)
}
/// Returns `true` if any item in `inputs` has just been released.
pub fn any_just_released(&self, inputs: impl IntoIterator<Item = T>) -> bool {
inputs.into_iter().any(|input| self.just_released(input))
}
/// Returns `true` if all items in `inputs` have just been released.
pub fn all_just_released(&self, inputs: impl IntoIterator<Item = T>) -> bool {
inputs.into_iter().all(|input| self.just_released(input))
}
/// Returns `true` if all items in `inputs` have been just pressed.
pub fn all_just_pressed(&self, inputs: impl IntoIterator<Item = T>) -> bool {
inputs.into_iter().all(|input| self.just_pressed(input))
}
/// Clears the `just_released` state of the `input` and returns `true` if the `input` has just been released.
///
/// Future calls to [`ButtonInput::just_released`] for the given input will return false until a new release event occurs.
pub fn clear_just_released(&mut self, input: T) -> bool {
self.just_released.remove(&input)
}
/// Clears the `pressed`, `just_pressed` and `just_released` data of the `input`.
pub fn reset(&mut self, input: T) {
self.pressed.remove(&input);
self.just_pressed.remove(&input);
self.just_released.remove(&input);
}
/// Clears the `pressed`, `just_pressed`, and `just_released` data for every input.
///
/// See also [`ButtonInput::clear`] for simulating elapsed time steps.
pub fn reset_all(&mut self) {
self.pressed.clear();
self.just_pressed.clear();
self.just_released.clear();
}
/// Clears the `just pressed` and `just released` data for every input.
///
/// See also [`ButtonInput::reset_all`] for a full reset.
pub fn clear(&mut self) {
self.just_pressed.clear();
self.just_released.clear();
}
/// An iterator visiting every pressed input in arbitrary order.
pub fn get_pressed(&self) -> impl ExactSizeIterator<Item = &T> {
self.pressed.iter()
}
/// An iterator visiting every just pressed input in arbitrary order.
///
/// Note: Returned elements do not imply information regarding the current state of [`ButtonInput::pressed`] or [`ButtonInput::just_released`].
pub fn get_just_pressed(&self) -> impl ExactSizeIterator<Item = &T> {
self.just_pressed.iter()
}
/// An iterator visiting every just released input in arbitrary order.
///
/// Note: Returned elements do not imply information regarding the current state of [`ButtonInput::pressed`] or [`ButtonInput::just_pressed`].
pub fn get_just_released(&self) -> impl ExactSizeIterator<Item = &T> {
self.just_released.iter()
}
}
#[cfg(test)]
mod test {
use crate::ButtonInput;
/// Used for testing the functionality of [`ButtonInput`].
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
enum DummyInput {
Input1,
Input2,
}
#[test]
fn test_press() {
let mut input = ButtonInput::default();
assert!(!input.pressed.contains(&DummyInput::Input1));
assert!(!input.just_pressed.contains(&DummyInput::Input1));
input.press(DummyInput::Input1);
assert!(input.just_pressed.contains(&DummyInput::Input1));
assert!(input.pressed.contains(&DummyInput::Input1));
}
#[test]
fn test_pressed() {
let mut input = ButtonInput::default();
assert!(!input.pressed(DummyInput::Input1));
input.press(DummyInput::Input1);
assert!(input.pressed(DummyInput::Input1));
}
#[test]
fn test_any_pressed() {
let mut input = ButtonInput::default();
assert!(!input.any_pressed([DummyInput::Input1]));
assert!(!input.any_pressed([DummyInput::Input2]));
assert!(!input.any_pressed([DummyInput::Input1, DummyInput::Input2]));
input.press(DummyInput::Input1);
assert!(input.any_pressed([DummyInput::Input1]));
assert!(!input.any_pressed([DummyInput::Input2]));
assert!(input.any_pressed([DummyInput::Input1, DummyInput::Input2]));
}
#[test]
fn test_all_pressed() {
let mut input = ButtonInput::default();
assert!(!input.all_pressed([DummyInput::Input1]));
assert!(!input.all_pressed([DummyInput::Input2]));
assert!(!input.all_pressed([DummyInput::Input1, DummyInput::Input2]));
input.press(DummyInput::Input1);
assert!(input.all_pressed([DummyInput::Input1]));
assert!(!input.all_pressed([DummyInput::Input1, DummyInput::Input2]));
input.press(DummyInput::Input2);
assert!(input.all_pressed([DummyInput::Input1, DummyInput::Input2]));
}
#[test]
fn test_release() {
let mut input = ButtonInput::default();
input.press(DummyInput::Input1);
assert!(input.pressed.contains(&DummyInput::Input1));
assert!(!input.just_released.contains(&DummyInput::Input1));
input.release(DummyInput::Input1);
assert!(!input.pressed.contains(&DummyInput::Input1));
assert!(input.just_released.contains(&DummyInput::Input1));
}
#[test]
fn test_release_all() {
let mut input = ButtonInput::default();
input.press(DummyInput::Input1);
input.press(DummyInput::Input2);
input.release_all();
assert!(input.pressed.is_empty());
assert!(input.just_released.contains(&DummyInput::Input1));
assert!(input.just_released.contains(&DummyInput::Input2));
}
#[test]
fn test_just_pressed() {
let mut input = ButtonInput::default();
assert!(!input.just_pressed(DummyInput::Input1));
input.press(DummyInput::Input1);
assert!(input.just_pressed(DummyInput::Input1));
}
#[test]
fn test_any_just_pressed() {
let mut input = ButtonInput::default();
assert!(!input.any_just_pressed([DummyInput::Input1]));
assert!(!input.any_just_pressed([DummyInput::Input2]));
assert!(!input.any_just_pressed([DummyInput::Input1, DummyInput::Input2]));
input.press(DummyInput::Input1);
assert!(input.any_just_pressed([DummyInput::Input1]));
assert!(!input.any_just_pressed([DummyInput::Input2]));
assert!(input.any_just_pressed([DummyInput::Input1, DummyInput::Input2]));
}
#[test]
fn test_clear_just_pressed() {
let mut input = ButtonInput::default();
input.press(DummyInput::Input1);
assert!(input.just_pressed(DummyInput::Input1));
input.clear_just_pressed(DummyInput::Input1);
assert!(!input.just_pressed(DummyInput::Input1));
}
#[test]
fn test_just_released() {
let mut input = ButtonInput::default();
input.press(DummyInput::Input1);
assert!(!input.just_released(DummyInput::Input1));
input.release(DummyInput::Input1);
assert!(input.just_released(DummyInput::Input1));
}
#[test]
fn test_any_just_released() {
let mut input = ButtonInput::default();
input.press(DummyInput::Input1);
assert!(!input.any_just_released([DummyInput::Input1]));
assert!(!input.any_just_released([DummyInput::Input2]));
assert!(!input.any_just_released([DummyInput::Input1, DummyInput::Input2]));
input.release(DummyInput::Input1);
assert!(input.any_just_released([DummyInput::Input1]));
assert!(!input.any_just_released([DummyInput::Input2]));
assert!(input.any_just_released([DummyInput::Input1, DummyInput::Input2]));
}
#[test]
fn test_clear_just_released() {
let mut input = ButtonInput::default();
input.press(DummyInput::Input1);
input.release(DummyInput::Input1);
assert!(input.just_released(DummyInput::Input1));
input.clear_just_released(DummyInput::Input1);
assert!(!input.just_released(DummyInput::Input1));
}
#[test]
fn test_reset() {
let mut input = ButtonInput::default();
// Pressed
input.press(DummyInput::Input1);
assert!(input.pressed(DummyInput::Input1));
assert!(input.just_pressed(DummyInput::Input1));
assert!(!input.just_released(DummyInput::Input1));
input.reset(DummyInput::Input1);
assert!(!input.pressed(DummyInput::Input1));
assert!(!input.just_pressed(DummyInput::Input1));
assert!(!input.just_released(DummyInput::Input1));
// Released
input.press(DummyInput::Input1);
input.release(DummyInput::Input1);
assert!(!input.pressed(DummyInput::Input1));
assert!(input.just_pressed(DummyInput::Input1));
assert!(input.just_released(DummyInput::Input1));
input.reset(DummyInput::Input1);
assert!(!input.pressed(DummyInput::Input1));
assert!(!input.just_pressed(DummyInput::Input1));
assert!(!input.just_released(DummyInput::Input1));
}
#[test]
fn test_reset_all() {
let mut input = ButtonInput::default();
input.press(DummyInput::Input1);
input.press(DummyInput::Input2);
input.release(DummyInput::Input2);
assert!(input.pressed.contains(&DummyInput::Input1));
assert!(input.just_pressed.contains(&DummyInput::Input1));
assert!(input.just_released.contains(&DummyInput::Input2));
input.reset_all();
assert!(input.pressed.is_empty());
assert!(input.just_pressed.is_empty());
assert!(input.just_released.is_empty());
}
#[test]
fn test_clear() {
let mut input = ButtonInput::default();
// Pressed
input.press(DummyInput::Input1);
assert!(input.pressed(DummyInput::Input1));
assert!(input.just_pressed(DummyInput::Input1));
assert!(!input.just_released(DummyInput::Input1));
input.clear();
assert!(input.pressed(DummyInput::Input1));
assert!(!input.just_pressed(DummyInput::Input1));
assert!(!input.just_released(DummyInput::Input1));
// Released
input.press(DummyInput::Input1);
input.release(DummyInput::Input1);
assert!(!input.pressed(DummyInput::Input1));
assert!(!input.just_pressed(DummyInput::Input1));
assert!(input.just_released(DummyInput::Input1));
input.clear();
assert!(!input.pressed(DummyInput::Input1));
assert!(!input.just_pressed(DummyInput::Input1));
assert!(!input.just_released(DummyInput::Input1));
}
#[test]
fn test_get_pressed() {
let mut input = ButtonInput::default();
input.press(DummyInput::Input1);
input.press(DummyInput::Input2);
let pressed = input.get_pressed();
assert_eq!(pressed.len(), 2);
for pressed_input in pressed {
assert!(input.pressed.contains(pressed_input));
}
}
#[test]
fn test_get_just_pressed() {
let mut input = ButtonInput::default();
input.press(DummyInput::Input1);
input.press(DummyInput::Input2);
let just_pressed = input.get_just_pressed();
assert_eq!(just_pressed.len(), 2);
for just_pressed_input in just_pressed {
assert!(input.just_pressed.contains(just_pressed_input));
}
}
#[test]
fn test_get_just_released() {
let mut input = ButtonInput::default();
input.press(DummyInput::Input1);
input.press(DummyInput::Input2);
input.release(DummyInput::Input1);
input.release(DummyInput::Input2);
let just_released = input.get_just_released();
assert_eq!(just_released.len(), 2);
for just_released_input in just_released {
assert!(input.just_released.contains(just_released_input));
}
}
#[test]
fn test_general_input_handling() {
let mut input = ButtonInput::default();
// Test pressing
input.press(DummyInput::Input1);
input.press(DummyInput::Input2);
// Check if they were `just_pressed` (pressed on this update)
assert!(input.just_pressed(DummyInput::Input1));
assert!(input.just_pressed(DummyInput::Input2));
// Check if they are also marked as pressed
assert!(input.pressed(DummyInput::Input1));
assert!(input.pressed(DummyInput::Input2));
// Clear the `input`, removing `just_pressed` and `just_released`
input.clear();
// Check if they're marked `just_pressed`
assert!(!input.just_pressed(DummyInput::Input1));
assert!(!input.just_pressed(DummyInput::Input2));
// Check if they're marked as pressed
assert!(input.pressed(DummyInput::Input1));
assert!(input.pressed(DummyInput::Input2));
// Release the inputs and check state
input.release(DummyInput::Input1);
input.release(DummyInput::Input2);
// Check if they're marked as `just_released` (released on this update)
assert!(input.just_released(DummyInput::Input1));
assert!(input.just_released(DummyInput::Input2));
// Check that they're not incorrectly marked as pressed
assert!(!input.pressed(DummyInput::Input1));
assert!(!input.pressed(DummyInput::Input2));
// Clear the `Input` and check for removal from `just_released`
input.clear();
// Check that they're not incorrectly marked as just released
assert!(!input.just_released(DummyInput::Input1));
assert!(!input.just_released(DummyInput::Input2));
// Set up an `Input` to test resetting
let mut input = ButtonInput::default();
input.press(DummyInput::Input1);
input.release(DummyInput::Input2);
// Reset the `Input` and test if it was reset correctly
input.reset(DummyInput::Input1);
input.reset(DummyInput::Input2);
assert!(!input.just_pressed(DummyInput::Input1));
assert!(!input.pressed(DummyInput::Input1));
assert!(!input.just_released(DummyInput::Input2));
}
}