use std::ops::BitOr;
use bevy::prelude::*;
use crate::model::{is_modifier_key, shortcut_label};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Reflect)]
#[reflect(Debug, PartialEq, Hash, Clone)]
pub enum Platform {
Mac,
Other,
}
impl Platform {
pub const fn current() -> Self {
if cfg!(target_os = "macos") {
Self::Mac
} else {
Self::Other
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Reflect)]
#[reflect(Debug, Default, PartialEq, Hash, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Modifiers {
pub ctrl: bool,
pub shift: bool,
pub alt: bool,
pub meta: bool,
}
impl Modifiers {
pub const NONE: Self = Self::of(false, false, false, false);
pub const CTRL: Self = Self::of(true, false, false, false);
pub const SHIFT: Self = Self::of(false, true, false, false);
pub const ALT: Self = Self::of(false, false, true, false);
pub const META: Self = Self::of(false, false, false, true);
pub const PRIMARY: Self = Self::primary_on(Platform::current());
const fn of(ctrl: bool, shift: bool, alt: bool, meta: bool) -> Self {
Self {
ctrl,
shift,
alt,
meta,
}
}
pub const fn primary_on(platform: Platform) -> Self {
match platform {
Platform::Mac => Self::META,
Platform::Other => Self::CTRL,
}
}
pub fn held(keys: &ButtonInput<KeyCode>) -> Self {
let either = |left, right| keys.pressed(left) || keys.pressed(right);
Self {
ctrl: either(KeyCode::ControlLeft, KeyCode::ControlRight),
shift: either(KeyCode::ShiftLeft, KeyCode::ShiftRight),
alt: either(KeyCode::AltLeft, KeyCode::AltRight),
meta: either(KeyCode::SuperLeft, KeyCode::SuperRight),
}
}
pub const fn is_empty(self) -> bool {
!(self.ctrl || self.shift || self.alt || self.meta)
}
}
impl BitOr for Modifiers {
type Output = Self;
fn bitor(self, other: Self) -> Self {
Self::of(
self.ctrl | other.ctrl,
self.shift | other.shift,
self.alt | other.alt,
self.meta | other.meta,
)
}
}
#[derive(Component, Clone, Copy, Debug, PartialEq, Eq, Hash, Reflect)]
#[reflect(Component, Debug, PartialEq, Hash, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Shortcut {
pub modifiers: Modifiers,
pub key: KeyCode,
}
impl Shortcut {
pub const fn new(modifiers: Modifiers, key: KeyCode) -> Self {
Self { modifiers, key }
}
pub const fn primary(key: KeyCode) -> Self {
Self::new(Modifiers::PRIMARY, key)
}
pub fn is_valid(&self) -> bool {
!is_modifier_key(&format!("{:?}", self.key))
}
pub fn label(&self) -> String {
self.label_on(Platform::current())
}
pub fn label_on(&self, platform: Platform) -> String {
let Modifiers {
ctrl,
shift,
alt,
meta,
} = self.modifiers;
shortcut_label(
&format!("{:?}", self.key),
[ctrl, shift, alt, meta],
platform == Platform::Mac,
)
}
pub fn just_pressed(&self, keys: &ButtonInput<KeyCode>) -> bool {
keys.just_pressed(self.key) && Modifiers::held(keys) == self.modifiers
}
}