use std::time::Duration;
use crate::{
axis::{Dir4, Dir8, Sign},
Input, Key,
};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(untagged))]
pub enum KeyRepeatConfig {
Repeat { first: Duration, multi: Duration },
NoRepeat,
}
impl Default for KeyRepeatConfig {
fn default() -> Self {
Self::NoRepeat
}
}
impl KeyRepeatConfig {
pub fn repeat(first: Duration, multi: Duration) -> Self {
KeyRepeatConfig::Repeat { first, multi }
}
pub fn no_repeat() -> Self {
KeyRepeatConfig::NoRepeat
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum RawButtonState {
Down,
Up,
Pressed,
Released,
}
#[derive(Debug, Clone, Default)]
struct KeyRepeatState {
config: KeyRepeatConfig,
accum_repeat: Duration,
accum_down: Duration,
is_on_first_repeat: bool,
}
impl KeyRepeatState {
pub fn new(repeat: KeyRepeatConfig) -> Self {
Self {
config: repeat,
accum_repeat: Duration::new(0, 0),
accum_down: Duration::new(0, 0),
is_on_first_repeat: false,
}
}
}
impl KeyRepeatState {
fn update(&mut self, state: RawButtonState, dt: Duration) -> bool {
match state {
RawButtonState::Up | RawButtonState::Released => {
self.accum_repeat = Duration::new(0, 0);
self.accum_down = Duration::new(0, 0);
self.is_on_first_repeat = false;
false
}
RawButtonState::Pressed => {
self.accum_repeat = Duration::new(0, 0);
self.accum_down = Duration::new(0, 0);
self.is_on_first_repeat = true;
false
}
RawButtonState::Down => {
let repeat_duration = match self.config {
KeyRepeatConfig::NoRepeat => return false,
KeyRepeatConfig::Repeat { first, multi } => {
if self.is_on_first_repeat {
first
} else {
multi
}
}
};
self.accum_repeat += dt;
self.accum_down += dt;
let mut is_repeating = false;
while self.accum_repeat > repeat_duration {
is_repeating = true;
self.is_on_first_repeat = false;
self.accum_repeat -= repeat_duration;
}
is_repeating
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct KeyEntry {
key: Key,
#[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "is_false"))]
ctrl: bool,
#[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "is_false"))]
shift: bool,
#[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "is_false"))]
meta: bool,
}
fn is_false(b: &bool) -> bool {
*b == false
}
impl From<Key> for KeyEntry {
fn from(key: Key) -> KeyEntry {
Self {
key,
ctrl: false,
shift: false,
meta: false,
}
}
}
impl KeyEntry {
pub fn key(key: Key) -> Self {
Self {
key,
ctrl: false,
shift: false,
meta: false,
}
}
}
#[derive(Debug, Clone, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct InputBundle {
pub keys: Vec<KeyEntry>,
}
impl InputBundle {
fn state(&self, input: &Input) -> RawButtonState {
let mut is_any_down = false;
let mut is_any_released = false;
for entry in self.keys.iter() {
let mut is_pressed = true;
let mut is_down = true;
let mut is_down_prev = true;
macro_rules! _add {
($key:expr) => {
is_pressed &= input.kbd.is_key_pressed($key);
is_down &= input.kbd.is_key_down($key);
is_down_prev |= input.kbd.states.b.is_down($key);
};
}
_add!(entry.key);
if entry.ctrl {
_add!(Key::LCtrl);
_add!(Key::RCtrl);
}
if entry.shift {
_add!(Key::LShift);
_add!(Key::RShift);
}
if entry.meta {
_add!(Key::LMeta);
_add!(Key::RMeta);
}
if is_pressed {
return RawButtonState::Pressed;
}
is_any_down |= is_down;
is_any_released |= is_down_prev && is_down;
}
if is_any_down {
RawButtonState::Down
} else {
if is_any_released {
RawButtonState::Released
} else {
RawButtonState::Up
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum StrictButtonState {
Down,
Up,
Pressed,
Repeating,
Released,
}
#[cfg(feature = "serde")]
pub mod button_serde_with {
use super::*;
use serde::{
de::{Deserialize, Deserializer},
ser::{Serialize, Serializer},
};
pub fn serialize<S>(value: &Button, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
value.input.serialize(serializer)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Button, D::Error>
where
D: Deserializer<'de>,
{
let input = InputBundle::deserialize(deserializer)?;
Ok(Button::new(input, KeyRepeatConfig::NoRepeat))
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Button {
pub input: InputBundle,
pub state: StrictButtonState,
#[cfg_attr(feature = "serde", serde(skip))]
repeat: KeyRepeatState,
}
impl Button {
pub fn new(bundle: InputBundle, repeat_cfg: KeyRepeatConfig) -> Self {
Self {
input: bundle,
state: StrictButtonState::Up,
repeat: KeyRepeatState::new(repeat_cfg),
}
}
pub fn set_repeat_config(&mut self, cfg: KeyRepeatConfig) {
self.repeat = KeyRepeatState::new(cfg);
}
pub fn is_down(&self) -> bool {
matches!(
self.state,
StrictButtonState::Down | StrictButtonState::Pressed | StrictButtonState::Repeating
)
}
pub fn is_pressed(&self) -> bool {
matches!(
self.state,
StrictButtonState::Pressed | StrictButtonState::Repeating
)
}
pub fn accum_down(&self) -> Duration {
self.repeat.accum_down
}
}
impl Button {
pub fn update(&mut self, input: &Input, dt: Duration) {
let state = self.input.state(input);
let is_repeating = self.repeat.update(state, dt);
self.state = if is_repeating {
StrictButtonState::Repeating
} else {
match state {
RawButtonState::Down => StrictButtonState::Down,
RawButtonState::Up => StrictButtonState::Up,
RawButtonState::Pressed => StrictButtonState::Pressed,
RawButtonState::Released => StrictButtonState::Released,
}
};
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct AxisButton {
#[cfg_attr(feature = "serde", serde(with = "button_serde_with"))]
pub pos: Button,
#[cfg_attr(feature = "serde", serde(with = "button_serde_with"))]
pub neg: Button,
}
impl AxisButton {
pub fn update(&mut self, input: &Input, dt: Duration) {
self.pos.update(input, dt);
self.neg.update(input, dt);
}
}
impl AxisButton {
pub fn sign_down(&self) -> Sign {
match [self.pos.is_down(), self.neg.is_down()] {
[true, true] => {
if self.pos.repeat.accum_down <= self.neg.repeat.accum_down {
Sign::Pos
} else {
Sign::Neg
}
}
[true, false] => Sign::Pos,
[false, true] => Sign::Neg,
[false, false] => Sign::Neutral,
}
}
pub fn sign_pressed(&self) -> Sign {
match [self.pos.is_down(), self.neg.is_down()] {
[true, true] => {
if self.pos.repeat.accum_down <= self.neg.repeat.accum_down {
if self.pos.is_pressed() {
Sign::Pos
} else {
Sign::Neutral
}
} else {
if self.neg.is_pressed() {
Sign::Neg
} else {
Sign::Neutral
}
}
}
[true, false] => {
if self.pos.is_pressed() {
Sign::Pos
} else {
Sign::Neutral
}
}
[false, true] => {
if self.neg.is_pressed() {
Sign::Neg
} else {
Sign::Neutral
}
}
[false, false] => Sign::Neutral,
}
}
pub fn accum_down(&self) -> Duration {
std::cmp::min(self.pos.repeat.accum_down, self.neg.repeat.accum_down)
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct AxisDirButton {
x: AxisButton,
y: AxisButton,
}
impl AxisDirButton {
pub fn new(repeat: KeyRepeatConfig, xs: [InputBundle; 2], ys: [InputBundle; 2]) -> Self {
let x_pos = Button::new(xs[0].clone(), repeat);
let x_neg = Button::new(xs[1].clone(), repeat);
let y_pos = Button::new(ys[0].clone(), repeat);
let y_neg = Button::new(ys[1].clone(), repeat);
Self {
x: AxisButton {
pos: x_pos,
neg: x_neg,
},
y: AxisButton {
pos: y_pos,
neg: y_neg,
},
}
}
}
impl AxisDirButton {
pub fn update(&mut self, input: &Input, dt: Duration) {
self.x.update(input, dt);
self.y.update(input, dt);
}
}
impl AxisDirButton {
pub fn dir4_down(&self) -> Option<Dir4> {
let x = self.x.sign_down().to_i8();
let y = self.y.sign_down().to_i8();
self.dir4(x, y)
}
pub fn dir4_pressed(&self) -> Option<Dir4> {
let x = self.x.sign_pressed().to_i8();
let y = self.y.sign_pressed().to_i8();
self.dir4(x, y)
}
pub fn dir8_down(&self) -> Option<Dir8> {
let x = self.x.sign_down().to_i8();
let y = self.y.sign_down().to_i8();
self.dir8(x, y)
}
pub fn dir8_pressed(&self) -> Option<Dir8> {
let x = self.x.sign_pressed().to_i8();
let y = self.y.sign_pressed().to_i8();
self.dir8(x, y)
}
fn dir4(&self, x: i32, y: i32) -> Option<Dir4> {
Some(match [x, y] {
[0, 0] => return None,
[0, -1] => Dir4::N,
[1, 0] => Dir4::E,
[0, 1] => Dir4::S,
[-1, 0] => Dir4::W,
[_, _] => {
if self.x.accum_down() <= self.y.accum_down() {
match x {
1 => Dir4::E,
-1 => Dir4::W,
_ => unreachable!(),
}
} else {
match y {
1 => Dir4::S,
-1 => Dir4::N,
_ => unreachable!(),
}
}
}
})
}
fn dir8(&self, x: i32, y: i32) -> Option<Dir8> {
Some(match [x, y] {
[0, 0] => return None,
[0, -1] => Dir8::N,
[1, -1] => Dir8::NE,
[1, 0] => Dir8::E,
[1, 1] => Dir8::SE,
[0, 1] => Dir8::S,
[-1, 1] => Dir8::SW,
[-1, 0] => Dir8::W,
[-1, -1] => Dir8::NW,
_ => unreachable!("unable to create Dir8 from virtual input"),
})
}
}