diff options
author | Matthias Beyer <mail@beyermatthias.de> | 2021-01-17 17:05:52 +0100 |
---|---|---|
committer | Matthias Beyer <mail@beyermatthias.de> | 2021-01-17 17:05:52 +0100 |
commit | ae02b290903bf078d404984b9bf0feda801b7d56 (patch) | |
tree | e06739dde40e20e27f6e1144a32f1c58bba7b1ef | |
parent | f94a92027b6d59cdbc880f4d81a0faa4b48b38e5 (diff) | |
parent | 50c4ab8defaac96174e9d5782b032091c6f37a36 (diff) |
Merge branch 'bindings'
-rw-r--r-- | src/bindings.rs | 154 | ||||
-rw-r--r-- | src/main.rs | 1 | ||||
-rw-r--r-- | src/main_view.rs | 78 |
3 files changed, 201 insertions, 32 deletions
diff --git a/src/bindings.rs b/src/bindings.rs new file mode 100644 index 0000000..9907bdc --- /dev/null +++ b/src/bindings.rs @@ -0,0 +1,154 @@ +use anyhow::Result; +use cursive::Cursive; +use cursive::Printer; +use cursive::Rect; +use cursive::View; +use cursive::XY; +use cursive::direction::Direction; +use cursive::event::Callback; +use cursive::event::Event; +use cursive::event::EventResult; +use cursive::event::Key; +use cursive::view::Nameable; +use cursive::view::Selector; +use cursive::view::SizeConstraint; +use cursive::views::NamedView; +use cursive::views::ResizedView; + +use crate::main_view::MainView; + +pub fn get_bindings() -> Bindings { + Bindings(vec![ + Binding { + chars: ["quit", "q"].iter().map(ToString::to_string).collect(), + callback: Callback::from_fn(|siv: &mut Cursive| { + trace!("Callback called: q"); + let continue_running = siv.call_on_name(crate::main_view::MAIN_VIEW_NAME, |mv: &mut MainView| { + if mv.tabs().tab_order().len() == 1 { + false + } else { + if let Some(key) = mv.tabs().active_tab().cloned() { + debug!("Removing tab: {}", key); + if let Err(e) = mv.tabs_mut().remove_tab(&key) { + error!("{:?}", e); // TODO do more than just logging + } + debug!("Remove tab"); + } else { + debug!("No tab to remove found."); + } + true + } + }) + .unwrap_or(true); + + if !continue_running { + debug!("Byebye"); + siv.quit(); + } + }) + }, + + Binding { + chars: ["open", "o"].iter().map(ToString::to_string).collect(), + callback: Callback::from_fn(MainView::add_notmuch_query_layer), + } + ]) +} + +pub const BINDINGS_CALLER: &str = "BINDINGS_CALLER"; + +#[derive(Clone)] +pub struct Bindings(Vec<Binding>); + +impl Bindings { + pub fn new(bs: Vec<Binding>) -> Bindings { + Bindings(bs) + } + + pub fn caller(&self) -> BindingCaller { + BindingCaller::new(self.clone()) + } +} + +#[derive(Clone)] +pub struct Binding { + chars: Vec<String>, + callback: Callback, +} + +impl Binding { + pub fn for_events(chars: Vec<String>, callback: Callback) -> Binding { + Binding { chars, callback } + } +} + +pub struct BindingCaller { + configuration: Bindings, + state: Vec<char> +} + +impl BindingCaller { + pub fn new(configuration: Bindings) -> Self { + BindingCaller { configuration, state: Vec::new() } + } + + pub fn process(&mut self, chr: char) { + debug!("Char = {}", chr); + self.state.push(chr); + } + + pub fn finalize(&self) -> Option<Callback> { + self.configuration + .0 + .iter() + .find(|binding| { + trace!("chars {:?} == state {:?}", binding.chars, self.state); + let state_str = self.state.iter().collect::<String>(); + binding.chars.iter().any(|state| *state == state_str) + }) + .map(|binding| { + trace!("Binding found"); + binding.callback.clone() + }) + } + +} + +impl View for BindingCaller { + fn draw(&self, printer: &Printer) { + trace!("Drawing with offset = {:?}", printer.offset); + trace!("Drawing with output_size = {:?}", printer.output_size); + trace!("Drawing with size = {:?}", printer.size); + + { + let line = "-".repeat(printer.output_size.x); + let position = XY { + x: printer.offset.x, + y: printer.output_size.y - 3, + }; + printer.print(position, &line) + } + + { + let line = format!(":{}", self.state.iter().collect::<String>()); + let position = XY { + x: printer.offset.x, + y: printer.output_size.y - 2, + }; + printer.print(position, &line) + } + } + + fn on_event(&mut self, e: Event) -> EventResult { + match e { + Event::Key(Key::Enter) => EventResult::Consumed(self.finalize()), + Event::Char(chr) => { + self.process(chr); + EventResult::Consumed(None) + }, + _ => unimplemented!() + } + } + +} + diff --git a/src/main.rs b/src/main.rs index 596f2ae..906da9e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,6 +8,7 @@ use cursive::event::{Event, EventTrigger}; use cursive_flexi_logger_view::FlexiLoggerView; use flexi_logger::{Logger, LogTarget}; +mod bindings; mod main_view; mod mail_view; mod maillist_view; diff --git a/src/main_view.rs b/src/main_view.rs index 8425872..c0dcd08 100644 --- a/src/main_view.rs +++ b/src/main_view.rs @@ -16,24 +16,38 @@ use cursive::views::NamedView; use cursive::views::ResizedView; use cursive::traits::Resizable; use cursive_multiplex::Mux; +use cursive::view::SizeConstraint; +use cursive::event::Key; +use getset::{Getters, MutGetters}; use crate::configuration::Configuration; use crate::maillist_view::MaillistView; use crate::maillist_view::MailListingData; +use crate::bindings::Bindings; +use crate::bindings::BindingCaller; pub const MAIN_VIEW_NAME: &'static str = "main_view"; pub const MAIN_MUX_NAME: &'static str = "main_mux"; pub const MAIN_MAIL_LIST_NAME: &'static str = "main_mail_list"; +#[derive(Getters, MutGetters)] pub struct MainView { config: Configuration, muxroot: cursive_multiplex::Id, + + #[getset(get = "pub", get_mut = "pub")] tabs: cursive_tabs::TabPanel<String>, + + bindings: Bindings, + bindings_caller: Option<ResizedView<NamedView<BindingCaller>>>, } impl View for MainView { fn draw(&self, printer: &Printer) { - self.tabs.draw(printer) + self.tabs.draw(printer); + if let Some(caller) = self.bindings_caller.as_ref() { + caller.draw(printer); + } } fn layout(&mut self, xy: XY<usize>) { @@ -49,35 +63,34 @@ impl View for MainView { } fn on_event(&mut self, e: Event) -> EventResult { - match e { - Event::Char('q') => { - if self.tabs.tab_order().len() == 1 { - EventResult::Ignored - } else { - if let Some(key) = self.tabs.active_tab().cloned() { - debug!("Removing tab: {}", key); - if let Err(e) = self.tabs.remove_tab(&key) { - error!("{:?}", e); // TODO do more than just logging - } - debug!("Remove tab"); - EventResult::Consumed(None) - } else { - debug!("No tab to remove found."); - EventResult::Ignored - } - } - }, - - Event::Key(cursive::event::Key::Enter) => { - let muxroot = self.muxroot.clone(); - EventResult::Consumed(Some(Callback::from_fn(move |siv| Self::add_mailview(siv, muxroot)))) - }, - - Event::Char('o') => { - EventResult::Consumed(Some(Callback::from_fn(MainView::add_notmuch_query_layer))) + debug!("Received event: {:?}", e); + match self.bindings_caller.as_mut() { + Some(caller) => match e { + Event::Key(Key::Esc) => { + self.bindings_caller = None; + debug!("Escape. Resetting bindings caller."); + EventResult::Consumed(None) + }, + other => { + debug!("Forwarding event to bindings caller"); + caller.on_event(other) + }, }, - - other => self.tabs.on_event(other), + None => match e { + Event::Char(':') => { + debug!(": -> Constructing bindings caller."); + self.bindings_caller = Some({ + self.bindings.caller() + .with_name(crate::bindings::BINDINGS_CALLER) + .resized(SizeConstraint::Full, SizeConstraint::AtLeast(5)) + }); + EventResult::Consumed(None) + }, + other => { + debug!("Forwarding event to tabs"); + self.tabs.on_event(other) + }, + } } } @@ -105,7 +118,6 @@ impl View for MainView { impl MainView { pub fn new(config: Configuration) -> Result<NamedView<Self>> { - use cursive::view::SizeConstraint; let mut tab = cursive_multiplex::Mux::new(); let muxroot = tab.root().build().unwrap(); @@ -123,7 +135,9 @@ impl MainView { .with_bar_placement(cursive_tabs::Placement::HorizontalTop) .with_tab(config.notmuch_default_query().clone(), tab.with_name(MAIN_MUX_NAME)); - Ok(MainView { config, muxroot, tabs }.with_name(MAIN_VIEW_NAME)) + let bindings = crate::bindings::get_bindings(); + + Ok(MainView { config, muxroot, tabs, bindings, bindings_caller: None }.with_name(MAIN_VIEW_NAME)) } pub fn add_tab<T: View>(&mut self, id: String, view: T) { @@ -134,7 +148,7 @@ impl MainView { &self.config } - fn add_notmuch_query_layer(siv: &mut Cursive) { + pub fn add_notmuch_query_layer(siv: &mut Cursive) { use crate::util::dialog_for; use crate::util::error_dialog_for; |