mouse_codes/mapping/
custom.rs1use std::borrow::Cow;
4use std::collections::HashMap;
5use std::fmt;
6
7use crate::{
8 error::MouseParseError,
9 types::{Button, CodeMapper, Platform},
10};
11
12#[derive(Debug, Clone, PartialEq, Eq, Hash)]
14#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
15pub enum CustomButton {
16 Standard(Button),
18 Custom(Cow<'static, str>),
20}
21
22impl CustomButton {
23 pub fn custom_static(name: &'static str) -> Self {
25 CustomButton::Custom(Cow::Borrowed(name))
26 }
27
28 pub fn custom_string(name: String) -> Self {
30 CustomButton::Custom(Cow::Owned(name))
31 }
32}
33
34impl fmt::Display for CustomButton {
35 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36 match self {
37 CustomButton::Standard(btn) => write!(f, "{}", btn),
38 CustomButton::Custom(name) => write!(f, "{}", name),
39 }
40 }
41}
42
43#[derive(Debug, Clone)]
45#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
46pub struct CustomButtonMap {
47 name: String,
48 mappings: HashMap<CustomButton, [Option<usize>; 3]>, reverse_mappings: [HashMap<usize, CustomButton>; 3], }
51
52impl CustomButtonMap {
53 pub fn new(name: &str) -> Self {
55 Self {
56 name: name.to_string(),
57 mappings: HashMap::new(),
58 reverse_mappings: [HashMap::new(), HashMap::new(), HashMap::new()],
59 }
60 }
61
62 pub fn add_button(
64 &mut self,
65 button: CustomButton,
66 windows_code: Option<usize>,
67 linux_code: Option<usize>,
68 macos_code: Option<usize>,
69 ) -> Result<(), MouseParseError> {
70 if self.mappings.contains_key(&button) {
71 return Err(MouseParseError::DuplicateCustomButton(button.to_string()));
72 }
73
74 let button_clone = button.clone();
76
77 self.mappings
79 .insert(button, [windows_code, linux_code, macos_code]);
80
81 if let Some(code) = windows_code {
83 self.reverse_mappings[0].insert(code, button_clone.clone());
84 }
85 if let Some(code) = linux_code {
86 self.reverse_mappings[1].insert(code, button_clone.clone());
87 }
88 if let Some(code) = macos_code {
89 self.reverse_mappings[2].insert(code, button_clone);
90 }
91
92 Ok(())
93 }
94
95 pub fn name(&self) -> &str {
97 &self.name
98 }
99}
100
101impl CodeMapper for CustomButton {
103 fn to_code(&self, platform: Platform) -> usize {
104 match self {
105 CustomButton::Standard(btn) => <Button as CodeMapper>::to_code(btn, platform),
106 CustomButton::Custom(_) => {
107 panic!("Custom buttons require a CustomButtonMap for conversion")
108 }
109 }
110 }
111
112 fn from_code(code: usize, platform: Platform) -> Option<Self> {
113 <Button as CodeMapper>::from_code(code, platform).map(CustomButton::Standard)
114 }
115}
116
117impl CustomButtonMap {
118 pub fn get_code_for_button(&self, button: &CustomButton, platform: Platform) -> Option<usize> {
120 match button {
121 CustomButton::Standard(btn) => Some(<Button as CodeMapper>::to_code(btn, platform)),
122 CustomButton::Custom(_) => {
123 let idx = match platform {
124 Platform::Windows => 0,
125 Platform::Linux => 1,
126 Platform::MacOS => 2,
127 };
128 self.mappings.get(button).and_then(|codes| codes[idx])
129 }
130 }
131 }
132
133 pub fn from_code(&self, code: usize, platform: Platform) -> Option<CustomButton> {
135 let idx = match platform {
136 Platform::Windows => 0,
137 Platform::Linux => 1,
138 Platform::MacOS => 2,
139 };
140
141 self.reverse_mappings[idx].get(&code).cloned().or_else(|| {
143 <Button as CodeMapper>::from_code(code, platform).map(CustomButton::Standard)
144 })
145 }
146}