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::view::Nameable; use cursive::view::Selector; use cursive::views::Dialog; use cursive::views::EditView; 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, bindings: Bindings, bindings_caller: Option>>, } impl View for MainView { fn draw(&self, printer: &Printer) { self.tabs.draw(printer); if let Some(caller) = self.bindings_caller.as_ref() { caller.draw(printer); } } fn layout(&mut self, xy: XY) { self.tabs.layout(xy) } fn needs_relayout(&self) -> bool { self.tabs.needs_relayout() } fn required_size(&mut self, constraint: XY) -> XY { self.tabs.required_size(constraint) } fn on_event(&mut self, e: Event) -> EventResult { 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) }, }, 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) }, } } } fn call_on_any<'a>(&mut self, s: &Selector, tpl: &'a mut (dyn FnMut(&mut (dyn View + 'static)) + 'a)) { self.tabs.call_on_any(s, tpl); } fn focus_view(&mut self, s: &Selector) -> Result<(), ()> { self.tabs.focus_view(s) } fn take_focus(&mut self, source: Direction) -> bool { self.tabs.take_focus(source) } fn important_area(&self, view_size: XY) -> Rect { self.tabs.important_area(view_size) } fn type_name(&self) -> &'static str { self.tabs.type_name() } } impl MainView { pub fn new(config: Configuration) -> Result> { let mut tab = cursive_multiplex::Mux::new(); let muxroot = tab.root().build().unwrap(); { let dbpath = config.notmuch_database_path(); let dbquery = config.notmuch_default_query(); let mlname = MAIN_MAIL_LIST_NAME.to_string(); let view = MaillistView::create_for(dbpath.to_path_buf(), dbquery, mlname)?; let _ = tab.add_right_of(view, muxroot); } let tabs = cursive_tabs::TabPanel::default() .with_bar_alignment(cursive_tabs::Align::Start) .with_bar_placement(cursive_tabs::Placement::HorizontalTop) .with_tab(config.notmuch_default_query().clone(), tab.with_name(MAIN_MUX_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(&mut self, id: String, view: T) { self.tabs.add_tab(id, view) } pub fn config(&self) -> &Configuration { &self.config } pub fn add_notmuch_query_layer(siv: &mut Cursive) { use crate::util::dialog_for; use crate::util::error_dialog_for; let edit_view = EditView::new() .on_submit(move |siv: &mut Cursive, query: &str| { siv.call_on_name(MAIN_VIEW_NAME, move |main_view: &mut MainView| { main_view.config().notmuch_database_path().clone() }) .map(|dbpath| { let t = MaillistView::create_for(dbpath.to_path_buf(), query, query.to_string())? .full_screen() .with_name(format!("{}-view", query)); siv.call_on_name(MAIN_VIEW_NAME, move |main_view: &mut MainView| { main_view.add_tab(query.to_string(), t); }); siv.pop_layer(); Ok(()) }) .unwrap_or_else(|| { siv.pop_layer(); siv.add_layer(dialog_for("Failed to get database connection set up")); Ok(()) }) .unwrap_or_else(|e: anyhow::Error| { siv.pop_layer(); siv.add_layer(error_dialog_for(e)) }); }) .with_name("query"); siv.add_layer({ Dialog::around(edit_view) .title("Query") .min_width(80) }) } fn add_mailview(siv: &mut Cursive, muxroot: cursive_multiplex::Id) { debug!("Creating mailview"); siv.call_on_name(MAIN_MAIL_LIST_NAME, |maillist_view: &mut MaillistView| { if let Some(ml_data) = maillist_view.item().and_then(move |idx| maillist_view.borrow_item(idx)) { debug!("Found item"); Ok(ml_data.clone()) } else { unimplemented!() } }) .map(|maillist_item: Result| { if let Ok(mldata) = maillist_item { let _ = siv.call_on_name(MAIN_MUX_NAME, |mux: &mut Mux| { debug!("Adding mux pane"); let _ = mux.add_right_of(crate::mailview::MailView::create_for(mldata), muxroot); // TODO handle error }); // TODO handle error. } else { // do something } Ok(()) }) .unwrap_or_else(|| { siv.add_layer(crate::util::dialog_for("Failed to find View")); Ok(()) }) .unwrap_or_else(|e: anyhow::Error| { siv.add_layer(crate::util::error_dialog_for(e)) }); } }