use itertools::Itertools;
use std::time::{Duration, Instant};
use bevy::{
input::{keyboard::KeyCode, mouse::MouseButton},
prelude::GamepadButton,
};
use crate::{
axis::{AxisBinding, ValueState},
clash::ClashableKind,
};
#[derive(Debug, Clone, PartialEq)]
pub struct ButtonChord {
actions: Vec<ButtonBinding>,
}
impl ButtonChord {
pub fn len(&self) -> usize {
self.actions.len()
}
pub fn clashables(&self) -> Vec<ClashableKind> {
self.actions
.iter()
.flat_map(|asdf| asdf.clashables())
.dedup()
.collect()
}
pub fn bindings(&self) -> &[ButtonBinding] {
&self.actions
}
pub fn bindings_mut(&mut self) -> &mut [ButtonBinding] {
&mut self.actions
}
pub fn new(bindings: Vec<ButtonBinding>) -> Self {
Self { actions: bindings }
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub enum ButtonComboRules {
None,
PreviousMustBeReleased,
#[default]
NextMustBeReleased,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ButtonCombo {
actions: Vec<ButtonBinding>,
current_index: usize,
last_hit: Instant,
tolerance: Duration,
rules: ButtonComboRules,
}
impl ButtonCombo {
pub fn clashables(&self) -> Vec<ClashableKind> {
self.actions
.iter()
.flat_map(|asdf| asdf.clashables())
.dedup()
.collect()
}
pub fn rules(&self) -> ButtonComboRules {
self.rules
}
pub fn new_with_tolerance(
bindings: Vec<ButtonBinding>,
rules: ButtonComboRules,
tolerance: Duration,
) -> Self {
if bindings.len() <= 1 {
bevy::log::warn!("inlet detected a button combo that is less than 2 buttons long.")
}
ButtonCombo {
actions: bindings,
current_index: 0,
last_hit: Instant::now(),
tolerance,
rules,
}
}
pub fn new_with_tolerance_default_rules(
bindings: Vec<ButtonBinding>,
tolerance: Duration,
) -> Self {
Self::new_with_tolerance(bindings, ButtonComboRules::default(), tolerance)
}
pub fn new(bindings: Vec<ButtonBinding>, rules: ButtonComboRules) -> Self {
Self::new_with_tolerance(bindings, rules, Duration::from_millis(250))
}
pub fn new_default_rules(bindings: Vec<ButtonBinding>) -> Self {
Self::new_with_tolerance(
bindings,
ButtonComboRules::default(),
Duration::from_millis(250),
)
}
pub fn tolerance(&self) -> Duration {
self.tolerance
}
pub fn with_tolerance(mut self, tolerance: Duration) -> Self {
self.tolerance = tolerance;
self
}
pub fn with_rules(mut self, rules: ButtonComboRules) -> Self {
self.rules = rules;
self
}
pub fn expected_binding(&self) -> &ButtonBinding {
let i = if self.current_index != 0 && self.last_hit.elapsed() > self.tolerance {
0
} else {
self.current_index
};
&self.actions[i]
}
pub fn next_binding(&self) -> Option<&ButtonBinding> {
let i = self.current_index + 1;
if i == self.actions.len() {
None
} else {
Some(&self.actions[i])
}
}
pub fn previous_binding(&self) -> Option<&ButtonBinding> {
if self.current_index == 0 {
None
} else {
Some(&self.actions[self.current_index - 1])
}
}
pub fn expected_binding_mut(&mut self) -> &mut ButtonBinding {
if self.current_index != 0 && self.last_hit.elapsed() > self.tolerance {
self.current_index = 0;
}
&mut self.actions[self.current_index]
}
pub fn next_binding_mut(&mut self) -> Option<&mut ButtonBinding> {
let i = self.current_index + 1;
if i == self.actions.len() {
None
} else {
Some(&mut self.actions[i])
}
}
pub fn previous_binding_mut(&mut self) -> Option<&mut ButtonBinding> {
if self.current_index == 0 {
None
} else {
Some(&mut self.actions[self.current_index - 1])
}
}
pub fn hit(&mut self) -> bool {
self.last_hit = Instant::now();
let next = self.current_index + 1;
let out = if next == self.actions.len() {
self.current_index = 0;
true
} else {
self.current_index = next;
false
};
out
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum ButtonBinding {
Chord(ButtonChord),
Combo(ButtonCombo),
Keyboard(KeyCode),
Mouse(MouseButton),
Gamepad(GamepadButton),
Axis(Box<AxisBinding>),
Mock(bool),
}
impl ButtonBinding {
pub fn clashables(&self) -> Vec<ClashableKind> {
let mut out = Vec::with_capacity(1);
match self {
ButtonBinding::Chord(button_chord) => out.extend(button_chord.clashables()),
ButtonBinding::Combo(button_combo) => out.extend(button_combo.clashables()),
ButtonBinding::Keyboard(key_code) => out.push(ClashableKind::Keyboard(*key_code)),
ButtonBinding::Mouse(mouse_button) => {
out.push(ClashableKind::MouseButton(*mouse_button))
}
ButtonBinding::Gamepad(gamepad_button) => {
out.push(ClashableKind::GamepadButton(*gamepad_button))
}
ButtonBinding::Axis(axis_binding) => {
out.extend(axis_binding.clashables());
}
ButtonBinding::Mock(_) => {
}
}
out
}
pub fn is_mock(&self) -> bool {
matches!(self, Self::Mock(_))
}
}
impl From<KeyCode> for ButtonBinding {
fn from(value: KeyCode) -> Self {
ButtonBinding::Keyboard(value)
}
}
impl From<MouseButton> for ButtonBinding {
fn from(value: MouseButton) -> Self {
ButtonBinding::Mouse(value)
}
}
impl From<GamepadButton> for ButtonBinding {
fn from(value: GamepadButton) -> Self {
ButtonBinding::Gamepad(value)
}
}
impl From<ButtonCombo> for ButtonBinding {
fn from(value: ButtonCombo) -> Self {
ButtonBinding::Combo(value)
}
}
impl From<ButtonChord> for ButtonBinding {
fn from(value: ButtonChord) -> Self {
ButtonBinding::Chord(value)
}
}
impl From<AxisBinding> for ButtonBinding {
fn from(value: AxisBinding) -> Self {
ButtonBinding::Axis(Box::new(value))
}
}
#[derive(Debug, Hash, Copy, Clone, PartialEq, Eq)]
pub struct ButtonState {
pub(crate) kind: ActionableState,
pub(crate) start: Instant,
}
impl ButtonState {
pub fn value_state(&self) -> ValueState {
let (previous, current) = match self.kind {
ActionableState::Released => (0., 0.),
ActionableState::JustPressed => (0., 1.),
ActionableState::Pressed => (1., 1.),
ActionableState::JustReleased => (1., 0.),
};
ValueState {
previous,
current,
last_transition: self.start,
}
}
pub fn kind(&self) -> &ActionableState {
&self.kind
}
pub fn last_transition(&self) -> Duration {
self.start.elapsed()
}
pub fn just_pressed(&self) -> bool {
matches!(self.kind, ActionableState::JustPressed)
}
pub fn pressed(&self) -> bool {
matches!(
self.kind,
ActionableState::Pressed | ActionableState::JustPressed
)
}
pub fn held_for(&self, duration: &Duration) -> bool {
matches!(self.kind, ActionableState::Pressed) && self.start.elapsed() >= *duration
}
pub fn held_range(&self, start: &Duration, stop: &Duration) -> bool {
let elapsed = self.start.elapsed();
matches!(self.kind, ActionableState::Pressed) && elapsed >= *start && elapsed < *stop
}
pub fn try_get_held_duration(&self) -> Option<Duration> {
if matches!(self.kind, ActionableState::Pressed) {
Some(self.start.elapsed())
} else {
None
}
}
pub fn just_released(&self) -> bool {
matches!(self.kind, ActionableState::JustReleased)
}
pub fn released(&self) -> bool {
matches!(
self.kind,
ActionableState::Released | ActionableState::JustReleased
)
}
pub fn feed(&mut self, pressed: bool) -> bool {
match self.kind.tick(pressed) {
ActionableStateTick::None => false,
ActionableStateTick::Changed => true,
ActionableStateTick::Transitioned => {
self.start = Instant::now();
true
}
}
}
}
impl Default for ButtonState {
fn default() -> Self {
Self {
kind: Default::default(),
start: Instant::now(),
}
}
}
pub enum ActionableStateTick {
None,
Changed,
Transitioned,
}
#[derive(Debug, Hash, Copy, Clone, PartialEq, Eq, Default)]
pub enum ActionableState {
#[default]
Released,
JustPressed,
Pressed,
JustReleased,
}
impl ActionableState {
pub fn tick(&mut self, pressed: bool) -> ActionableStateTick {
if pressed {
match self {
ActionableState::Released | ActionableState::JustReleased => {
*self = ActionableState::JustPressed;
ActionableStateTick::Transitioned
}
ActionableState::JustPressed => {
*self = ActionableState::Pressed;
ActionableStateTick::Changed
}
_ => ActionableStateTick::None,
}
} else {
match self {
ActionableState::Pressed | ActionableState::JustPressed => {
*self = ActionableState::JustReleased;
ActionableStateTick::Transitioned
}
ActionableState::Released => ActionableStateTick::None,
ActionableState::JustReleased => {
*self = ActionableState::Released;
ActionableStateTick::Changed
}
}
}
}
pub fn is_released(&self) -> bool {
matches!(self, Self::JustReleased | Self::Released)
}
pub fn is_pressed(&self) -> bool {
matches!(self, Self::JustPressed | Self::Pressed)
}
pub fn is_just_pressed(&self) -> bool {
matches!(self, Self::JustPressed)
}
pub fn is_just_released(&self) -> bool {
matches!(self, Self::JustReleased)
}
}
pub struct ActionBinding<T> {
pub(crate) bindings: Vec<ButtonBinding>,
pub(crate) event: ButtonEventBinding<T>,
pub(crate) state: ButtonState,
}
impl<T> ActionBinding<T> {
pub fn clashables(&self) -> Vec<ClashableKind> {
let mut out = Vec::default();
for b in &self.bindings {
out.extend(b.clashables());
}
out
}
pub fn bindings(&self) -> &[ButtonBinding] {
&self.bindings
}
pub fn bindings_mut(&mut self) -> &mut [ButtonBinding] {
&mut self.bindings
}
#[inline]
pub fn just_pressed(&self) -> bool {
self.state.just_pressed()
}
#[inline]
pub fn pressed(&self) -> bool {
self.state.pressed()
}
#[inline]
pub fn held_for(&self, duration: &Duration) -> bool {
self.state.held_for(duration)
}
#[inline]
pub fn held_range(&self, start: &Duration, stop: &Duration) -> bool {
self.state.held_range(start, stop)
}
#[inline]
pub fn try_get_held_duration(&self) -> Option<Duration> {
self.state.try_get_held_duration()
}
#[inline]
pub fn just_released(&self) -> bool {
self.state.just_released()
}
#[inline]
pub fn released(&self) -> bool {
self.state.released()
}
pub fn new(bindings: Vec<ButtonBinding>, event: ButtonEventBinding<T>) -> Self {
Self {
bindings,
event,
state: ButtonState::default(),
}
}
pub fn new_no_event(bindings: Vec<ButtonBinding>) -> Self {
Self {
bindings,
event: ButtonEventBinding::None,
state: ButtonState::default(),
}
}
pub fn state(&self) -> &ButtonState {
&self.state
}
pub fn feed(&mut self, pressed: bool) -> bool {
self.state.feed(pressed)
}
pub fn feed_event(&mut self, pressed: bool) -> Option<T> {
if self.state.feed(pressed) {
self.event.try_get_event(&self.state)
} else {
None
}
}
pub fn mock(&mut self, pressed: bool) {
for bind in self.bindings.iter_mut() {
if let ButtonBinding::Mock(val) = bind {
*val = pressed;
return;
}
}
self.bindings.push(ButtonBinding::Mock(true));
}
pub fn mock_clear(&mut self) {
self.bindings.retain(|asdf| !asdf.is_mock());
}
}
impl<T> From<(ButtonBinding, ButtonEventBinding<T>)> for ActionBinding<T> {
fn from(value: (ButtonBinding, ButtonEventBinding<T>)) -> Self {
ActionBinding::new(vec![value.0], value.1)
}
}
impl<T> From<ButtonBinding> for ActionBinding<T> {
fn from(value: ButtonBinding) -> Self {
ActionBinding::new_no_event(vec![value])
}
}
impl<T> From<KeyCode> for ActionBinding<T> {
fn from(value: KeyCode) -> Self {
ActionBinding::new_no_event(vec![ButtonBinding::Keyboard(value)])
}
}
impl<T> From<MouseButton> for ActionBinding<T> {
fn from(value: MouseButton) -> Self {
ActionBinding::new_no_event(vec![ButtonBinding::Mouse(value)])
}
}
impl<T> From<GamepadButton> for ActionBinding<T> {
fn from(value: GamepadButton) -> Self {
ActionBinding::new_no_event(vec![ButtonBinding::Gamepad(value)])
}
}
impl<T> From<Vec<ButtonBinding>> for ActionBinding<T> {
fn from(value: Vec<ButtonBinding>) -> Self {
ActionBinding::new_no_event(value)
}
}
impl<T> From<(Vec<ButtonBinding>, ButtonEventBinding<T>)> for ActionBinding<T> {
fn from(value: (Vec<ButtonBinding>, ButtonEventBinding<T>)) -> Self {
ActionBinding::new(value.0, value.1)
}
}
impl<T> From<ButtonChord> for ActionBinding<T> {
fn from(value: ButtonChord) -> Self {
ActionBinding::new_no_event(vec![ButtonBinding::Chord(value)])
}
}
impl<T> From<ButtonCombo> for ActionBinding<T> {
fn from(value: ButtonCombo) -> Self {
ActionBinding::new_no_event(vec![ButtonBinding::Combo(value)])
}
}
#[derive(Clone)]
pub enum ButtonEventBinding<T> {
WhenPressed(fn() -> T),
WhilePressed(fn() -> T),
WhenPressedFor(Duration, fn() -> T, bool),
WhilePressedFor(Duration, fn() -> T),
PressedRange {
start: Duration,
end: Duration,
event: fn() -> T,
},
CapturePressDuration(fn(Duration) -> Option<T>),
WhenReleased(fn() -> T),
WhileReleased(fn() -> T),
None,
}
impl<T> ButtonEventBinding<T> {
pub fn try_get_event(&mut self, state: &ButtonState) -> Option<T> {
match self {
ButtonEventBinding::WhenPressed(event) => {
if state.just_pressed() {
return Some(event());
}
}
ButtonEventBinding::WhilePressed(event) => {
if state.pressed() {
return Some(event());
}
}
ButtonEventBinding::WhenPressedFor(duration, event, activated) => {
if state.held_for(duration) {
if !*activated {
*activated = true;
return Some(event());
}
} else if *activated {
*activated = false;
}
}
ButtonEventBinding::WhilePressedFor(duration, event) => {
if state.held_for(duration) {
return Some(event());
}
}
ButtonEventBinding::PressedRange { start, end, event } => {
if state.held_range(start, end) {
return Some(event());
}
}
ButtonEventBinding::CapturePressDuration(event) => {
if let Some(dur) = state.try_get_held_duration() {
return event(dur);
}
}
ButtonEventBinding::WhenReleased(event) => {
if state.just_released() {
return Some(event());
}
}
ButtonEventBinding::WhileReleased(event) => {
if state.released() {
return Some(event());
}
}
ButtonEventBinding::None => {}
}
None
}
pub fn none() -> Self {
Self::None
}
pub fn when_pressed(event: fn() -> T) -> Self {
Self::WhenPressed(event)
}
pub fn while_pressed(event: fn() -> T) -> Self {
Self::WhilePressed(event)
}
pub fn when_released(event: fn() -> T) -> Self {
Self::WhenReleased(event)
}
pub fn while_released(event: fn() -> T) -> Self {
Self::WhileReleased(event)
}
pub fn when_pressed_for(event: fn() -> T, duration: Duration) -> Self {
Self::WhenPressedFor(duration, event, false)
}
pub fn while_pressed_for(event: fn() -> T, duration: Duration) -> Self {
Self::WhilePressedFor(duration, event)
}
pub fn pressed_range(event: fn() -> T, start: Duration, end: Duration) -> Self {
Self::PressedRange { start, end, event }
}
pub fn capture_press_duration(event: fn(Duration) -> Option<T>) -> Self {
Self::CapturePressDuration(event)
}
}