diff --git a/ui/src/components.rs b/ui/src/components.rs index dd3febb7..e5018fbd 100644 --- a/ui/src/components.rs +++ b/ui/src/components.rs @@ -78,7 +78,7 @@ const _DOUBLE_UP_AND_RIGHT: char = '╚'; type ComponentId = Uuid; pub type ShortcutMap = FnvHashMap<&'static str, Key>; -pub type ShortcutMaps = FnvHashMap; +pub type ShortcutMaps = FnvHashMap<&'static str, ShortcutMap>; /// 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 diff --git a/ui/src/components/contacts/contact_list.rs b/ui/src/components/contacts/contact_list.rs index 4df6dd57..8272acc7 100644 --- a/ui/src/components/contacts/contact_list.rs +++ b/ui/src/components/contacts/contact_list.rs @@ -552,7 +552,7 @@ impl Component for ContactList { let shortcuts = &self.get_shortcuts(context)[Self::DESCRIPTION]; match *event { UIEvent::Input(ref key) - if *key == shortcuts["create_contact"] && self.view.is_none() => + if key == shortcuts["create_contact"] && self.view.is_none() => { let mut manager = ContactManager::default(); manager.set_parent_id(self.id); @@ -565,7 +565,7 @@ impl Component for ContactList { } UIEvent::Input(ref key) - if *key == shortcuts["edit_contact"] && self.length > 0 && self.view.is_none() => + if key == shortcuts["edit_contact"] && self.length > 0 && self.view.is_none() => { let account = &mut context.accounts[self.account_pos]; let book = &mut account.address_book; @@ -580,7 +580,7 @@ impl Component for ContactList { return true; } - UIEvent::Input(ref key) if *key == shortcuts["next_account"] && self.view.is_none() => { + UIEvent::Input(ref key) if key == shortcuts["next_account"] && self.view.is_none() => { let amount = if self.cmd_buf.is_empty() { 1 } else if let Ok(amount) = self.cmd_buf.parse::() { @@ -612,7 +612,7 @@ impl Component for ContactList { return true; } - UIEvent::Input(ref key) if *key == shortcuts["prev_account"] && self.view.is_none() => { + UIEvent::Input(ref key) if key == shortcuts["prev_account"] && self.view.is_none() => { let amount = if self.cmd_buf.is_empty() { 1 } else if let Ok(amount) = self.cmd_buf.parse::() { @@ -747,13 +747,7 @@ impl Component for ContactList { .unwrap_or_default(); let config_map = context.settings.shortcuts.contact_list.key_values(); - map.insert( - self.to_string(), - config_map - .into_iter() - .map(|(k, v)| (k, v.clone())) - .collect(), - ); + map.insert(Self::DESCRIPTION, config_map); map } diff --git a/ui/src/components/mail/compose.rs b/ui/src/components/mail/compose.rs index b88d827c..a87db51a 100644 --- a/ui/src/components/mail/compose.rs +++ b/ui/src/components/mail/compose.rs @@ -1034,10 +1034,8 @@ impl Component for Composer { Default::default() }; - let mut our_map: ShortcutMap = Default::default(); - our_map.insert("Deliver draft to mailer.", Key::Char('s')); - our_map.insert("Edit in $EDITOR", Key::Char('e')); - map.insert(Composer::DESCRIPTION.to_string(), our_map); + let our_map: ShortcutMap = context.settings.shortcuts.composing.key_values(); + map.insert(Composer::DESCRIPTION, our_map); map } diff --git a/ui/src/components/mail/listing.rs b/ui/src/components/mail/listing.rs index 53e24e00..8b4c3471 100644 --- a/ui/src/components/mail/listing.rs +++ b/ui/src/components/mail/listing.rs @@ -508,7 +508,7 @@ impl Component for Listing { self.component.set_movement(PageMovement::Down(amount)); return true; } - UIEvent::Input(ref key) if *key == shortcuts["prev_page"] => { + UIEvent::Input(ref key) if key == shortcuts["prev_page"] => { let mult = if self.cmd_buf.is_empty() { 1 } else if let Ok(mult) = self.cmd_buf.parse::() { @@ -527,7 +527,7 @@ impl Component for Listing { self.component.set_movement(PageMovement::PageUp(mult)); return true; } - UIEvent::Input(ref key) if *key == shortcuts["next_page"] => { + UIEvent::Input(ref key) if key == shortcuts["next_page"] => { let mult = if self.cmd_buf.is_empty() { 1 } else if let Ok(mult) = self.cmd_buf.parse::() { @@ -564,7 +564,7 @@ impl Component for Listing { .push_back(UIEvent::Action(Tab(NewDraft(self.cursor_pos.0, None)))); return true; } - UIEvent::Input(ref key) if *key == shortcuts["search"] => { + UIEvent::Input(ref key) if key == shortcuts["search"] => { context .replies .push_back(UIEvent::ExInput(Key::Paste("filter ".to_string()))); @@ -573,7 +573,7 @@ impl Component for Listing { .push_back(UIEvent::ChangeMode(UIMode::Execute)); return true; } - UIEvent::Input(ref key) if *key == shortcuts["set_seen"] => { + UIEvent::Input(ref key) if key == shortcuts["set_seen"] => { let mut event = UIEvent::Action(Action::Listing(ListingAction::SetSeen)); if match self.component { Plain(ref mut l) => l.process_event(&mut event, context), @@ -647,13 +647,7 @@ impl Component for Listing { Conversations(ref l) => l.get_shortcuts(context), }; let config_map = context.settings.shortcuts.listing.key_values(); - map.insert( - Listing::DESCRIPTION.to_string(), - config_map - .into_iter() - .map(|(k, v)| (k, v.clone())) - .collect(), - ); + map.insert(Listing::DESCRIPTION, config_map); map } diff --git a/ui/src/components/mail/listing/compact.rs b/ui/src/components/mail/listing/compact.rs index b2aa6faf..b4804662 100644 --- a/ui/src/components/mail/listing/compact.rs +++ b/ui/src/components/mail/listing/compact.rs @@ -1066,7 +1066,7 @@ impl Component for CompactListing { let shortcuts = &self.get_shortcuts(context)[CompactListing::DESCRIPTION]; if self.length > 0 { match *event { - UIEvent::Input(ref k) if !self.unfocused && *k == shortcuts["open_thread"] => { + UIEvent::Input(ref k) if !self.unfocused && k == shortcuts["open_thread"] => { if self.filtered_selection.is_empty() { self.view = ThreadView::new(self.cursor_pos, None, context); } else { @@ -1087,7 +1087,7 @@ impl Component for CompactListing { self.dirty = true; return true; } - UIEvent::Input(ref k) if self.unfocused && *k == shortcuts["exit_thread"] => { + UIEvent::Input(ref k) if self.unfocused && k == shortcuts["exit_thread"] => { self.unfocused = false; self.dirty = true; /* If self.row_updates is not empty and we exit a thread, the row_update events @@ -1096,7 +1096,7 @@ impl Component for CompactListing { self.force_draw = true; return true; } - UIEvent::Input(ref key) if !self.unfocused && *key == shortcuts["select_entry"] => { + UIEvent::Input(ref key) if !self.unfocused && key == shortcuts["select_entry"] => { let thread_hash = self.get_thread_under_cursor(self.cursor_pos.2, context); self.selection.entry(thread_hash).and_modify(|e| *e = !*e); } @@ -1306,13 +1306,7 @@ impl Component for CompactListing { }; let config_map = context.settings.shortcuts.compact_listing.key_values(); - map.insert( - CompactListing::DESCRIPTION.to_string(), - config_map - .into_iter() - .map(|(k, v)| (k, v.clone())) - .collect(), - ); + map.insert(CompactListing::DESCRIPTION, config_map); map } diff --git a/ui/src/components/mail/listing/conversations.rs b/ui/src/components/mail/listing/conversations.rs index 0dac1be6..67ce1c4a 100644 --- a/ui/src/components/mail/listing/conversations.rs +++ b/ui/src/components/mail/listing/conversations.rs @@ -1013,7 +1013,7 @@ impl Component for ConversationsListing { let shortcuts = &self.get_shortcuts(context)[ConversationsListing::DESCRIPTION]; if self.length > 0 { match *event { - UIEvent::Input(ref k) if !self.unfocused && *k == shortcuts["open_thread"] => { + UIEvent::Input(ref k) if !self.unfocused && k == shortcuts["open_thread"] => { if self.length == 0 { return true; } @@ -1039,7 +1039,7 @@ impl Component for ConversationsListing { self.dirty = true; return true; } - UIEvent::Input(ref k) if self.unfocused && *k == shortcuts["exit_thread"] => { + UIEvent::Input(ref k) if self.unfocused && k == shortcuts["exit_thread"] => { self.unfocused = false; self.dirty = true; /* If self.row_updates is not empty and we exit a thread, the row_update events @@ -1048,7 +1048,7 @@ impl Component for ConversationsListing { self.force_draw = true; return true; } - UIEvent::Input(ref key) if !self.unfocused && *key == shortcuts["select_entry"] => { + UIEvent::Input(ref key) if !self.unfocused && key == shortcuts["select_entry"] => { let thread_hash = self.get_thread_under_cursor(self.cursor_pos.2, context); self.selection.entry(thread_hash).and_modify(|e| *e = !*e); return true; @@ -1263,13 +1263,7 @@ impl Component for ConversationsListing { }; let config_map = context.settings.shortcuts.compact_listing.key_values(); - map.insert( - ConversationsListing::DESCRIPTION.to_string(), - config_map - .into_iter() - .map(|(k, v)| (k, v.clone())) - .collect(), - ); + map.insert(ConversationsListing::DESCRIPTION, config_map); map } diff --git a/ui/src/components/mail/listing/plain.rs b/ui/src/components/mail/listing/plain.rs index 59c8c052..f9d8de12 100644 --- a/ui/src/components/mail/listing/plain.rs +++ b/ui/src/components/mail/listing/plain.rs @@ -894,7 +894,7 @@ impl Component for PlainListing { let shortcuts = &self.get_shortcuts(context)[PlainListing::DESCRIPTION]; if self.length > 0 { match *event { - UIEvent::Input(ref k) if !self.unfocused && *k == shortcuts["open_thread"] => { + UIEvent::Input(ref k) if !self.unfocused && k == shortcuts["open_thread"] => { let env_hash = self.get_env_under_cursor(self.cursor_pos.2, context); let temp = (self.cursor_pos.0, self.cursor_pos.1, env_hash); self.view = MailView::new(temp, None, None); @@ -902,7 +902,7 @@ impl Component for PlainListing { self.dirty = true; return true; } - UIEvent::Input(ref k) if self.unfocused && *k == shortcuts["exit_thread"] => { + UIEvent::Input(ref k) if self.unfocused && k == shortcuts["exit_thread"] => { self.unfocused = false; self.dirty = true; /* If self.row_updates is not empty and we exit a thread, the row_update events @@ -911,7 +911,7 @@ impl Component for PlainListing { self.force_draw = true; return true; } - UIEvent::Input(ref key) if !self.unfocused && *key == shortcuts["select_entry"] => { + UIEvent::Input(ref key) if !self.unfocused && key == shortcuts["select_entry"] => { let env_hash = self.get_env_under_cursor(self.cursor_pos.2, context); self.selection.entry(env_hash).and_modify(|e| *e = !*e); } @@ -1074,13 +1074,7 @@ impl Component for PlainListing { }; let config_map = context.settings.shortcuts.compact_listing.key_values(); - map.insert( - PlainListing::DESCRIPTION.to_string(), - config_map - .into_iter() - .map(|(k, v)| (k, v.clone())) - .collect(), - ); + map.insert(PlainListing::DESCRIPTION, config_map); map } diff --git a/ui/src/components/mail/view.rs b/ui/src/components/mail/view.rs index 11c1019e..8aaf3b89 100644 --- a/ui/src/components/mail/view.rs +++ b/ui/src/components/mail/view.rs @@ -693,7 +693,7 @@ impl Component for MailView { let shortcuts = &self.get_shortcuts(context)[MailView::DESCRIPTION]; match *event { - UIEvent::Input(ref k) if *k == shortcuts["reply"] => { + UIEvent::Input(ref k) if k == shortcuts["reply"] => { let account = &context.accounts[self.coordinates.0]; let folder_hash = account[self.coordinates.1].unwrap().folder.hash(); let envelope: EnvelopeRef = account.collection.get_env(self.coordinates.2); @@ -710,7 +710,7 @@ impl Component for MailView { )))); return true; } - UIEvent::Input(ref k) if *k == shortcuts["edit"] => { + UIEvent::Input(ref k) if k == shortcuts["edit"] => { context.replies.push_back(UIEvent::Action(Tab(Edit( self.coordinates.0, self.coordinates.2, @@ -719,7 +719,7 @@ impl Component for MailView { } UIEvent::Input(ref key) if !self.mode.is_contact_selector() - && *key == shortcuts["add_addresses_to_contacts"] => + && key == shortcuts["add_addresses_to_contacts"] => { let account = &context.accounts[self.coordinates.0]; let envelope: EnvelopeRef = account.collection.get_env(self.coordinates.2); @@ -766,7 +766,7 @@ impl Component for MailView { } UIEvent::Input(ref key) if (self.mode == ViewMode::Normal || self.mode == ViewMode::Subview) - && *key == shortcuts["view_raw_source"] => + && key == shortcuts["view_raw_source"] => { self.mode = ViewMode::Raw; self.set_dirty(); @@ -777,7 +777,7 @@ impl Component for MailView { || self.mode == ViewMode::Subview || self.mode == ViewMode::Url || self.mode == ViewMode::Raw) - && *key == shortcuts["return_to_normal_view"] => + && key == shortcuts["return_to_normal_view"] => { self.mode = ViewMode::Normal; self.set_dirty(); @@ -786,7 +786,7 @@ impl Component for MailView { UIEvent::Input(ref key) if (self.mode == ViewMode::Normal || self.mode == ViewMode::Subview) && !self.cmd_buf.is_empty() - && *key == shortcuts["open_mailcap"] => + && key == shortcuts["open_mailcap"] => { let lidx = self.cmd_buf.parse::().unwrap(); self.cmd_buf.clear(); @@ -843,7 +843,7 @@ impl Component for MailView { } } UIEvent::Input(ref key) - if *key == shortcuts["open_attachment"] + if key == shortcuts["open_attachment"] && !self.cmd_buf.is_empty() && (self.mode == ViewMode::Normal || self.mode == ViewMode::Subview) => { @@ -975,7 +975,7 @@ impl Component for MailView { } }; } - UIEvent::Input(ref key) if *key == shortcuts["toggle_expand_headers"] => { + UIEvent::Input(ref key) if key == shortcuts["toggle_expand_headers"] => { self.expand_headers = !self.expand_headers; self.dirty = true; return true; @@ -983,7 +983,7 @@ impl Component for MailView { UIEvent::Input(ref key) if !self.cmd_buf.is_empty() && self.mode == ViewMode::Url - && *key == shortcuts["go_to_url"] => + && key == shortcuts["go_to_url"] => { let lidx = self.cmd_buf.parse::().unwrap(); self.cmd_buf.clear(); @@ -1041,7 +1041,7 @@ impl Component for MailView { } UIEvent::Input(ref key) if (self.mode == ViewMode::Normal || self.mode == ViewMode::Url) - && *key == shortcuts["toggle_url_mode"] => + && key == shortcuts["toggle_url_mode"] => { match self.mode { ViewMode::Normal => self.mode = ViewMode::Url, @@ -1338,7 +1338,7 @@ impl Component for MailView { our_map.insert("toggle_url_mode", Key::Char('u')); } our_map.insert("toggle_expand_headers", Key::Char('h')); - map.insert(MailView::DESCRIPTION.to_string(), our_map); + map.insert(MailView::DESCRIPTION, our_map); map } diff --git a/ui/src/components/mail/view/thread.rs b/ui/src/components/mail/view/thread.rs index 9b398a2a..9da65e75 100644 --- a/ui/src/components/mail/view/thread.rs +++ b/ui/src/components/mail/view/thread.rs @@ -1074,7 +1074,7 @@ impl Component for ThreadView { let config_map = context.settings.shortcuts.compact_listing.key_values(); map.insert( - ThreadView::DESCRIPTION.to_string(), + ThreadView::DESCRIPTION, [ ("reverse thread order", Key::Ctrl('r')), ("toggle_mailview", Key::Char('p')), diff --git a/ui/src/components/utilities.rs b/ui/src/components/utilities.rs index e431b7c5..7a573f48 100644 --- a/ui/src/components/utilities.rs +++ b/ui/src/components/utilities.rs @@ -311,7 +311,6 @@ impl Pager { pub fn update_from_str(&mut self, text: &str, width: Option) { let lines: Vec = text.split_lines_reflow(self.reflow, width); - debug!(&lines); let height = lines.len() + 2; let width = width.unwrap_or_else(|| lines.iter().map(|l| l.len()).max().unwrap_or(0)); let ascii_drawing = self.content.ascii_drawing; @@ -616,23 +615,11 @@ impl Component for Pager { self.dirty = true; } fn get_shortcuts(&self, context: &Context) -> ShortcutMaps { - let config_map: FnvHashMap<&'static str, &Key> = + let config_map: FnvHashMap<&'static str, Key> = context.settings.shortcuts.pager.key_values(); - [( - Pager::DESCRIPTION.to_string(), - [ - ("scroll_up", (*config_map["scroll_up"]).clone()), - ("scroll_down", (*config_map["scroll_down"]).clone()), - ("page_up", (*config_map["page_up"]).clone()), - ("page_down", (*config_map["page_down"]).clone()), - ] - .iter() - .cloned() - .collect::(), - )] - .iter() - .cloned() - .collect() + let mut ret: ShortcutMaps = Default::default(); + ret.insert(Pager::DESCRIPTION, config_map); + ret } fn id(&self) -> ComponentId { @@ -1382,7 +1369,9 @@ impl Component for Tabbed { get_x(bottom_right!(area)).saturating_sub(3), ), ); - let children_maps = self.children[self.cursor_pos].get_shortcuts(context); + let mut children_maps = self.children[self.cursor_pos].get_shortcuts(context); + let our_map = self.get_shortcuts(context); + children_maps.extend(our_map.into_iter()); if children_maps.is_empty() { return; } @@ -1508,6 +1497,8 @@ impl Component for Tabbed { self.dirty = false; } fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool { + let our_shortcuts = self.get_shortcuts(context); + let shortcuts: &ShortcutMap = &our_shortcuts["general"]; match *event { UIEvent::Input(Key::Alt(no)) if no >= '1' && no <= '9' => { let no = no as usize - '1' as usize; @@ -1524,7 +1515,7 @@ impl Component for Tabbed { } return true; } - UIEvent::Input(Key::Char('T')) => { + UIEvent::Input(ref key) if *key == shortcuts["next_tab"] => { self.cursor_pos = (self.cursor_pos + 1) % self.children.len(); context .replies @@ -1664,6 +1655,12 @@ impl Component for Tabbed { self.id = id; } + fn get_shortcuts(&self, context: &Context) -> ShortcutMaps { + let mut map = ShortcutMaps::default(); + map.insert("general", context.settings.shortcuts.general.key_values()); + map + } + fn can_quit_cleanly(&mut self, context: &Context) -> bool { for (i, c) in self.children.iter_mut().enumerate() { if !c.can_quit_cleanly(context) { diff --git a/ui/src/conf/shortcuts.rs b/ui/src/conf/shortcuts.rs index 075065b1..c6c1a29d 100644 --- a/ui/src/conf/shortcuts.rs +++ b/ui/src/conf/shortcuts.rs @@ -4,9 +4,13 @@ use fnv::FnvHashMap; #[derive(Debug, Clone, Default, Serialize, Deserialize)] pub struct Shortcuts { + #[serde(flatten)] + pub general: GeneralShortcuts, #[serde(flatten)] pub listing: ListingShortcuts, #[serde(flatten)] + pub composing: ComposingShortcuts, + #[serde(flatten)] pub compact_listing: CompactListingShortcuts, #[serde(flatten)] pub contact_list: ContactListShortcuts, @@ -38,9 +42,9 @@ macro_rules! shortcut_key_values { } } /// Returns a hashmap of all shortcuts and their values - pub fn key_values(&self) -> FnvHashMap<&'static str, &Key> { + pub fn key_values(&self) -> FnvHashMap<&'static str, Key> { [ - $((stringify!($fname),&(self.$fname)),)* + $((stringify!($fname),(self.$fname).clone()),)* ].iter().cloned().collect() } } @@ -100,3 +104,17 @@ shortcut_key_values! { "pager", page_down: Key |> "Go to next pager page" |> Key::PageDown } } + +shortcut_key_values! { "general", + pub struct GeneralShortcuts { + next_tab: Key |> "Next tab." |> Key::Char('T'), + go_to_tab: Key |> "Go to the nth tab" |> Key::Alt('n') + } +} + +shortcut_key_values! { "composing", + pub struct ComposingShortcuts { + send_mail: Key |> "Deliver draft to mailer" |> Key::Char('s'), + edit_mail: Key |> "Edit mail." |> Key::Char('e') + } +}