use bevy::ecs::message::Messages;
use bevy::ecs::system::StaticSystemParam;
use bevy::ecs::system::lifetimeless::SRes;
use bevy::input::keyboard::{Key, KeyboardInput, NativeKey};
use bevy::input::{ButtonInput, ButtonState};
use bevy::prelude::{Entity, KeyCode, Reflect, ResMut, World};
use leafwing_input_manager_macros::serde_typetag;
use serde::{Deserialize, Serialize};
use crate as leafwing_input_manager;
use crate::InputControlKind;
use crate::buttonlike::ButtonValue;
use crate::clashing_inputs::BasicInputs;
use crate::user_input::{ButtonlikeChord, UserInput};
use super::Buttonlike;
use super::updating::{CentralInputStore, UpdatableInput};
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 = SRes<ButtonInput<KeyCode>>;
fn compute(
mut central_input_store: ResMut<CentralInputStore>,
source_data: StaticSystemParam<Self::SourceData>,
) {
for key in source_data.get_pressed() {
central_input_store.update_buttonlike(*key, ButtonValue::from_pressed(true));
}
for key in source_data.get_just_released() {
central_input_store.update_buttonlike(*key, ButtonValue::from_pressed(false));
}
}
}
#[serde_typetag]
impl Buttonlike for KeyCode {
#[inline]
fn get_pressed(&self, input_store: &CentralInputStore, _gamepad: Entity) -> Option<bool> {
input_store.pressed(self)
}
fn press(&self, world: &mut World) {
let mut messages = world.resource_mut::<Messages<KeyboardInput>>();
messages.write(KeyboardInput {
key_code: *self,
logical_key: Key::Unidentified(NativeKey::Unidentified),
state: ButtonState::Pressed,
repeat: false,
window: Entity::PLACEHOLDER,
text: None,
});
}
fn release(&self, world: &mut World) {
let mut messages = world.resource_mut::<Messages<KeyboardInput>>();
messages.write(KeyboardInput {
key_code: *self,
logical_key: Key::Unidentified(NativeKey::Unidentified),
state: ButtonState::Released,
repeat: false,
window: Entity::PLACEHOLDER,
text: None,
});
}
fn set_value(&self, world: &mut World, value: f32) {
if value > 0.0 {
self.press(world);
} else {
self.release(world);
}
}
}
#[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 {
#[inline]
fn get_pressed(&self, input_store: &CentralInputStore, _gamepad: Entity) -> Option<bool> {
let left = input_store.pressed(&self.left());
let right = input_store.pressed(&self.right());
if (None, None) == (left, right) {
None
} else {
Some(left.unwrap_or(false) || right.unwrap_or(false))
}
}
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);
}
fn set_value(&self, world: &mut World, value: f32) {
if value > 0.0 {
self.press(world);
} else {
self.release(world);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::plugin::CentralInputStorePlugin;
use bevy::input::InputPlugin;
use bevy::prelude::*;
fn test_app() -> App {
let mut app = App::new();
app.add_plugins(InputPlugin)
.add_plugins(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 mut app = test_app();
app.update();
let gamepad = app.world_mut().spawn(()).id();
let inputs = app.world().resource::<CentralInputStore>();
assert!(!up.pressed(inputs, gamepad));
assert!(!left.pressed(inputs, gamepad));
assert!(!alt.pressed(inputs, gamepad));
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));
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));
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));
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));
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));
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));
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));
}
}