use core::fmt::{self, Display, Formatter};
use bevy::prelude::*;
use bitflags::bitflags;
#[cfg(feature = "serialize")]
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(
feature = "reflect",
derive(Reflect),
reflect(Clone, Debug, Default, PartialEq)
)]
#[cfg_attr(
all(feature = "reflect", feature = "serialize"),
reflect(Serialize, Deserialize)
)]
pub struct ModKeys(u8);
bitflags! {
impl ModKeys: u8 {
const CONTROL = 0b00000001;
const SHIFT = 0b00000010;
const ALT = 0b00000100;
const SUPER = 0b00001000;
}
}
#[cfg(feature = "serialize")]
impl Serialize for ModKeys {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
bitflags::serde::serialize(self, serializer)
}
}
#[cfg(feature = "serialize")]
impl<'de> Deserialize<'de> for ModKeys {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
bitflags::serde::deserialize(deserializer)
}
}
impl Display for ModKeys {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
for (index, (_, mod_key)) in self.iter_names().enumerate() {
if index != 0 {
write!(f, " + ")?;
}
match mod_key {
ModKeys::CONTROL => write!(f, "Ctrl")?,
ModKeys::SHIFT => write!(f, "Shift")?,
ModKeys::ALT => write!(f, "Alt")?,
ModKeys::SUPER => write!(f, "Super")?,
_ => unreachable!("iteration should yield only named flags"),
}
}
Ok(())
}
}
impl ModKeys {
#[must_use]
pub fn pressed(keys: &ButtonInput<KeyCode>) -> Self {
let mut mod_keys = Self::empty();
for [key1, key2] in Self::all().iter_keys() {
if keys.any_pressed([key1, key2]) {
mod_keys |= key1.into();
}
}
mod_keys
}
pub fn iter_keys(self) -> impl Iterator<Item = [KeyCode; 2]> {
self.iter_names().map(|(_, mod_key)| match mod_key {
ModKeys::CONTROL => [KeyCode::ControlLeft, KeyCode::ControlRight],
ModKeys::SHIFT => [KeyCode::ShiftLeft, KeyCode::ShiftRight],
ModKeys::ALT => [KeyCode::AltLeft, KeyCode::AltRight],
ModKeys::SUPER => [KeyCode::SuperLeft, KeyCode::SuperRight],
_ => unreachable!("iteration should yield only named flags"),
})
}
}
impl From<KeyCode> for ModKeys {
fn from(value: KeyCode) -> Self {
match value {
KeyCode::ControlLeft | KeyCode::ControlRight => ModKeys::CONTROL,
KeyCode::ShiftLeft | KeyCode::ShiftRight => ModKeys::SHIFT,
KeyCode::AltLeft | KeyCode::AltRight => ModKeys::ALT,
KeyCode::SuperLeft | KeyCode::SuperRight => ModKeys::SUPER,
_ => ModKeys::empty(),
}
}
}
#[cfg(test)]
mod tests {
use alloc::string::ToString;
use super::*;
#[test]
fn pressed_mod_keys() {
let mut keys = ButtonInput::default();
keys.press(KeyCode::ControlLeft);
keys.press(KeyCode::ShiftLeft);
keys.press(KeyCode::KeyC);
let mod_keys = ModKeys::pressed(&keys);
assert_eq!(mod_keys, ModKeys::CONTROL | ModKeys::SHIFT);
}
#[test]
fn mod_keys_display() {
assert_eq!(ModKeys::CONTROL.to_string(), "Ctrl");
assert_eq!(ModKeys::all().to_string(), "Ctrl + Shift + Alt + Super");
assert_eq!(ModKeys::empty().to_string(), "");
}
#[cfg(feature = "serialize")]
#[test]
fn mod_keys_serde() {
assert_eq!(ron::to_string(&ModKeys::CONTROL).unwrap(), "\"CONTROL\"");
assert_eq!(
ron::to_string(&(ModKeys::CONTROL | ModKeys::SHIFT)).unwrap(),
"\"CONTROL | SHIFT\""
);
assert_eq!(ron::to_string(&ModKeys::empty()).unwrap(), "\"\"");
let parsed: ModKeys = ron::from_str("\"CONTROL | SHIFT\"").unwrap();
assert_eq!(parsed, ModKeys::CONTROL | ModKeys::SHIFT);
}
}