use std::borrow::Cow;
use std::collections::HashMap;
use std::fmt;
use crate::{
error::MouseParseError,
types::{Button, CodeMapper, Platform},
};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum CustomButton {
Standard(Button),
Custom(Cow<'static, str>),
}
impl CustomButton {
pub fn custom_static(name: &'static str) -> Self {
CustomButton::Custom(Cow::Borrowed(name))
}
pub fn custom_string(name: String) -> Self {
CustomButton::Custom(Cow::Owned(name))
}
}
impl fmt::Display for CustomButton {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CustomButton::Standard(btn) => write!(f, "{}", btn),
CustomButton::Custom(name) => write!(f, "{}", name),
}
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CustomButtonMap {
name: String,
mappings: HashMap<CustomButton, [Option<usize>; 3]>, reverse_mappings: [HashMap<usize, CustomButton>; 3], }
impl CustomButtonMap {
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
mappings: HashMap::new(),
reverse_mappings: [HashMap::new(), HashMap::new(), HashMap::new()],
}
}
pub fn add_button(
&mut self,
button: CustomButton,
windows_code: Option<usize>,
linux_code: Option<usize>,
macos_code: Option<usize>,
) -> Result<(), MouseParseError> {
if self.mappings.contains_key(&button) {
return Err(MouseParseError::DuplicateCustomButton(button.to_string()));
}
let button_clone = button.clone();
self.mappings
.insert(button, [windows_code, linux_code, macos_code]);
if let Some(code) = windows_code {
self.reverse_mappings[0].insert(code, button_clone.clone());
}
if let Some(code) = linux_code {
self.reverse_mappings[1].insert(code, button_clone.clone());
}
if let Some(code) = macos_code {
self.reverse_mappings[2].insert(code, button_clone);
}
Ok(())
}
pub fn name(&self) -> &str {
&self.name
}
}
impl CodeMapper for CustomButton {
fn to_code(&self, platform: Platform) -> usize {
match self {
CustomButton::Standard(btn) => <Button as CodeMapper>::to_code(btn, platform),
CustomButton::Custom(_) => {
panic!("Custom buttons require a CustomButtonMap for conversion")
}
}
}
fn from_code(code: usize, platform: Platform) -> Option<Self> {
<Button as CodeMapper>::from_code(code, platform).map(CustomButton::Standard)
}
}
impl CustomButtonMap {
pub fn get_code_for_button(&self, button: &CustomButton, platform: Platform) -> Option<usize> {
match button {
CustomButton::Standard(btn) => Some(<Button as CodeMapper>::to_code(btn, platform)),
CustomButton::Custom(_) => {
let idx = match platform {
Platform::Windows => 0,
Platform::Linux => 1,
Platform::MacOS => 2,
};
self.mappings.get(button).and_then(|codes| codes[idx])
}
}
}
pub fn from_code(&self, code: usize, platform: Platform) -> Option<CustomButton> {
let idx = match platform {
Platform::Windows => 0,
Platform::Linux => 1,
Platform::MacOS => 2,
};
self.reverse_mappings[idx].get(&code).cloned().or_else(|| {
<Button as CodeMapper>::from_code(code, platform).map(CustomButton::Standard)
})
}
}