use crate::axislike::{AxisType, MouseMotionAxisType, MouseWheelAxisType};
use crate::buttonlike::{MouseMotionDirection, MouseWheelDirection};
use crate::input_streams::{InputStreams, MutableInputStreams};
use crate::user_input::UserInput;
use bevy::app::App;
use bevy::ecs::event::Events;
use bevy::ecs::system::{ResMut, SystemState};
use bevy::ecs::world::World;
#[cfg(feature = "ui")]
use bevy::ecs::{component::Component, query::With, system::Query};
use bevy::input::gamepad::GamepadEventRaw;
use bevy::input::mouse::MouseScrollUnit;
use bevy::input::ButtonState;
use bevy::input::{
gamepad::{Gamepad, GamepadButton, GamepadEvent, GamepadEventType},
keyboard::{KeyCode, KeyboardInput},
mouse::{MouseButton, MouseButtonInput, MouseMotion, MouseWheel},
touch::{TouchInput, Touches},
Input,
};
use bevy::math::Vec2;
#[cfg(feature = "ui")]
use bevy::ui::Interaction;
use bevy::window::CursorMoved;
pub trait MockInput {
fn send_input(&mut self, input: impl Into<UserInput>);
fn send_input_as_gamepad(&mut self, input: impl Into<UserInput>, gamepad: Option<Gamepad>);
fn release_input(&mut self, input: impl Into<UserInput>);
fn release_input_as_gamepad(&mut self, input: impl Into<UserInput>, gamepad: Option<Gamepad>);
fn pressed(&self, input: impl Into<UserInput>) -> bool;
fn pressed_for_gamepad(&self, input: impl Into<UserInput>, gamepad: Option<Gamepad>) -> bool;
fn reset_inputs(&mut self);
#[cfg(feature = "ui")]
fn click_button<Marker: Component>(&mut self);
#[cfg(feature = "ui")]
fn hover_button<Marker: Component>(&mut self);
}
impl MockInput for MutableInputStreams<'_> {
fn send_input(&mut self, input: impl Into<UserInput>) {
self.send_input_as_gamepad(input, self.guess_gamepad());
}
fn send_input_as_gamepad(&mut self, input: impl Into<UserInput>, gamepad: Option<Gamepad>) {
let input_to_send: UserInput = input.into();
let raw_inputs = input_to_send.raw_inputs();
for button in raw_inputs.keycodes {
self.keyboard_events.send(KeyboardInput {
scan_code: u32::MAX,
key_code: Some(button),
state: ButtonState::Pressed,
});
}
for button in raw_inputs.mouse_buttons {
self.mouse_button_events.send(MouseButtonInput {
button,
state: ButtonState::Pressed,
});
}
for mouse_wheel_direction in raw_inputs.mouse_wheel {
match mouse_wheel_direction {
MouseWheelDirection::Left => self.mouse_wheel.send(MouseWheel {
unit: MouseScrollUnit::Pixel,
x: -1.0,
y: 0.0,
}),
MouseWheelDirection::Right => self.mouse_wheel.send(MouseWheel {
unit: MouseScrollUnit::Pixel,
x: 1.0,
y: 0.0,
}),
MouseWheelDirection::Up => self.mouse_wheel.send(MouseWheel {
unit: MouseScrollUnit::Pixel,
x: 0.0,
y: 1.0,
}),
MouseWheelDirection::Down => self.mouse_wheel.send(MouseWheel {
unit: MouseScrollUnit::Pixel,
x: 0.0,
y: -1.0,
}),
}
}
for mouse_motion_direction in raw_inputs.mouse_motion {
match mouse_motion_direction {
MouseMotionDirection::Up => self.mouse_motion.send(MouseMotion {
delta: Vec2 { x: 0.0, y: 1.0 },
}),
MouseMotionDirection::Down => self.mouse_motion.send(MouseMotion {
delta: Vec2 { x: 0.0, y: -1.0 },
}),
MouseMotionDirection::Right => self.mouse_motion.send(MouseMotion {
delta: Vec2 { x: 1.0, y: 0.0 },
}),
MouseMotionDirection::Left => self.mouse_motion.send(MouseMotion {
delta: Vec2 { x: -1.0, y: 0.0 },
}),
}
}
for button_type in raw_inputs.gamepad_buttons {
if let Some(gamepad) = gamepad {
self.gamepad_events.send(GamepadEventRaw {
gamepad,
event_type: GamepadEventType::ButtonChanged(button_type, 1.0),
});
}
}
for (outer_axis_type, maybe_position_data) in raw_inputs.axis_data {
if let Some(position_data) = maybe_position_data {
match outer_axis_type {
AxisType::Gamepad(axis_type) => {
if let Some(gamepad) = gamepad {
self.gamepad_events.send(GamepadEventRaw {
gamepad,
event_type: GamepadEventType::AxisChanged(axis_type, position_data),
});
}
}
AxisType::MouseWheel(axis_type) => {
match axis_type {
MouseWheelAxisType::X => self.mouse_wheel.send(MouseWheel {
unit: MouseScrollUnit::Pixel,
x: position_data,
y: 0.0,
}),
MouseWheelAxisType::Y => self.mouse_wheel.send(MouseWheel {
unit: MouseScrollUnit::Pixel,
x: 0.0,
y: position_data,
}),
}
}
AxisType::MouseMotion(axis_type) => match axis_type {
MouseMotionAxisType::X => self.mouse_motion.send(MouseMotion {
delta: Vec2 {
x: position_data,
y: 0.0,
},
}),
MouseMotionAxisType::Y => self.mouse_motion.send(MouseMotion {
delta: Vec2 {
x: 0.0,
y: position_data,
},
}),
},
}
}
}
}
fn release_input(&mut self, input: impl Into<UserInput>) {
self.release_input_as_gamepad(input, self.guess_gamepad())
}
fn release_input_as_gamepad(&mut self, input: impl Into<UserInput>, gamepad: Option<Gamepad>) {
let input_to_release: UserInput = input.into();
let raw_inputs = input_to_release.raw_inputs();
for button_type in raw_inputs.gamepad_buttons {
if let Some(gamepad) = gamepad {
self.gamepad_events.send(GamepadEventRaw {
gamepad,
event_type: GamepadEventType::ButtonChanged(button_type, 1.0),
});
}
}
for button in raw_inputs.keycodes {
self.keyboard_events.send(KeyboardInput {
scan_code: u32::MAX,
key_code: Some(button),
state: ButtonState::Released,
});
}
for button in raw_inputs.mouse_buttons {
self.mouse_button_events.send(MouseButtonInput {
button,
state: ButtonState::Released,
});
}
}
fn pressed(&self, input: impl Into<UserInput>) -> bool {
let input_streams: InputStreams = self.into();
input_streams.input_pressed(&input.into())
}
fn pressed_for_gamepad(&self, input: impl Into<UserInput>, gamepad: Option<Gamepad>) -> bool {
let mut input_streams: InputStreams = self.into();
input_streams.associated_gamepad = gamepad;
input_streams.input_pressed(&input.into())
}
fn reset_inputs(&mut self) {
*self.gamepad_buttons = Default::default();
*self.gamepad_axes = Default::default();
*self.keycodes = Default::default();
*self.mouse_buttons = Default::default();
*self.mouse_wheel = Default::default();
*self.mouse_motion = Default::default();
}
#[cfg(feature = "ui")]
fn click_button<Marker: Component>(&mut self) {
panic!("Cannot use bevy_ui input mocking from `MutableInputStreams`, use an `App` or `World` instead.")
}
#[cfg(feature = "ui")]
fn hover_button<Marker: Component>(&mut self) {
panic!("Cannot use bevy_ui input mocking from `MutableInputStreams`, use an `App` or `World` instead.")
}
}
impl MockInput for World {
fn send_input(&mut self, input: impl Into<UserInput>) {
let mut mutable_input_streams = MutableInputStreams::from_world(self, None);
mutable_input_streams.send_input(input);
}
fn send_input_as_gamepad(&mut self, input: impl Into<UserInput>, gamepad: Option<Gamepad>) {
let mut mutable_input_streams = MutableInputStreams::from_world(self, gamepad);
mutable_input_streams.send_input_as_gamepad(input, gamepad);
}
fn release_input(&mut self, input: impl Into<UserInput>) {
let mut mutable_input_streams = MutableInputStreams::from_world(self, None);
mutable_input_streams.release_input(input);
}
fn release_input_as_gamepad(&mut self, input: impl Into<UserInput>, gamepad: Option<Gamepad>) {
let mut mutable_input_streams = MutableInputStreams::from_world(self, gamepad);
mutable_input_streams.release_input_as_gamepad(input, gamepad);
}
fn pressed(&self, input: impl Into<UserInput>) -> bool {
self.pressed_for_gamepad(input, None)
}
fn pressed_for_gamepad(&self, input: impl Into<UserInput>, gamepad: Option<Gamepad>) -> bool {
let input_streams = InputStreams::from_world(self, gamepad);
input_streams.input_pressed(&input.into())
}
fn reset_inputs(&mut self) {
#[cfg(feature = "ui")]
{
let mut interraction_system_state: SystemState<Query<&mut Interaction>> =
SystemState::new(self);
let mut interaction_query = interraction_system_state.get_mut(self);
for mut interaction in interaction_query.iter_mut() {
*interaction = Interaction::None;
}
}
let mut input_system_state: SystemState<(
Option<ResMut<Input<GamepadButton>>>,
Option<ResMut<Input<KeyCode>>>,
Option<ResMut<Input<MouseButton>>>,
)> = SystemState::new(self);
let (maybe_gamepad, maybe_keyboard, maybe_mouse) = input_system_state.get_mut(self);
if let Some(mut gamepad) = maybe_gamepad {
*gamepad = Default::default();
}
if let Some(mut keyboard) = maybe_keyboard {
*keyboard = Default::default();
}
if let Some(mut mouse) = maybe_mouse {
*mouse = Default::default();
}
self.insert_resource(Events::<GamepadEvent>::default());
self.insert_resource(Events::<KeyboardInput>::default());
self.insert_resource(Events::<MouseButtonInput>::default());
self.insert_resource(Events::<MouseWheel>::default());
self.insert_resource(Events::<CursorMoved>::default());
self.insert_resource(Touches::default());
self.insert_resource(Events::<TouchInput>::default());
}
#[cfg(feature = "ui")]
fn click_button<Marker: Component>(&mut self) {
let mut button_query = self.query_filtered::<&mut Interaction, With<Marker>>();
for mut interaction in button_query.iter_mut(self) {
*interaction = Interaction::Clicked;
}
}
#[cfg(feature = "ui")]
fn hover_button<Marker: Component>(&mut self) {
let mut button_query = self.query_filtered::<&mut Interaction, With<Marker>>();
for mut interaction in button_query.iter_mut(self) {
*interaction = Interaction::Hovered;
}
}
}
impl MockInput for App {
fn send_input(&mut self, input: impl Into<UserInput>) {
self.world.send_input(input);
}
fn send_input_as_gamepad(&mut self, input: impl Into<UserInput>, gamepad: Option<Gamepad>) {
self.world.send_input_as_gamepad(input, gamepad);
}
fn release_input(&mut self, input: impl Into<UserInput>) {
self.world.release_input(input);
}
fn release_input_as_gamepad(&mut self, input: impl Into<UserInput>, gamepad: Option<Gamepad>) {
self.world.release_input_as_gamepad(input, gamepad);
}
fn pressed(&self, input: impl Into<UserInput>) -> bool {
self.world.pressed(input)
}
fn pressed_for_gamepad(&self, input: impl Into<UserInput>, gamepad: Option<Gamepad>) -> bool {
self.world.pressed_for_gamepad(input, gamepad)
}
fn reset_inputs(&mut self) {
self.world.reset_inputs();
}
#[cfg(feature = "ui")]
fn click_button<Marker: Component>(&mut self) {
self.world.click_button::<Marker>();
}
#[cfg(feature = "ui")]
fn hover_button<Marker: Component>(&mut self) {
self.world.hover_button::<Marker>();
}
}
#[cfg(test)]
mod test {
use crate::input_mocking::MockInput;
use bevy::{
input::{gamepad::GamepadInfo, InputPlugin},
prelude::*,
};
#[test]
fn ordinary_button_inputs() {
let mut app = App::new();
app.add_plugin(InputPlugin);
assert!(!app.pressed(KeyCode::Space));
assert!(!app.pressed(MouseButton::Right));
app.send_input(KeyCode::Space);
app.send_input(MouseButton::Right);
app.update();
let keyboard_input: &Input<KeyCode> = app.world.resource();
assert!(keyboard_input.pressed(KeyCode::Space));
assert!(app.pressed(KeyCode::Space));
assert!(app.pressed(MouseButton::Right));
app.reset_inputs();
app.update();
assert!(!app.pressed(KeyCode::Space));
assert!(!app.pressed(MouseButton::Right));
}
#[test]
fn explicit_gamepad_button_inputs() {
let mut app = App::new();
app.add_plugin(InputPlugin);
let gamepad = Gamepad { id: 0 };
let mut gamepad_events = app.world.resource_mut::<Events<GamepadEvent>>();
gamepad_events.send(GamepadEvent {
gamepad,
event_type: GamepadEventType::Connected(GamepadInfo {
name: "TestController".into(),
}),
});
app.update();
assert!(!app.pressed_for_gamepad(GamepadButtonType::North, Some(gamepad)));
app.send_input_as_gamepad(GamepadButtonType::North, Some(gamepad));
app.update();
let gamepad_input = app.world.resource::<Input<GamepadButton>>();
assert!(gamepad_input.pressed(GamepadButton {
gamepad,
button_type: GamepadButtonType::North,
}));
assert!(app.pressed_for_gamepad(GamepadButtonType::North, Some(gamepad)));
app.reset_inputs();
app.update();
assert!(!app.pressed_for_gamepad(GamepadButtonType::North, Some(gamepad)));
}
#[test]
fn implicit_gamepad_button_inputs() {
let mut app = App::new();
app.add_plugin(InputPlugin);
let gamepad = Gamepad { id: 0 };
let mut gamepad_events = app.world.resource_mut::<Events<GamepadEvent>>();
gamepad_events.send(GamepadEvent {
gamepad,
event_type: GamepadEventType::Connected(GamepadInfo {
name: "TestController".into(),
}),
});
app.update();
assert!(!app.pressed(GamepadButtonType::North));
app.send_input(GamepadButtonType::North);
app.update();
assert!(app.pressed(GamepadButtonType::North));
app.reset_inputs();
app.update();
assert!(!app.pressed(GamepadButtonType::North));
}
#[test]
#[cfg(feature = "ui")]
fn ui_inputs() {
use bevy::ecs::prelude::*;
use bevy::ui::Interaction;
#[derive(Component)]
struct ButtonMarker;
let mut app = App::new();
app.add_plugin(InputPlugin);
app.world.spawn((Interaction::None, ButtonMarker));
app.world.spawn(Interaction::None);
app.world.click_button::<ButtonMarker>();
app.update();
let mut interaction_query = app.world.query::<(&Interaction, Option<&ButtonMarker>)>();
for (interaction, maybe_marker) in interaction_query.iter(&app.world) {
match maybe_marker {
Some(_) => assert_eq!(*interaction, Interaction::Clicked),
None => assert_eq!(*interaction, Interaction::None),
}
}
app.world.reset_inputs();
let mut interaction_query = app.world.query::<&Interaction>();
for interaction in interaction_query.iter(&app.world) {
assert_eq!(*interaction, Interaction::None)
}
app.hover_button::<ButtonMarker>();
app.update();
let mut interaction_query = app.world.query::<(&Interaction, Option<&ButtonMarker>)>();
for (interaction, maybe_marker) in interaction_query.iter(&app.world) {
match maybe_marker {
Some(_) => assert_eq!(*interaction, Interaction::Hovered),
None => assert_eq!(*interaction, Interaction::None),
}
}
app.world.reset_inputs();
let mut interaction_query = app.world.query::<&Interaction>();
for interaction in interaction_query.iter(&app.world) {
assert_eq!(*interaction, Interaction::None)
}
}
}