use super::{InputClassifier, InputEvent};
use crate::PagerState;
use crossterm::event::{Event, MouseEvent};
use std::{
collections::HashMap, collections::hash_map::RandomState, hash::BuildHasher, hash::Hash,
sync::Arc,
};
use std::borrow::Cow;
type EventReturnType = Arc<dyn Fn(Event, &PagerState) -> InputEvent + Send + Sync>;
#[derive(Clone)]
struct EventCallback {
cb: EventReturnType,
desc: Cow<'static, str>,
}
#[derive(Clone, Eq)]
enum EventWrapper {
ExactMatchEvent(Event),
WildEvent,
}
impl From<Event> for EventWrapper {
fn from(e: Event) -> Self {
Self::ExactMatchEvent(e)
}
}
impl From<&Event> for EventWrapper {
fn from(e: &Event) -> Self {
Self::ExactMatchEvent(e.clone())
}
}
impl PartialEq for EventWrapper {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(
Self::ExactMatchEvent(Event::Mouse(MouseEvent {
kind, modifiers, ..
})),
Self::ExactMatchEvent(Event::Mouse(MouseEvent {
kind: o_kind,
modifiers: o_modifiers,
..
})),
) => kind == o_kind && modifiers == o_modifiers,
(
Self::ExactMatchEvent(Event::Resize(..)),
Self::ExactMatchEvent(Event::Resize(..)),
)
| (Self::WildEvent, Self::WildEvent) => true,
(Self::ExactMatchEvent(ev), Self::ExactMatchEvent(o_ev)) => ev == o_ev,
_ => false,
}
}
}
impl Hash for EventWrapper {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
let tag = std::mem::discriminant(self);
tag.hash(state);
match self {
Self::ExactMatchEvent(Event::Mouse(MouseEvent {
kind, modifiers, ..
})) => {
kind.hash(state);
modifiers.hash(state);
}
Self::WildEvent | Self::ExactMatchEvent(Event::Resize(..)) => {}
Self::ExactMatchEvent(v) => {
v.hash(state);
}
}
}
}
pub struct HashedEventRegister<S>(HashMap<EventWrapper, EventCallback, S>);
impl HashedEventRegister<RandomState> {
#[must_use]
pub fn with_default_hasher() -> Self {
Self::new(RandomState::new())
}
}
impl Default for HashedEventRegister<RandomState> {
fn default() -> Self {
let mut event_register = Self::new(RandomState::new());
super::generate_default_bindings(&mut event_register);
event_register
}
}
impl<S> InputClassifier for HashedEventRegister<S>
where
S: BuildHasher,
{
fn classify_input(&self, ev: Event, ps: &crate::PagerState) -> Option<InputEvent> {
self.get(&ev).map(|c| c(ev, ps))
}
fn format_help(&self) -> Option<String> {
let h = self.format_help();
if h.is_empty() {
None
} else {
Some(h)
}
}
}
impl<S> HashedEventRegister<S>
where
S: BuildHasher,
{
pub const fn new(s: S) -> Self {
Self(HashMap::with_hasher(s))
}
#[must_use]
pub fn format_help(&self) -> String {
let entries = self.0.iter().filter_map(|(k, v)| match k {
EventWrapper::ExactMatchEvent(Event::Key(ke)) => Some((ke, v.desc.as_ref())),
_ => None,
});
crate::help::format_help_table_from_entries(entries)
}
pub fn insert_wild_event_matcher(
&mut self,
cb: impl Fn(Event, &PagerState) -> InputEvent + Send + Sync + 'static,
) {
self.0.insert(
EventWrapper::WildEvent,
EventCallback {
cb: Arc::new(cb),
desc: Cow::Borrowed(""),
},
);
}
fn get(&self, k: &Event) -> Option<&EventReturnType> {
self.0
.get(&k.into())
.map_or_else(|| self.0.get(&EventWrapper::WildEvent), Some)
.map(|entry| &entry.cb)
}
pub fn add_resize_event(
&mut self,
cb: impl Fn(Event, &PagerState) -> InputEvent + Send + Sync + 'static,
) {
let v = Arc::new(cb);
self.0.insert(
EventWrapper::ExactMatchEvent(Event::Resize(0, 0)),
EventCallback {
cb: v,
desc: Cow::Borrowed(""),
},
);
}
pub fn remove_resize_event(&mut self) {
self.0
.remove(&EventWrapper::ExactMatchEvent(Event::Resize(0, 0)));
}
}
impl<S> HashedEventRegister<S>
where
S: BuildHasher,
{
pub fn add_key_events(
&mut self,
desc: &[&str],
cb: impl Fn(Event, &PagerState) -> InputEvent + Send + Sync + 'static,
) {
self.add_described_key_events(desc, "", cb);
}
pub fn add_described_key_events(
&mut self,
keys: &[&str],
desc: impl Into<Cow<'static, str>>,
cb: impl Fn(Event, &PagerState) -> InputEvent + Send + Sync + 'static,
) {
let v = Arc::new(cb);
let d = desc.into();
for k in keys {
self.0.insert(
Event::Key(super::definitions::keydefs::parse_key_event(k)).into(),
EventCallback {
cb: v.clone(),
desc: d.clone(),
},
);
}
}
pub fn add_key_events_checked(
&mut self,
desc: &[&str],
cb: impl Fn(Event, &PagerState) -> InputEvent + Send + Sync + 'static,
remap: bool,
) {
self.add_described_key_events_checked(desc, "", cb, remap);
}
pub fn add_described_key_events_checked(
&mut self,
keys: &[&str],
desc: impl Into<Cow<'static, str>>,
cb: impl Fn(Event, &PagerState) -> InputEvent + Send + Sync + 'static,
remap: bool,
) {
let v = Arc::new(cb);
let d = desc.into();
for k in keys {
let def: EventWrapper =
Event::Key(super::definitions::keydefs::parse_key_event(k)).into();
assert!(self.0.contains_key(&def) && remap, "");
self.0.insert(
def,
EventCallback {
cb: v.clone(),
desc: d.clone(),
},
);
}
}
pub fn remove_key_events(&mut self, desc: &[&str]) {
for k in desc {
self.0
.remove(&Event::Key(super::definitions::keydefs::parse_key_event(k)).into());
}
}
pub fn add_help_key(&mut self, desc: &[&str]) {
let keys = if desc.is_empty() { &["m-h"][..] } else { desc };
self.add_described_key_events(keys, "help", |_, _| InputEvent::ShowHelp);
}
pub fn add_help_key_checked(&mut self, desc: &[&str], remap: bool) {
let keys = if desc.is_empty() { &["m-h"][..] } else { desc };
self.add_described_key_events_checked(keys, "help", |_, _| InputEvent::ShowHelp, remap);
}
}
impl<S> HashedEventRegister<S>
where
S: BuildHasher,
{
pub fn add_mouse_events(
&mut self,
desc: &[&str],
cb: impl Fn(Event, &PagerState) -> InputEvent + Send + Sync + 'static,
) {
let v = Arc::new(cb);
for k in desc {
self.0.insert(
Event::Mouse(super::definitions::mousedefs::parse_mouse_event(k)).into(),
EventCallback {
cb: v.clone(),
desc: Cow::Borrowed(""),
},
);
}
}
pub fn add_mouse_events_checked(
&mut self,
desc: &[&str],
cb: impl Fn(Event, &PagerState) -> InputEvent + Send + Sync + 'static,
remap: bool,
) {
let v = Arc::new(cb);
for k in desc {
let def: EventWrapper =
Event::Mouse(super::definitions::mousedefs::parse_mouse_event(k)).into();
assert!(self.0.contains_key(&def) && remap, "");
self.0.insert(
def,
EventCallback {
cb: v.clone(),
desc: Cow::Borrowed(""),
},
);
}
}
pub fn remove_mouse_events(&mut self, mouse: &[&str]) {
for k in mouse {
self.0
.remove(&Event::Mouse(super::definitions::mousedefs::parse_mouse_event(k)).into());
}
}
}