use crate::platform::keycodes::KeyCode;
#[cfg_attr(feature = "bitcode", derive(bitcode::Encode, bitcode::Decode))]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "c_compatible", repr(C))]
pub struct KeyBind<T> {
pub keys: Vec<KeyCode>,
pub requires_shift: bool,
pub requires_alt: bool,
pub requires_control: bool,
pub action: T,
}
impl<T> KeyBind<T> {
#[allow(missing_docs)]
#[must_use]
pub const fn new(
requires_shift: bool,
requires_alt: bool,
requires_control: bool,
keys: Vec<KeyCode>,
action: T,
) -> Self {
Self {
keys,
requires_shift,
requires_alt,
requires_control,
action,
}
}
#[must_use]
pub fn is_keybind_activated(
&self,
newly_pressed: &[KeyCode],
shift_pressed: bool,
alt_pressed: bool,
control_pressed: bool,
) -> bool {
(self.keys.is_empty()
|| self.keys.iter().all(|k| newly_pressed.contains(k)))
&& (shift_pressed || !self.requires_shift)
&& (alt_pressed || !self.requires_alt)
&& (control_pressed || !self.requires_control)
}
#[must_use]
pub const fn get_priority(&self) -> usize {
self.requires_shift as usize
+ self.requires_alt as usize
+ self.requires_control as usize
+ !self.keys.is_empty() as usize
}
#[must_use]
pub fn does_other_keybind_eat_this_one(&self, other: &Self) -> bool {
(other.requires_control || !self.requires_control)
&& (other.requires_shift || !self.requires_shift)
&& (other.requires_alt || !self.requires_alt)
&& self.keys.iter().all(|x| other.keys.contains(x))
}
pub fn remove_required_keys(&self, list: &mut Vec<KeyCode>) {
list.retain(|x| !self.keys.contains(x));
}
}
#[must_use]
pub fn sort_actions<T: Clone>(actions: Vec<KeyBind<T>>) -> Vec<KeyBind<T>> {
let mut actions = actions;
actions.sort_by_key(KeyBind::get_priority);
actions.reverse();
let mut new_actions: Vec<KeyBind<T>> = Vec::new();
for i in actions.iter().rev() {
if !actions.iter().any(|y| {
!core::intrinsics::unlikely(core::ptr::eq(i, y)) && i.does_other_keybind_eat_this_one(y)
}) {
new_actions.push((*i).clone());
}
}
new_actions
}
#[must_use]
#[allow(clippy::ptr_arg)]
pub fn handle_keycodes<T: Clone>(
keybinds: &Vec<KeyBind<T>>,
newly_pressed: &[KeyCode],
held: &[KeyCode],
) -> (Vec<KeyBind<T>>, Vec<KeyCode>) {
let mut newly_pressed = newly_pressed.to_vec();
let control_down = held.contains(&KeyCode::LeftControl)
|| held.contains(&KeyCode::RightControl);
let alt_down =
held.contains(&KeyCode::LeftAlt) || held.contains(&KeyCode::RightAlt);
let shift_down = held.contains(&KeyCode::LeftShift)
|| held.contains(&KeyCode::RightShift);
let mut active_keybinds = Vec::new();
for keybind in keybinds.clone() {
let active = keybind.is_keybind_activated(
&newly_pressed,
shift_down,
alt_down,
control_down,
);
if active {
active_keybinds.push(keybind);
}
}
let new_actions = sort_actions(active_keybinds);
for i in &new_actions {
i.remove_required_keys(&mut newly_pressed);
}
(new_actions, newly_pressed)
}