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,
41  FromRepr, 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 {
51      if self.modifiers.contains (Modifiers::ANY) ||
52        other.modifiers.contains (Modifiers::ANY)
53      {
54        true
55      } else {
56        self.modifiers == other.modifiers
57      }
58    }
59  }
60}
61
62// NOTE: we need to define partial ord to use custom partial eq comparison
63impl PartialOrd for Button {
64  fn partial_cmp (&self, other : &Self) -> Option <std::cmp::Ordering> {
65    let out = if self == other {
66      std::cmp::Ordering::Equal
67    } else {
68      if self.variant > other.variant {
69        std::cmp::Ordering::Greater
70      } else if self.variant < other.variant {
71        std::cmp::Ordering::Less
72      } else if self.modifiers > other.modifiers {
73        std::cmp::Ordering::Greater
74      } else if self.modifiers < other.modifiers {
75        std::cmp::Ordering::Less
76      } else {
77        unreachable!()
78      }
79    };
80    Some (out)
81  }
82}
83
84// NOTE: we need to define ord to use custom partial ord comparison
85impl Ord for Button {
86  fn cmp (&self, other : &Self) -> std::cmp::Ordering {
87    self.partial_cmp (other).unwrap()
88  }
89}
90
91impl <K : Into <Variant>> From <K> for Button {
92  fn from (k : K) -> Self {
93    Button {
94      variant:   k.into(),
95      modifiers: Modifiers::empty()
96    }
97  }
98}
99
100#[cfg(test)]
101mod test {
102  use strum;
103  use crate::interface::controller::controls;
104
105  use super::*;
106
107  #[test]
108  fn variant_unique_serialization() {
109    use strum::EnumCount;
110    for i in 0..Keycode::COUNT {
111      use std::str::FromStr;
112
113      let variant = Variant::from (Keycode::from_repr (i).unwrap());
114      let s    = variant.to_string();
115      assert_eq!(variant, Variant::from_str (&s).unwrap());
116    }
117
118    for i in 0..Mouse::COUNT {
119      use std::str::FromStr;
120
121      let variant = Variant::from (Mouse::from_repr (i).unwrap());
122      let s    = variant.to_string();
123      assert_eq!(variant, Variant::from_str (&s).unwrap());
124    }
125  }
126
127  #[test]
128  fn button_equality() {
129    let a = Button {
130      modifiers: Modifiers::ANY,
131      .. Button::from (Keycode::A)
132    };
133    let b = Button {
134      modifiers: Modifiers::SHIFT,
135      .. Button::from (Keycode::A)
136    };
137    let c = Button {
138      modifiers: Modifiers::ANY,
139      .. Button::from (Keycode::C)
140    };
141    let d = Button::from (Keycode::C);
142    assert_eq!(a, b);
143    assert_eq!(&a, &b);
144    assert_ne!(a, c);
145    assert_eq!(c, d);
146    assert_eq!(&c, &d);
147    let buttons = vec![(a, controls::Button (0))];
148    assert_eq!(a.cmp (&b), std::cmp::Ordering::Equal);
149    assert!(buttons.binary_search_by_key (&b, |(button, _)| *button).is_ok());
150  }
151}