From b437e55b676585403d261f887514b5260591aee0 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Mon, 4 Mar 2019 10:15:11 +0200 Subject: [PATCH] use shortcuts from config or default closes #63 --- ui/src/components.rs | 4 +- ui/src/components/contacts/contact_list.rs | 15 ++++- ui/src/components/mail/listing.rs | 8 +-- ui/src/components/mail/listing/compact.rs | 33 +++++----- ui/src/components/utilities.rs | 44 ++++++++----- ui/src/conf.rs | 4 +- ui/src/conf/shortcuts.rs | 74 ++++++++++++++++++---- 7 files changed, 130 insertions(+), 52 deletions(-) diff --git a/ui/src/components.rs b/ui/src/components.rs index 594ac8f9..d6dd2d9e 100644 --- a/ui/src/components.rs +++ b/ui/src/components.rs @@ -142,7 +142,7 @@ impl Entity { } } -pub type ShortcutMap = FnvHashMap; +pub type ShortcutMap = FnvHashMap<&'static str, Key>; /// Types implementing this Trait can draw on the terminal and receive events. /// If a type wants to skip drawing if it has not changed anything, it can hold some flag in its @@ -157,7 +157,7 @@ pub trait Component: Display + Debug + Send { fn kill(&mut self, _id: EntityId) {} fn set_id(&mut self, _id: EntityId) {} - fn get_shortcuts(&self) -> ShortcutMap { Default::default() } + fn get_shortcuts(&self, context: &Context) -> ShortcutMap { Default::default() } } /* diff --git a/ui/src/components/contacts/contact_list.rs b/ui/src/components/contacts/contact_list.rs index 9ee5731d..f1b70ddf 100644 --- a/ui/src/components/contacts/contact_list.rs +++ b/ui/src/components/contacts/contact_list.rs @@ -130,8 +130,9 @@ impl Component for ContactList { return true; } } + let shortcuts = self.get_shortcuts(context); match event.event_type { - UIEventType::Input(Key::Char('c')) => { + UIEventType::Input(ref key) if *key == shortcuts["create_contact"] => { let mut manager = ContactManager::default(); manager.account_pos = self.account_pos; let entity = Entity::from(Box::new(manager)); @@ -141,7 +142,8 @@ impl Component for ContactList { return true; }, - UIEventType::Input(Key::Char('e')) if self.length > 0 => { + + UIEventType::Input(ref key) if *key == shortcuts["edit_contact"] && self.length > 0 => { let account = &mut context.accounts[self.account_pos]; let book = &mut account.address_book; let card = book[&self.id_positions[self.cursor_pos]].clone(); @@ -202,4 +204,13 @@ impl Component for ContactList { fn kill(&mut self, uuid: Uuid) { self.mode = ViewMode::Close(uuid); } + fn get_shortcuts(&self, context: &Context) -> ShortcutMap { + let mut map = self.view.as_ref().map(|p| p.get_shortcuts(context)).unwrap_or_default(); + + let config_map = context.settings.shortcuts.contact_list.key_values(); + map.insert("create_contact", (*config_map["create_contact"]).clone()); + map.insert("edit_contact", (*config_map["edit_contact"]).clone()); + + map + } } diff --git a/ui/src/components/mail/listing.rs b/ui/src/components/mail/listing.rs index 07f7347f..722c81a9 100644 --- a/ui/src/components/mail/listing.rs +++ b/ui/src/components/mail/listing.rs @@ -116,11 +116,11 @@ impl Component for Listing { } } - fn get_shortcuts(&self) -> ShortcutMap { + fn get_shortcuts(&self, context: &Context) -> ShortcutMap { match self { - Listing::Compact(l) => l.get_shortcuts(), - Listing::Plain(l) => l.get_shortcuts(), - Listing::Threaded(l) => l.get_shortcuts(), + Listing::Compact(l) => l.get_shortcuts(context), + Listing::Plain(l) => l.get_shortcuts(context), + Listing::Threaded(l) => l.get_shortcuts(context), } } } diff --git a/ui/src/components/mail/listing/compact.rs b/ui/src/components/mail/listing/compact.rs index 2af9d6e8..d47ea5cb 100644 --- a/ui/src/components/mail/listing/compact.rs +++ b/ui/src/components/mail/listing/compact.rs @@ -387,6 +387,8 @@ impl Component for CompactListing { return true; } } + + let shortcuts = self.get_shortcuts(context); match event.event_type { UIEventType::Input(Key::Up) => { if self.cursor_pos.2 > 0 { @@ -402,20 +404,20 @@ impl Component for CompactListing { } return true; } - UIEventType::Input(Key::Char('\n')) if !self.unfocused => { + UIEventType::Input(ref k) if !self.unfocused && *k == shortcuts["open_thread"] => { self.unfocused = true; self.dirty = true; return true; } - UIEventType::Input(Key::PageUp) => { + UIEventType::Input(ref key) if *key == shortcuts["prev_page"] => { self.movement = Some(PageMovement::PageUp); self.set_dirty(); } - UIEventType::Input(Key::PageDown) => { + UIEventType::Input(ref key) if *key == shortcuts["next_page"] => { self.movement = Some(PageMovement::PageDown); self.set_dirty(); } - UIEventType::Input(Key::Char('i')) if self.unfocused => { + UIEventType::Input(ref k) if self.unfocused && *k == shortcuts["exit_thread"] => { self.unfocused = false; self.dirty = true; self.view = None; @@ -524,18 +526,19 @@ impl Component for CompactListing { self.dirty = true; } - fn get_shortcuts(&self) -> ShortcutMap { - let mut map = self.view.as_ref().map(|p| p.get_shortcuts()).unwrap_or_default(); + fn get_shortcuts(&self, context: &Context) -> ShortcutMap { + let mut map = self.view.as_ref().map(|p| p.get_shortcuts(context)).unwrap_or_default(); - map.insert(Key::Char('\n'), "Open thread.".into()); - map.insert(Key::PageUp, "Go to previous page.".into()); - map.insert(Key::PageDown, "Go to next page.".into()); - map.insert(Key::Char('i'), "Exit thread view.".into()); - map.insert(Key::Char('J'), "Go to previous folder.".into()); - map.insert(Key::Char('K'), "Go to next folder.".into()); - map.insert(Key::Char('h'), "Go to previous account.".into()); - map.insert(Key::Char('l'), "Go to next account.".into()); - map.insert(Key::Char('m'), "Start new mail draft in new tab.".into()); + let config_map = context.settings.shortcuts.compact_listing.key_values(); + map.insert("open_thread", if let Some(key) = config_map.get("open_thread") { (*key).clone() } else { Key::Char('\n') }); + map.insert("prev_page", if let Some(key) = config_map.get("prev_page") { (*key).clone() } else { Key::PageUp }); + map.insert("next_page", if let Some(key) = config_map.get("next_page") { (*key).clone() } else { Key::PageDown }); + map.insert("exit_thread", if let Some(key) = config_map.get("exit_thread") { (*key).clone() } else { Key::Char('i') }); + map.insert("prev_folder", if let Some(key) = config_map.get("prev_folder") { (*key).clone() } else { Key::Char('J') }); + map.insert("next_folder", if let Some(key) = config_map.get("next_folder") { (*key).clone() } else { Key::Char('K') }); + map.insert("prev_account", if let Some(key) = config_map.get("prev_account") { (*key).clone() } else { Key::Char('h') }); + map.insert("next_account", if let Some(key) = config_map.get("next_account") { (*key).clone() } else { Key::Char('l') }); + map.insert("new_mail", if let Some(key) = config_map.get("new_mail") { (*key).clone() } else { Key::Char('m') }); map } diff --git a/ui/src/components/utilities.rs b/ui/src/components/utilities.rs index a1a81a0a..b44071ea 100644 --- a/ui/src/components/utilities.rs +++ b/ui/src/components/utilities.rs @@ -101,9 +101,9 @@ impl Component for HSplit { self.bottom.component.set_dirty(); } - fn get_shortcuts(&self) -> ShortcutMap { - let mut top_map = self.top.get_shortcuts(); - top_map.extend(self.bottom.get_shortcuts().into_iter()); + fn get_shortcuts(&self, context: &Context) -> ShortcutMap { + let mut top_map = self.top.get_shortcuts(context); + top_map.extend(self.bottom.get_shortcuts(context).into_iter()); top_map } } @@ -197,9 +197,9 @@ impl Component for VSplit { self.right.component.set_dirty(); } - fn get_shortcuts(&self) -> ShortcutMap { - let mut right_map = self.right.get_shortcuts(); - right_map.extend(self.left.get_shortcuts().into_iter()); + fn get_shortcuts(&self, context: &Context) -> ShortcutMap { + let mut right_map = self.right.get_shortcuts(context); + right_map.extend(self.left.get_shortcuts(context).into_iter()); right_map } } @@ -397,25 +397,26 @@ impl Component for Pager { } context.dirty_areas.push_back(area); } - fn process_event(&mut self, event: &mut UIEvent, _context: &mut Context) -> bool { + fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool { + let shortcuts = self.get_shortcuts(context); match event.event_type { - UIEventType::Input(Key::Char('k')) => { + UIEventType::Input(ref key) if *key == shortcuts["scroll_up"] => { if self.cursor_pos > 0 { self.cursor_pos -= 1; self.dirty = true; } } - UIEventType::Input(Key::Char('j')) => { + UIEventType::Input(ref key) if *key == shortcuts["scroll_down"] => { if self.height > 0 && self.cursor_pos + 1 < self.height { self.cursor_pos += 1; self.dirty = true; } } - UIEventType::Input(Key::PageUp) => { + UIEventType::Input(ref key) if *key == shortcuts["page_up"] => { self.movement = Some(PageMovement::PageUp); self.dirty = true; } - UIEventType::Input(Key::PageDown) => { + UIEventType::Input(ref key) if *key == shortcuts["page_down"] => { self.movement = Some(PageMovement::PageDown); self.dirty = true; } @@ -440,6 +441,17 @@ impl Component for Pager { fn set_dirty(&mut self) { self.dirty = true; } + fn get_shortcuts(&self, context: &Context) -> ShortcutMap { + let mut map = FnvHashMap::with_capacity_and_hasher(4, Default::default()); + + let config_map = context.settings.shortcuts.pager.key_values(); + map.insert("scroll_up", (*config_map["scroll_up"]).clone()); + map.insert("scroll_down", (*config_map["scroll_down"]).clone()); + map.insert("page_up", (*config_map["page_up"]).clone()); + map.insert("page_down", (*config_map["page_down"]).clone()); + + map + } } /// Status bar. @@ -650,8 +662,8 @@ impl Component for StatusBar { self.dirty = true; } - fn get_shortcuts(&self) -> ShortcutMap { - self.container.get_shortcuts() + fn get_shortcuts(&self, context: &Context) -> ShortcutMap { + self.container.get_shortcuts(context) } } @@ -823,9 +835,9 @@ impl Component for Tabbed { create_box(grid, area); // TODO: print into a pager - for (idx, (k, v)) in self.children[self.cursor_pos].get_shortcuts().into_iter().enumerate() { + for (idx, (k, v)) in self.children[self.cursor_pos].get_shortcuts(context).into_iter().enumerate() { let (x, y) = write_string_to_grid( - &format!("{:?}", k), + &k, grid, Color::Byte(29), Color::Default, @@ -833,7 +845,7 @@ impl Component for Tabbed { false, ); write_string_to_grid( - &v, + &format!("{:?}", v), grid, Color::Default, Color::Default, diff --git a/ui/src/conf.rs b/ui/src/conf.rs index 782bba26..93b258e2 100644 --- a/ui/src/conf.rs +++ b/ui/src/conf.rs @@ -116,7 +116,7 @@ struct FileSettings { accounts: HashMap, pager: PagerSettings, notifications: NotificationsSettings, - shortcuts: CompactListingShortcuts, + shortcuts: Shortcuts, } #[derive(Debug, Clone, Default)] @@ -142,7 +142,7 @@ pub struct Settings { pub accounts: HashMap, pub pager: PagerSettings, pub notifications: NotificationsSettings, - pub shortcuts: CompactListingShortcuts, + pub shortcuts: Shortcuts, } impl FileSettings { diff --git a/ui/src/conf/shortcuts.rs b/ui/src/conf/shortcuts.rs index bea6b0da..c7f23f0c 100644 --- a/ui/src/conf/shortcuts.rs +++ b/ui/src/conf/shortcuts.rs @@ -2,9 +2,19 @@ use types::Key; //use std::any::TypeId; use fnv::FnvHashMap; +#[derive(Debug, Clone, Default, Deserialize)] +pub struct Shortcuts { + #[serde(flatten)] + pub compact_listing: CompactListingShortcuts, + #[serde(flatten)] + pub contact_list: ContactListShortcuts, + #[serde(flatten)] + pub pager: PagerShortcuts, +} + #[macro_export] macro_rules! key_values { - ( $cname:expr, derive ($($derives:ident),*) : pub struct $name:ident { $($fname:ident : Key),* }) => { + ( $cname:expr, derive ($($derives:ident),*) : pub struct $name:ident { $($fname:ident : Key |> $fdesc:expr),* }) => { #[derive($($derives),*)] #[serde(default)] #[serde(rename = $cname)] @@ -13,6 +23,12 @@ macro_rules! key_values { } impl $name { + pub fn key_desc(&self, key: &str) -> &'static str { + match key { + $(stringify!($fname) => $fdesc),*, + _ => unreachable!() + } + } pub fn key_values(&self) -> FnvHashMap<&'static str, &Key> { let mut map: FnvHashMap<&'static str, &Key> = Default::default(); $(map.insert(stringify!($fname),&(self.$fname));)* @@ -24,22 +40,22 @@ macro_rules! key_values { key_values!{ "compact-listing", derive (Debug, Clone, Deserialize) : pub struct CompactListingShortcuts { - open_thread: Key, - exit_thread: Key, - prev_page: Key, - next_page: Key, - prev_folder: Key, - next_folder: Key, - prev_account: Key, - next_account: Key, - new_mail: Key + open_thread: Key |> "Open thread.", + exit_thread: Key |> "Exit thread view.", + prev_page: Key |> "Go to previous page.", + next_page: Key |> "Go to next page.", + prev_folder: Key |> "Go to previous folder.", + next_folder: Key |> "Go to next folder.", + prev_account: Key |> "Go to previous account.", + next_account: Key |> "Go to next account.", + new_mail: Key |> "Start new mail draft in new tab." } } impl Default for CompactListingShortcuts { fn default() -> Self { - CompactListingShortcuts { + CompactListingShortcuts { open_thread: Key::Char('\n'), exit_thread: Key::Char('i'), prev_page: Key::PageUp, @@ -52,3 +68,39 @@ impl Default for CompactListingShortcuts { } } } + +key_values!{ "contact-list", derive (Debug, Clone, Deserialize) : +pub struct ContactListShortcuts { + create_contact: Key |> "Create new contact.", + edit_contact: Key |> "Edit contact under cursor." +} +} + +impl Default for ContactListShortcuts { + fn default() -> Self { + ContactListShortcuts { + create_contact: Key::Char('c'), + edit_contact: Key::Char('e'), + } + } +} + +key_values!{ "pager", derive (Debug, Clone, Deserialize) : +pub struct PagerShortcuts { + scroll_up: Key |> "Scroll up pager.", + scroll_down: Key |> "Scroll down pager.", + page_up: Key |> "Go to previous pager page", + page_down: Key |> "Go to next pager page" +} +} + +impl Default for PagerShortcuts { + fn default() -> Self { + PagerShortcuts { + scroll_up: Key::Char('k'), + scroll_down: Key::Char('j'), + page_up: Key::PageUp, + page_down: Key::PageDown, + } + } +}