use bevy::input::keyboard::{Key, KeyboardInput, NativeKey};
use bevy::input::{ButtonInput, ButtonState};
use bevy::prelude::{Entity, Events, Gamepad, KeyCode, Reflect, Res, ResMut, Vec2, Vec3, World};
use leafwing_input_manager_macros::serde_typetag;
use serde::{Deserialize, Serialize};
use crate as leafwing_input_manager;
use crate::clashing_inputs::BasicInputs;
use crate::input_processing::{
AxisProcessor, DualAxisProcessor, WithAxisProcessingPipelineExt,
WithDualAxisProcessingPipelineExt,
};
use crate::user_input::{ButtonlikeChord, TripleAxislike, UserInput};
use crate::InputControlKind;
use super::updating::{CentralInputStore, UpdatableInput};
use super::{Axislike, Buttonlike, DualAxislike};
impl UserInput for KeyCode {
#[inline]
fn kind(&self) -> InputControlKind {
InputControlKind::Button
}
#[inline]
fn decompose(&self) -> BasicInputs {
BasicInputs::Simple(Box::new(*self))
}
}
impl UpdatableInput for KeyCode {
type SourceData = ButtonInput<KeyCode>;
fn compute(
mut central_input_store: ResMut<CentralInputStore>,
source_data: Res<Self::SourceData>,
) {
for key in source_data.get_pressed() {
central_input_store.update_buttonlike(*key, true);
}
for key in source_data.get_just_released() {
central_input_store.update_buttonlike(*key, false);
}
}
}
#[serde_typetag]
impl Buttonlike for KeyCode {
#[must_use]
#[inline]
fn pressed(&self, input_store: &CentralInputStore, _gamepad: Gamepad) -> bool {
input_store.pressed(self)
}
fn press(&self, world: &mut World) {
let mut events = world.resource_mut::<Events<KeyboardInput>>();
events.send(KeyboardInput {
key_code: *self,
logical_key: Key::Unidentified(NativeKey::Unidentified),
state: ButtonState::Pressed,
window: Entity::PLACEHOLDER,
});
}
fn release(&self, world: &mut World) {
let mut events = world.resource_mut::<Events<KeyboardInput>>();
events.send(KeyboardInput {
key_code: *self,
logical_key: Key::Unidentified(NativeKey::Unidentified),
state: ButtonState::Released,
window: Entity::PLACEHOLDER,
});
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
#[must_use]
pub enum ModifierKey {
Alt,
Control,
Shift,
Super,
}
impl ModifierKey {
#[must_use]
#[inline]
pub const fn keycodes(&self) -> [KeyCode; 2] {
[self.left(), self.right()]
}
#[must_use]
#[inline]
pub const fn left(&self) -> KeyCode {
match self {
ModifierKey::Alt => KeyCode::AltLeft,
ModifierKey::Control => KeyCode::ControlLeft,
ModifierKey::Shift => KeyCode::ShiftLeft,
ModifierKey::Super => KeyCode::SuperLeft,
}
}
#[must_use]
#[inline]
pub const fn right(&self) -> KeyCode {
match self {
ModifierKey::Alt => KeyCode::AltRight,
ModifierKey::Control => KeyCode::ControlRight,
ModifierKey::Shift => KeyCode::ShiftRight,
ModifierKey::Super => KeyCode::SuperRight,
}
}
#[inline]
pub fn with(&self, other: impl Buttonlike) -> ButtonlikeChord {
ButtonlikeChord::from_single(*self).with(other)
}
}
impl UserInput for ModifierKey {
#[inline]
fn kind(&self) -> InputControlKind {
InputControlKind::Button
}
#[inline]
fn decompose(&self) -> BasicInputs {
BasicInputs::Composite(vec![Box::new(self.left()), Box::new(self.right())])
}
}
#[serde_typetag]
impl Buttonlike for ModifierKey {
#[must_use]
#[inline]
fn pressed(&self, input_store: &CentralInputStore, _gamepad: Gamepad) -> bool {
input_store.pressed(&self.left()) || input_store.pressed(&self.right())
}
fn press(&self, world: &mut World) {
self.left().press(world);
self.right().press(world);
}
fn release(&self, world: &mut World) {
self.left().release(world);
self.right().release(world);
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
#[must_use]
pub struct KeyboardVirtualAxis {
pub(crate) negative: KeyCode,
pub(crate) positive: KeyCode,
pub(crate) processors: Vec<AxisProcessor>,
}
impl KeyboardVirtualAxis {
#[inline]
pub fn new(negative: KeyCode, positive: KeyCode) -> Self {
Self {
negative,
positive,
processors: Vec::new(),
}
}
pub const VERTICAL_ARROW_KEYS: Self = Self {
negative: KeyCode::ArrowDown,
positive: KeyCode::ArrowUp,
processors: Vec::new(),
};
pub const HORIZONTAL_ARROW_KEYS: Self = Self {
negative: KeyCode::ArrowLeft,
positive: KeyCode::ArrowRight,
processors: Vec::new(),
};
pub const WS: Self = Self {
negative: KeyCode::KeyS,
positive: KeyCode::KeyW,
processors: Vec::new(),
};
pub const AD: Self = Self {
negative: KeyCode::KeyA,
positive: KeyCode::KeyD,
processors: Vec::new(),
};
pub const VERTICAL_NUMPAD: Self = Self {
negative: KeyCode::Numpad2,
positive: KeyCode::Numpad8,
processors: Vec::new(),
};
pub const HORIZONTAL_NUMPAD: Self = Self {
negative: KeyCode::Numpad4,
positive: KeyCode::Numpad6,
processors: Vec::new(),
};
}
impl UserInput for KeyboardVirtualAxis {
#[inline]
fn kind(&self) -> InputControlKind {
InputControlKind::Axis
}
#[inline]
fn decompose(&self) -> BasicInputs {
BasicInputs::Composite(vec![Box::new(self.negative), Box::new(self.negative)])
}
}
#[serde_typetag]
impl Axislike for KeyboardVirtualAxis {
#[must_use]
#[inline]
fn value(&self, input_store: &CentralInputStore, _gamepad: Gamepad) -> f32 {
let negative = f32::from(input_store.pressed(&self.negative));
let positive = f32::from(input_store.pressed(&self.positive));
let value = positive - negative;
self.processors
.iter()
.fold(value, |value, processor| processor.process(value))
}
fn set_value(&self, world: &mut World, value: f32) {
if value < 0.0 {
self.negative.press(world);
} else if value > 0.0 {
self.positive.press(world);
}
}
}
impl WithAxisProcessingPipelineExt for KeyboardVirtualAxis {
#[inline]
fn reset_processing_pipeline(mut self) -> Self {
self.processors.clear();
self
}
#[inline]
fn replace_processing_pipeline(
mut self,
processors: impl IntoIterator<Item = AxisProcessor>,
) -> Self {
self.processors = processors.into_iter().collect();
self
}
#[inline]
fn with_processor(mut self, processor: impl Into<AxisProcessor>) -> Self {
self.processors.push(processor.into());
self
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
#[must_use]
pub struct KeyboardVirtualDPad {
pub(crate) up: KeyCode,
pub(crate) down: KeyCode,
pub(crate) left: KeyCode,
pub(crate) right: KeyCode,
pub(crate) processors: Vec<DualAxisProcessor>,
}
impl KeyboardVirtualDPad {
#[inline]
pub fn new(up: KeyCode, down: KeyCode, left: KeyCode, right: KeyCode) -> Self {
Self {
up,
down,
left,
right,
processors: Vec::new(),
}
}
pub const ARROW_KEYS: Self = Self {
up: KeyCode::ArrowUp,
down: KeyCode::ArrowDown,
left: KeyCode::ArrowLeft,
right: KeyCode::ArrowRight,
processors: Vec::new(),
};
pub const WASD: Self = Self {
up: KeyCode::KeyW,
down: KeyCode::KeyS,
left: KeyCode::KeyA,
right: KeyCode::KeyD,
processors: Vec::new(),
};
pub const NUMPAD: Self = Self {
up: KeyCode::Numpad8,
down: KeyCode::Numpad2,
left: KeyCode::Numpad4,
right: KeyCode::Numpad6,
processors: Vec::new(),
};
}
impl UserInput for KeyboardVirtualDPad {
#[inline]
fn kind(&self) -> InputControlKind {
InputControlKind::DualAxis
}
#[inline]
fn decompose(&self) -> BasicInputs {
BasicInputs::Composite(vec![
Box::new(self.up),
Box::new(self.down),
Box::new(self.left),
Box::new(self.right),
])
}
}
#[serde_typetag]
impl DualAxislike for KeyboardVirtualDPad {
#[must_use]
#[inline]
fn axis_pair(&self, input_store: &CentralInputStore, _gamepad: Gamepad) -> Vec2 {
let up = f32::from(input_store.pressed(&self.up));
let down = f32::from(input_store.pressed(&self.down));
let left = f32::from(input_store.pressed(&self.left));
let right = f32::from(input_store.pressed(&self.right));
let value = Vec2::new(right - left, up - down);
self.processors
.iter()
.fold(value, |value, processor| processor.process(value))
}
fn set_axis_pair(&self, world: &mut World, value: Vec2) {
if value.x < 0.0 {
self.left.press(world);
} else if value.x > 0.0 {
self.right.press(world);
}
if value.y < 0.0 {
self.down.press(world);
} else if value.y > 0.0 {
self.up.press(world);
}
}
}
impl WithDualAxisProcessingPipelineExt for KeyboardVirtualDPad {
#[inline]
fn reset_processing_pipeline(mut self) -> Self {
self.processors.clear();
self
}
#[inline]
fn replace_processing_pipeline(
mut self,
processors: impl IntoIterator<Item = DualAxisProcessor>,
) -> Self {
self.processors = processors.into_iter().collect();
self
}
#[inline]
fn with_processor(mut self, processor: impl Into<DualAxisProcessor>) -> Self {
self.processors.push(processor.into());
self
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
#[must_use]
pub struct KeyboardVirtualDPad3D {
pub(crate) up: KeyCode,
pub(crate) down: KeyCode,
pub(crate) left: KeyCode,
pub(crate) right: KeyCode,
pub(crate) forward: KeyCode,
pub(crate) backward: KeyCode,
}
impl KeyboardVirtualDPad3D {
#[inline]
pub fn new(
up: KeyCode,
down: KeyCode,
left: KeyCode,
right: KeyCode,
forward: KeyCode,
backward: KeyCode,
) -> Self {
Self {
up,
down,
left,
right,
forward,
backward,
}
}
}
impl UserInput for KeyboardVirtualDPad3D {
#[inline]
fn kind(&self) -> InputControlKind {
InputControlKind::TripleAxis
}
#[inline]
fn decompose(&self) -> BasicInputs {
BasicInputs::Composite(vec![
Box::new(self.up),
Box::new(self.down),
Box::new(self.left),
Box::new(self.right),
Box::new(self.forward),
Box::new(self.backward),
])
}
}
#[serde_typetag]
impl TripleAxislike for KeyboardVirtualDPad3D {
#[must_use]
#[inline]
fn axis_triple(&self, input_store: &CentralInputStore, _gamepad: Gamepad) -> Vec3 {
let up = f32::from(input_store.pressed(&self.up));
let down = f32::from(input_store.pressed(&self.down));
let left = f32::from(input_store.pressed(&self.left));
let right = f32::from(input_store.pressed(&self.right));
let forward = f32::from(input_store.pressed(&self.left));
let back = f32::from(input_store.pressed(&self.right));
Vec3::new(right - left, up - down, back - forward)
}
fn set_axis_triple(&self, world: &mut World, value: Vec3) {
if value.x < 0.0 {
self.left.press(world);
} else if value.x > 0.0 {
self.right.press(world);
}
if value.y < 0.0 {
self.down.press(world);
} else if value.y > 0.0 {
self.up.press(world);
}
if value.z < 0.0 {
self.forward.press(world);
} else if value.z > 0.0 {
self.backward.press(world);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::plugin::{AccumulatorPlugin, CentralInputStorePlugin};
use bevy::input::InputPlugin;
use bevy::prelude::*;
fn test_app() -> App {
let mut app = App::new();
app.add_plugins(InputPlugin)
.add_plugins((AccumulatorPlugin, CentralInputStorePlugin));
app
}
#[test]
fn test_keyboard_input() {
let up = KeyCode::ArrowUp;
assert_eq!(up.kind(), InputControlKind::Button);
let left = KeyCode::ArrowLeft;
assert_eq!(left.kind(), InputControlKind::Button);
let alt = ModifierKey::Alt;
assert_eq!(alt.kind(), InputControlKind::Button);
let arrow_y = KeyboardVirtualAxis::VERTICAL_ARROW_KEYS;
assert_eq!(arrow_y.kind(), InputControlKind::Axis);
let arrows = KeyboardVirtualDPad::ARROW_KEYS;
assert_eq!(arrows.kind(), InputControlKind::DualAxis);
let zeros = Vec2::new(0.0, 0.0);
let mut app = test_app();
app.update();
let inputs = app.world().resource::<CentralInputStore>();
let gamepad = Gamepad::new(0);
assert!(!up.pressed(inputs, gamepad));
assert!(!left.pressed(inputs, gamepad));
assert!(!alt.pressed(inputs, gamepad));
assert_eq!(arrow_y.value(inputs, gamepad), 0.0);
assert_eq!(arrows.axis_pair(inputs, gamepad), zeros);
let data = Vec2::new(0.0, 1.0);
let mut app = test_app();
KeyCode::ArrowUp.press(app.world_mut());
app.update();
let inputs = app.world().resource::<CentralInputStore>();
assert!(up.pressed(inputs, gamepad));
assert!(!left.pressed(inputs, gamepad));
assert!(!alt.pressed(inputs, gamepad));
assert_eq!(arrow_y.value(inputs, gamepad), data.y);
assert_eq!(arrows.axis_pair(inputs, gamepad), data);
let data = Vec2::new(0.0, -1.0);
let mut app = test_app();
KeyCode::ArrowDown.press(app.world_mut());
app.update();
let inputs = app.world().resource::<CentralInputStore>();
assert!(!up.pressed(inputs, gamepad));
assert!(!left.pressed(inputs, gamepad));
assert!(!alt.pressed(inputs, gamepad));
assert_eq!(arrow_y.value(inputs, gamepad), data.y);
assert_eq!(arrows.axis_pair(inputs, gamepad), data);
let data = Vec2::new(-1.0, 0.0);
let mut app = test_app();
KeyCode::ArrowLeft.press(app.world_mut());
app.update();
let inputs = app.world().resource::<CentralInputStore>();
assert!(!up.pressed(inputs, gamepad));
assert!(left.pressed(inputs, gamepad));
assert!(!alt.pressed(inputs, gamepad));
assert_eq!(arrow_y.value(inputs, gamepad), 0.0);
assert_eq!(arrows.axis_pair(inputs, gamepad), data);
let mut app = test_app();
KeyCode::ArrowDown.press(app.world_mut());
KeyCode::ArrowUp.press(app.world_mut());
app.update();
let inputs = app.world().resource::<CentralInputStore>();
assert!(up.pressed(inputs, gamepad));
assert!(!left.pressed(inputs, gamepad));
assert!(!alt.pressed(inputs, gamepad));
assert_eq!(arrow_y.value(inputs, gamepad), 0.0);
assert_eq!(arrows.axis_pair(inputs, gamepad), zeros);
let data = Vec2::new(-1.0, 1.0);
let mut app = test_app();
KeyCode::ArrowLeft.press(app.world_mut());
KeyCode::ArrowUp.press(app.world_mut());
app.update();
let inputs = app.world().resource::<CentralInputStore>();
assert!(up.pressed(inputs, gamepad));
assert!(left.pressed(inputs, gamepad));
assert!(!alt.pressed(inputs, gamepad));
assert_eq!(arrow_y.value(inputs, gamepad), data.y);
assert_eq!(arrows.axis_pair(inputs, gamepad), data);
let mut app = test_app();
KeyCode::AltLeft.press(app.world_mut());
app.update();
let inputs = app.world().resource::<CentralInputStore>();
assert!(!up.pressed(inputs, gamepad));
assert!(!left.pressed(inputs, gamepad));
assert!(alt.pressed(inputs, gamepad));
assert_eq!(arrow_y.value(inputs, gamepad), 0.0);
assert_eq!(arrows.axis_pair(inputs, gamepad), zeros);
let mut app = test_app();
KeyCode::AltRight.press(app.world_mut());
app.update();
let inputs = app.world().resource::<CentralInputStore>();
assert!(!up.pressed(inputs, gamepad));
assert!(!left.pressed(inputs, gamepad));
assert!(alt.pressed(inputs, gamepad));
assert_eq!(arrow_y.value(inputs, gamepad), 0.0);
assert_eq!(arrows.axis_pair(inputs, gamepad), zeros);
}
}