bevy/crates/bevy_input/src/input.rs

466 lines
17 KiB
Rust
Raw Normal View History

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;
Derive `Reflect` + `FromReflect` for input types (#6232) # Objective Adds support for reflecting many more of the input types. This allows those types to be used via scripting, `bevy-inspector-egui`, etc. These types are registered by the `InputPlugin` so that they're automatically available to anyone who wants to use them Closes #6223 ## Solution Many types now have `#[derive(Reflect, FromReflect)]` added to them in `bevy_input`. Additionally, `#[reflect(traits...)]` has been added for applicable traits to the types. This PR does not add reflection support for types which have private fields. Notably, `Touch` and `Touches` don't implement `Reflect`/`FromReflect`. This adds the "glam" feature to the `bevy_reflect` dependency for package `bevy_input`. Since `bevy_input` transitively depends on `glam` already, all this brings in are the reflection `impl`s. ## Migration Guide - `Input<T>` now implements `Reflect` via `#[reflect]` instead of `#[reflect_value]`. This means it now exposes its private fields via the `Reflect` trait rather than being treated as a value type. For code that relies on the `Input<T>` struct being treated as a value type by reflection, it is still possible to wrap the `Input<T>` type with a wrapper struct and apply `#[reflect_value]` to it. - As a reminder, private fields exposed via reflection are not subject to any stability guarantees. --- ## Changelog Added - Implemented `Reflect` + `FromReflect` for many input-related types. These types are automatically registered when adding the `InputPlugin`.
2022-10-26 19:52:20 +00:00
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
use bevy_utils::HashSet;
use std::hash::Hash;
// unused import, but needed for intra doc link to work
#[allow(unused_imports)]
Migrate engine to Schedule v3 (#7267) Huge thanks to @maniwani, @devil-ira, @hymm, @cart, @superdump and @jakobhellermann for the help with this PR. # Objective - Followup #6587. - Minimal integration for the Stageless Scheduling RFC: https://github.com/bevyengine/rfcs/pull/45 ## Solution - [x] Remove old scheduling module - [x] Migrate new methods to no longer use extension methods - [x] Fix compiler errors - [x] Fix benchmarks - [x] Fix examples - [x] Fix docs - [x] Fix tests ## Changelog ### Added - a large number of methods on `App` to work with schedules ergonomically - the `CoreSchedule` enum - `App::add_extract_system` via the `RenderingAppExtension` trait extension method - the private `prepare_view_uniforms` system now has a public system set for scheduling purposes, called `ViewSet::PrepareUniforms` ### Removed - stages, and all code that mentions stages - states have been dramatically simplified, and no longer use a stack - `RunCriteriaLabel` - `AsSystemLabel` trait - `on_hierarchy_reports_enabled` run criteria (now just uses an ad hoc resource checking run condition) - systems in `RenderSet/Stage::Extract` no longer warn when they do not read data from the main world - `RunCriteriaLabel` - `transform_propagate_system_set`: this was a nonstandard pattern that didn't actually provide enough control. The systems are already `pub`: the docs have been updated to ensure that the third-party usage is clear. ### Changed - `System::default_labels` is now `System::default_system_sets`. - `App::add_default_labels` is now `App::add_default_sets` - `CoreStage` and `StartupStage` enums are now `CoreSet` and `StartupSet` - `App::add_system_set` was renamed to `App::add_systems` - The `StartupSchedule` label is now defined as part of the `CoreSchedules` enum - `.label(SystemLabel)` is now referred to as `.in_set(SystemSet)` - `SystemLabel` trait was replaced by `SystemSet` - `SystemTypeIdLabel<T>` was replaced by `SystemSetType<T>` - The `ReportHierarchyIssue` resource now has a public constructor (`new`), and implements `PartialEq` - Fixed time steps now use a schedule (`CoreSchedule::FixedTimeStep`) rather than a run criteria. - Adding rendering extraction systems now panics rather than silently failing if no subapp with the `RenderApp` label is found. - the `calculate_bounds` system, with the `CalculateBounds` label, is now in `CoreSet::Update`, rather than in `CoreSet::PostUpdate` before commands are applied. - `SceneSpawnerSystem` now runs under `CoreSet::Update`, rather than `CoreStage::PreUpdate.at_end()`. - `bevy_pbr::add_clusters` is no longer an exclusive system - the top level `bevy_ecs::schedule` module was replaced with `bevy_ecs::scheduling` - `tick_global_task_pools_on_main_thread` is no longer run as an exclusive system. Instead, it has been replaced by `tick_global_task_pools`, which uses a `NonSend` resource to force running on the main thread. ## Migration Guide - Calls to `.label(MyLabel)` should be replaced with `.in_set(MySet)` - Stages have been removed. Replace these with system sets, and then add command flushes using the `apply_system_buffers` exclusive system where needed. - The `CoreStage`, `StartupStage, `RenderStage` and `AssetStage` enums have been replaced with `CoreSet`, `StartupSet, `RenderSet` and `AssetSet`. The same scheduling guarantees have been preserved. - Systems are no longer added to `CoreSet::Update` by default. Add systems manually if this behavior is needed, although you should consider adding your game logic systems to `CoreSchedule::FixedTimestep` instead for more reliable framerate-independent behavior. - Similarly, startup systems are no longer part of `StartupSet::Startup` by default. In most cases, this won't matter to you. - For example, `add_system_to_stage(CoreStage::PostUpdate, my_system)` should be replaced with - `add_system(my_system.in_set(CoreSet::PostUpdate)` - When testing systems or otherwise running them in a headless fashion, simply construct and run a schedule using `Schedule::new()` and `World::run_schedule` rather than constructing stages - Run criteria have been renamed to run conditions. These can now be combined with each other and with states. - Looping run criteria and state stacks have been removed. Use an exclusive system that runs a schedule if you need this level of control over system control flow. - For app-level control flow over which schedules get run when (such as for rollback networking), create your own schedule and insert it under the `CoreSchedule::Outer` label. - Fixed timesteps are now evaluated in a schedule, rather than controlled via run criteria. The `run_fixed_timestep` system runs this schedule between `CoreSet::First` and `CoreSet::PreUpdate` by default. - Command flush points introduced by `AssetStage` have been removed. If you were relying on these, add them back manually. - Adding extract systems is now typically done directly on the main app. Make sure the `RenderingAppExtension` trait is in scope, then call `app.add_extract_system(my_system)`. - the `calculate_bounds` system, with the `CalculateBounds` label, is now in `CoreSet::Update`, rather than in `CoreSet::PostUpdate` before commands are applied. You may need to order your movement systems to occur before this system in order to avoid system order ambiguities in culling behavior. - the `RenderLabel` `AppLabel` was renamed to `RenderApp` for clarity - `App::add_state` now takes 0 arguments: the starting state is set based on the `Default` impl. - Instead of creating `SystemSet` containers for systems that run in stages, simply use `.on_enter::<State::Variant>()` or its `on_exit` or `on_update` siblings. - `SystemLabel` derives should be replaced with `SystemSet`. You will also need to add the `Debug`, `PartialEq`, `Eq`, and `Hash` traits to satisfy the new trait bounds. - `with_run_criteria` has been renamed to `run_if`. Run criteria have been renamed to run conditions for clarity, and should now simply return a bool. - States have been dramatically simplified: there is no longer a "state stack". To queue a transition to the next state, call `NextState::set` ## TODO - [x] remove dead methods on App and World - [x] add `App::add_system_to_schedule` and `App::add_systems_to_schedule` - [x] avoid adding the default system set at inappropriate times - [x] remove any accidental cycles in the default plugins schedule - [x] migrate benchmarks - [x] expose explicit labels for the built-in command flush points - [x] migrate engine code - [x] remove all mentions of stages from the docs - [x] verify docs for States - [x] fix uses of exclusive systems that use .end / .at_start / .before_commands - [x] migrate RenderStage and AssetStage - [x] migrate examples - [x] ensure that transform propagation is exported in a sufficiently public way (the systems are already pub) - [x] ensure that on_enter schedules are run at least once before the main app - [x] re-enable opt-in to execution order ambiguities - [x] revert change to `update_bounds` to ensure it runs in `PostUpdate` - [x] test all examples - [x] unbreak directional lights - [x] unbreak shadows (see 3d_scene, 3d_shape, lighting, transparaency_3d examples) - [x] game menu example shows loading screen and menu simultaneously - [x] display settings menu is a blank screen - [x] `without_winit` example panics - [x] ensure all tests pass - [x] SubApp doc test fails - [x] runs_spawn_local tasks fails - [x] [Fix panic_when_hierachy_cycle test hanging](https://github.com/alice-i-cecile/bevy/pull/120) ## Points of Difficulty and Controversy **Reviewers, please give feedback on these and look closely** 1. Default sets, from the RFC, have been removed. These added a tremendous amount of implicit complexity and result in hard to debug scheduling errors. They're going to be tackled in the form of "base sets" by @cart in a followup. 2. The outer schedule controls which schedule is run when `App::update` is called. 3. I implemented `Label for `Box<dyn Label>` for our label types. This enables us to store schedule labels in concrete form, and then later run them. I ran into the same set of problems when working with one-shot systems. We've previously investigated this pattern in depth, and it does not appear to lead to extra indirection with nested boxes. 4. `SubApp::update` simply runs the default schedule once. This sucks, but this whole API is incomplete and this was the minimal changeset. 5. `time_system` and `tick_global_task_pools_on_main_thread` no longer use exclusive systems to attempt to force scheduling order 6. Implemetnation strategy for fixed timesteps 7. `AssetStage` was migrated to `AssetSet` without reintroducing command flush points. These did not appear to be used, and it's nice to remove these bottlenecks. 8. Migration of `bevy_render/lib.rs` and pipelined rendering. The logic here is unusually tricky, as we have complex scheduling requirements. ## Future Work (ideally before 0.10) - Rename schedule_v3 module to schedule or scheduling - Add a derive macro to states, and likely a `EnumIter` trait of some form - Figure out what exactly to do with the "systems added should basically work by default" problem - Improve ergonomics for working with fixed timesteps and states - Polish FixedTime API to match Time - Rebase and merge #7415 - Resolve all internal ambiguities (blocked on better tools, especially #7442) - Add "base sets" to replace the removed default sets.
2023-02-06 02:04:50 +00:00
use bevy_ecs::schedule_v3::State;
/// 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:
///
/// * [`Input::pressed`] will return `true` between a press and a release event.
/// * [`Input::just_pressed`] will return `true` for one frame after a press event.
/// * [`Input::just_released`] will return `true` for one frame after a release event.
///
/// ## Multiple systems
///
/// In case multiple systems are checking for [`Input::just_pressed`] or [`Input::just_released`]
/// but only one should react, for example in the case of triggering
Migrate engine to Schedule v3 (#7267) Huge thanks to @maniwani, @devil-ira, @hymm, @cart, @superdump and @jakobhellermann for the help with this PR. # Objective - Followup #6587. - Minimal integration for the Stageless Scheduling RFC: https://github.com/bevyengine/rfcs/pull/45 ## Solution - [x] Remove old scheduling module - [x] Migrate new methods to no longer use extension methods - [x] Fix compiler errors - [x] Fix benchmarks - [x] Fix examples - [x] Fix docs - [x] Fix tests ## Changelog ### Added - a large number of methods on `App` to work with schedules ergonomically - the `CoreSchedule` enum - `App::add_extract_system` via the `RenderingAppExtension` trait extension method - the private `prepare_view_uniforms` system now has a public system set for scheduling purposes, called `ViewSet::PrepareUniforms` ### Removed - stages, and all code that mentions stages - states have been dramatically simplified, and no longer use a stack - `RunCriteriaLabel` - `AsSystemLabel` trait - `on_hierarchy_reports_enabled` run criteria (now just uses an ad hoc resource checking run condition) - systems in `RenderSet/Stage::Extract` no longer warn when they do not read data from the main world - `RunCriteriaLabel` - `transform_propagate_system_set`: this was a nonstandard pattern that didn't actually provide enough control. The systems are already `pub`: the docs have been updated to ensure that the third-party usage is clear. ### Changed - `System::default_labels` is now `System::default_system_sets`. - `App::add_default_labels` is now `App::add_default_sets` - `CoreStage` and `StartupStage` enums are now `CoreSet` and `StartupSet` - `App::add_system_set` was renamed to `App::add_systems` - The `StartupSchedule` label is now defined as part of the `CoreSchedules` enum - `.label(SystemLabel)` is now referred to as `.in_set(SystemSet)` - `SystemLabel` trait was replaced by `SystemSet` - `SystemTypeIdLabel<T>` was replaced by `SystemSetType<T>` - The `ReportHierarchyIssue` resource now has a public constructor (`new`), and implements `PartialEq` - Fixed time steps now use a schedule (`CoreSchedule::FixedTimeStep`) rather than a run criteria. - Adding rendering extraction systems now panics rather than silently failing if no subapp with the `RenderApp` label is found. - the `calculate_bounds` system, with the `CalculateBounds` label, is now in `CoreSet::Update`, rather than in `CoreSet::PostUpdate` before commands are applied. - `SceneSpawnerSystem` now runs under `CoreSet::Update`, rather than `CoreStage::PreUpdate.at_end()`. - `bevy_pbr::add_clusters` is no longer an exclusive system - the top level `bevy_ecs::schedule` module was replaced with `bevy_ecs::scheduling` - `tick_global_task_pools_on_main_thread` is no longer run as an exclusive system. Instead, it has been replaced by `tick_global_task_pools`, which uses a `NonSend` resource to force running on the main thread. ## Migration Guide - Calls to `.label(MyLabel)` should be replaced with `.in_set(MySet)` - Stages have been removed. Replace these with system sets, and then add command flushes using the `apply_system_buffers` exclusive system where needed. - The `CoreStage`, `StartupStage, `RenderStage` and `AssetStage` enums have been replaced with `CoreSet`, `StartupSet, `RenderSet` and `AssetSet`. The same scheduling guarantees have been preserved. - Systems are no longer added to `CoreSet::Update` by default. Add systems manually if this behavior is needed, although you should consider adding your game logic systems to `CoreSchedule::FixedTimestep` instead for more reliable framerate-independent behavior. - Similarly, startup systems are no longer part of `StartupSet::Startup` by default. In most cases, this won't matter to you. - For example, `add_system_to_stage(CoreStage::PostUpdate, my_system)` should be replaced with - `add_system(my_system.in_set(CoreSet::PostUpdate)` - When testing systems or otherwise running them in a headless fashion, simply construct and run a schedule using `Schedule::new()` and `World::run_schedule` rather than constructing stages - Run criteria have been renamed to run conditions. These can now be combined with each other and with states. - Looping run criteria and state stacks have been removed. Use an exclusive system that runs a schedule if you need this level of control over system control flow. - For app-level control flow over which schedules get run when (such as for rollback networking), create your own schedule and insert it under the `CoreSchedule::Outer` label. - Fixed timesteps are now evaluated in a schedule, rather than controlled via run criteria. The `run_fixed_timestep` system runs this schedule between `CoreSet::First` and `CoreSet::PreUpdate` by default. - Command flush points introduced by `AssetStage` have been removed. If you were relying on these, add them back manually. - Adding extract systems is now typically done directly on the main app. Make sure the `RenderingAppExtension` trait is in scope, then call `app.add_extract_system(my_system)`. - the `calculate_bounds` system, with the `CalculateBounds` label, is now in `CoreSet::Update`, rather than in `CoreSet::PostUpdate` before commands are applied. You may need to order your movement systems to occur before this system in order to avoid system order ambiguities in culling behavior. - the `RenderLabel` `AppLabel` was renamed to `RenderApp` for clarity - `App::add_state` now takes 0 arguments: the starting state is set based on the `Default` impl. - Instead of creating `SystemSet` containers for systems that run in stages, simply use `.on_enter::<State::Variant>()` or its `on_exit` or `on_update` siblings. - `SystemLabel` derives should be replaced with `SystemSet`. You will also need to add the `Debug`, `PartialEq`, `Eq`, and `Hash` traits to satisfy the new trait bounds. - `with_run_criteria` has been renamed to `run_if`. Run criteria have been renamed to run conditions for clarity, and should now simply return a bool. - States have been dramatically simplified: there is no longer a "state stack". To queue a transition to the next state, call `NextState::set` ## TODO - [x] remove dead methods on App and World - [x] add `App::add_system_to_schedule` and `App::add_systems_to_schedule` - [x] avoid adding the default system set at inappropriate times - [x] remove any accidental cycles in the default plugins schedule - [x] migrate benchmarks - [x] expose explicit labels for the built-in command flush points - [x] migrate engine code - [x] remove all mentions of stages from the docs - [x] verify docs for States - [x] fix uses of exclusive systems that use .end / .at_start / .before_commands - [x] migrate RenderStage and AssetStage - [x] migrate examples - [x] ensure that transform propagation is exported in a sufficiently public way (the systems are already pub) - [x] ensure that on_enter schedules are run at least once before the main app - [x] re-enable opt-in to execution order ambiguities - [x] revert change to `update_bounds` to ensure it runs in `PostUpdate` - [x] test all examples - [x] unbreak directional lights - [x] unbreak shadows (see 3d_scene, 3d_shape, lighting, transparaency_3d examples) - [x] game menu example shows loading screen and menu simultaneously - [x] display settings menu is a blank screen - [x] `without_winit` example panics - [x] ensure all tests pass - [x] SubApp doc test fails - [x] runs_spawn_local tasks fails - [x] [Fix panic_when_hierachy_cycle test hanging](https://github.com/alice-i-cecile/bevy/pull/120) ## Points of Difficulty and Controversy **Reviewers, please give feedback on these and look closely** 1. Default sets, from the RFC, have been removed. These added a tremendous amount of implicit complexity and result in hard to debug scheduling errors. They're going to be tackled in the form of "base sets" by @cart in a followup. 2. The outer schedule controls which schedule is run when `App::update` is called. 3. I implemented `Label for `Box<dyn Label>` for our label types. This enables us to store schedule labels in concrete form, and then later run them. I ran into the same set of problems when working with one-shot systems. We've previously investigated this pattern in depth, and it does not appear to lead to extra indirection with nested boxes. 4. `SubApp::update` simply runs the default schedule once. This sucks, but this whole API is incomplete and this was the minimal changeset. 5. `time_system` and `tick_global_task_pools_on_main_thread` no longer use exclusive systems to attempt to force scheduling order 6. Implemetnation strategy for fixed timesteps 7. `AssetStage` was migrated to `AssetSet` without reintroducing command flush points. These did not appear to be used, and it's nice to remove these bottlenecks. 8. Migration of `bevy_render/lib.rs` and pipelined rendering. The logic here is unusually tricky, as we have complex scheduling requirements. ## Future Work (ideally before 0.10) - Rename schedule_v3 module to schedule or scheduling - Add a derive macro to states, and likely a `EnumIter` trait of some form - Figure out what exactly to do with the "systems added should basically work by default" problem - Improve ergonomics for working with fixed timesteps and states - Polish FixedTime API to match Time - Rebase and merge #7415 - Resolve all internal ambiguities (blocked on better tools, especially #7442) - Add "base sets" to replace the removed default sets.
2023-02-06 02:04:50 +00:00
/// [`State`](bevy_ecs::schedule_v3::State) change, you should consider clearing the input state, either by:
///
/// * Using [`Input::clear_just_pressed`] or [`Input::clear_just_released`] instead.
/// * Calling [`Input::clear`] or [`Input::reset`] immediately after the state change.
///
/// ## Note
///
/// When adding this resource for a new input type, you should:
///
/// * Call the [`Input::press`] method for each press event.
/// * Call the [`Input::release`] method for each release event.
/// * Call the [`Input::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, Reflect)]
Derive `Reflect` + `FromReflect` for input types (#6232) # Objective Adds support for reflecting many more of the input types. This allows those types to be used via scripting, `bevy-inspector-egui`, etc. These types are registered by the `InputPlugin` so that they're automatically available to anyone who wants to use them Closes #6223 ## Solution Many types now have `#[derive(Reflect, FromReflect)]` added to them in `bevy_input`. Additionally, `#[reflect(traits...)]` has been added for applicable traits to the types. This PR does not add reflection support for types which have private fields. Notably, `Touch` and `Touches` don't implement `Reflect`/`FromReflect`. This adds the "glam" feature to the `bevy_reflect` dependency for package `bevy_input`. Since `bevy_input` transitively depends on `glam` already, all this brings in are the reflection `impl`s. ## Migration Guide - `Input<T>` now implements `Reflect` via `#[reflect]` instead of `#[reflect_value]`. This means it now exposes its private fields via the `Reflect` trait rather than being treated as a value type. For code that relies on the `Input<T>` struct being treated as a value type by reflection, it is still possible to wrap the `Input<T>` type with a wrapper struct and apply `#[reflect_value]` to it. - As a reminder, private fields exposed via reflection are not subject to any stability guarantees. --- ## Changelog Added - Implemented `Reflect` + `FromReflect` for many input-related types. These types are automatically registered when adding the `InputPlugin`.
2022-10-26 19:52:20 +00:00
#[reflect(Default)]
pub struct Input<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 Input<T> {
fn default() -> Self {
Self {
pressed: Default::default(),
just_pressed: Default::default(),
just_released: Default::default(),
}
}
}
impl<T> Input<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))
}
/// 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 just been pressed.
pub fn just_pressed(&self, input: T) -> bool {
self.just_pressed.contains(&input)
}
/// Returns `true` if any item in `inputs` has just been pressed.
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 [`Input::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 just been released.
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(|it| self.just_released(it))
}
/// Clears the `just_released` state of the `input` and returns `true` if the `input` has just been released.
///
/// Future calls to [`Input::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 [`Input::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 [`Input::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.
pub fn get_just_pressed(&self) -> impl ExactSizeIterator<Item = &T> {
self.just_pressed.iter()
}
/// An iterator visiting every just released input in arbitrary order.
pub fn get_just_released(&self) -> impl ExactSizeIterator<Item = &T> {
self.just_released.iter()
}
}
#[cfg(test)]
mod test {
use crate::Input;
/// Used for testing the functionality of [`Input`].
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
enum DummyInput {
Input1,
Input2,
}
#[test]
fn test_press() {
let mut input = Input::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 = Input::default();
assert!(!input.pressed(DummyInput::Input1));
input.press(DummyInput::Input1);
assert!(input.pressed(DummyInput::Input1));
}
#[test]
fn test_any_pressed() {
let mut input = Input::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_release() {
let mut input = Input::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 = Input::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 = Input::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 = Input::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 = Input::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 = Input::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 = Input::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 = Input::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 = Input::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 = Input::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 = Input::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 = Input::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 = Input::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 = Input::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 = Input::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 = Input::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));
}
}