summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Beyer <mail@beyermatthias.de>2021-01-17 17:05:52 +0100
committerMatthias Beyer <mail@beyermatthias.de>2021-01-17 17:05:52 +0100
commitae02b290903bf078d404984b9bf0feda801b7d56 (patch)
treee06739dde40e20e27f6e1144a32f1c58bba7b1ef
parentf94a92027b6d59cdbc880f4d81a0faa4b48b38e5 (diff)
parent50c4ab8defaac96174e9d5782b032091c6f37a36 (diff)
Merge branch 'bindings'
-rw-r--r--src/bindings.rs154
-rw-r--r--src/main.rs1
-rw-r--r--src/main_view.rs78
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;