gooey/interface/view/input/button/
mod.rs

1use std;
2use derive_more::{From, TryInto};
3use parse_display::{Display, FromStr};
4use strum::{EnumCount, FromRepr};
5use super::Modifiers;
6
7pub mod keycode;
8pub use self::keycode::Keycode;
9
10#[derive(Clone, Copy, Debug, Eq)]
11pub struct Button {
12  pub variant   : Variant,
13  pub modifiers : Modifiers
14}
15
16#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
17pub enum State {
18  Pressed,
19  Released
20}
21
22/// Identifies which button was pressed.
23///
24/// NOTE: the types contained in this newtype wrapper should have unique
25/// serializations, this is so we can omit the tag when giving bindings in
26/// configuration files (see `control::button::Binding`).
27///
28/// `serde_plain` is unable to parse untagged enums, so we use the
29/// `parse_display::FromStr` derive instead.
30// TODO: joystick buttons ?
31#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd,
32  Display, FromStr, From, TryInto)]
33#[display("{0:?}")]  // an "untagged" serialization format for FromStr
34pub enum Variant {
35  Keycode (Keycode),
36  Mouse   (Mouse)
37}
38
39/// A mouse button
40#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, EnumCount, FromRepr,
41  FromStr)]
42pub enum Mouse {
43  Mouse1, Mouse2, Mouse3, Mouse4, Mouse5
44}
45
46impl PartialEq for Button {
47  fn eq (&self, other : &Self) -> bool {
48    if self.variant != other.variant {
49      false
50    } else if self.modifiers.contains (Modifiers::ANY)
51      || other.modifiers.contains (Modifiers::ANY)
52    {
53      true
54    } else {
55      self.modifiers == other.modifiers
56    }
57  }
58}
59
60// NOTE: we need to define partial ord to use custom partial eq comparison
61#[expect(clippy::non_canonical_partial_ord_impl)]
62impl PartialOrd for Button {
63  fn partial_cmp (&self, other : &Self) -> Option <std::cmp::Ordering> {
64    let out = if self == other {
65      std::cmp::Ordering::Equal
66    } else if self.variant > other.variant {
67      std::cmp::Ordering::Greater
68    } else if self.variant < other.variant {
69      std::cmp::Ordering::Less
70    } else if self.modifiers > other.modifiers {
71      std::cmp::Ordering::Greater
72    } else if self.modifiers < other.modifiers {
73      std::cmp::Ordering::Less
74    } else {
75      unreachable!()
76    };
77    Some (out)
78  }
79}
80
81// NOTE: we need to define ord to use custom partial ord comparison
82impl Ord for Button {
83  fn cmp (&self, other : &Self) -> std::cmp::Ordering {
84    self.partial_cmp (other).unwrap()
85  }
86}
87
88impl <K : Into <Variant>> From <K> for Button {
89  fn from (k : K) -> Self {
90    Button {
91      variant:   k.into(),
92      modifiers: Modifiers::empty()
93    }
94  }
95}
96
97#[cfg(test)]
98mod test {
99  use strum;
100  use crate::interface::controller::controls;
101
102  use super::*;
103
104  #[test]
105  fn variant_unique_serialization() {
106    use strum::EnumCount;
107    for i in 0..Keycode::COUNT {
108      use std::str::FromStr;
109      let variant = Variant::from (Keycode::from_repr (i).unwrap());
110      let s = variant.to_string();
111      assert_eq!(variant, Variant::from_str (&s).unwrap());
112    }
113
114    for i in 0..Mouse::COUNT {
115      use std::str::FromStr;
116      let variant = Variant::from (Mouse::from_repr (i).unwrap());
117      let s = variant.to_string();
118      assert_eq!(variant, Variant::from_str (&s).unwrap());
119    }
120  }
121
122  #[test]
123  fn button_equality() {
124    let a = Button { modifiers: Modifiers::ANY, .. Button::from (Keycode::A) };
125    let b = Button { modifiers: Modifiers::SHIFT, .. Button::from (Keycode::A) };
126    let c = Button { modifiers: Modifiers::ANY, .. Button::from (Keycode::C) };
127    let d = Button::from (Keycode::C);
128    assert_eq!(a, b);
129    assert_eq!(&a, &b);
130    assert_ne!(a, c);
131    assert_eq!(c, d);
132    assert_eq!(&c, &d);
133    let buttons = [(a, controls::Button (0))];
134    assert_eq!(a.cmp (&b), std::cmp::Ordering::Equal);
135    let _ = buttons.binary_search_by_key (&b, |(button, _)| *button).unwrap();
136  }
137}