diff options
author | Akshay <nerdy@peppe.rs> | 2020-07-18 14:35:59 +0530 |
---|---|---|
committer | Akshay <nerdy@peppe.rs> | 2020-07-18 14:35:59 +0530 |
commit | 7740a2ad558eb289e9d8c0b33fe43453942398e0 (patch) | |
tree | 7537d4b2aafe941ce1bca5b66c95e62d7a06f2f6 | |
parent | 3eace50dfb39317fb08e5a95d6126b787c567a17 (diff) |
refactor app.rs into module: app
-rw-r--r-- | src/app/impl_self.rs (renamed from src/app.rs) | 194 | ||||
-rw-r--r-- | src/app/impl_view.rs | 175 | ||||
-rw-r--r-- | src/app/mod.rs | 27 |
3 files changed, 210 insertions, 186 deletions
diff --git a/src/app.rs b/src/app/impl_self.rs index 52c7f5f..efed4e0 100644 --- a/src/app.rs +++ b/src/app/impl_self.rs @@ -3,38 +3,20 @@ use std::f64; use std::fs::{File, OpenOptions}; use std::io::prelude::*; use std::path::PathBuf; -use std::sync::mpsc::{channel, Receiver}; +use std::sync::mpsc::channel; use std::time::Duration; use chrono::Local; -use cursive::direction::{Absolute, Direction}; -use cursive::event::{Event, EventResult, Key}; -use cursive::view::View; -use cursive::{Printer, Vec2}; -use notify::{watcher, DebouncedEvent, INotifyWatcher, RecursiveMode, Watcher}; +use cursive::direction::Absolute; +use cursive::Vec2; +use notify::{watcher, RecursiveMode, Watcher}; use crate::habit::{Bit, Count, HabitWrapper, TrackEvent, ViewMode}; use crate::utils; use crate::Command; use crate::CONFIGURATION; -struct StatusLine(String, String); - -pub struct App { - // holds app data - habits: Vec<Box<dyn HabitWrapper>>, - - _file_watcher: INotifyWatcher, - file_event_recv: Receiver<DebouncedEvent>, - focus: usize, - view_month_offset: u32, -} - -impl Default for App { - fn default() -> Self { - App::new() - } -} +use crate::app::{App, StatusLine}; impl App { pub fn new() -> Self { @@ -98,7 +80,7 @@ impl App { } } - fn set_focus(&mut self, d: Absolute) { + pub fn set_focus(&mut self, d: Absolute) { let grid_width = CONFIGURATION.grid_width; match d { Absolute::Right => { @@ -129,7 +111,7 @@ impl App { } } - fn status(&self) -> StatusLine { + pub fn status(&self) -> StatusLine { let today = chrono::Local::now().naive_utc().date(); let remaining = self.habits.iter().map(|h| h.remaining(today)).sum::<u32>(); let total = self.habits.iter().map(|h| h.goal()).sum::<u32>(); @@ -153,7 +135,7 @@ impl App { } } - fn max_size(&self) -> Vec2 { + pub fn max_size(&self) -> Vec2 { let grid_width = CONFIGURATION.grid_width; let width = { if self.habits.len() > 0 { @@ -263,163 +245,3 @@ impl App { } } } - -impl View for App { - fn draw(&self, printer: &Printer) { - let grid_width = CONFIGURATION.grid_width; - let view_width = CONFIGURATION.view_width; - let view_height = CONFIGURATION.view_height; - let mut offset = Vec2::zero(); - for (idx, i) in self.habits.iter().enumerate() { - if idx >= grid_width && idx % grid_width == 0 { - offset = offset.map_y(|y| y + view_height).map_x(|_| 0); - } - i.draw(&printer.offset(offset).focused(self.focus == idx)); - offset = offset.map_x(|x| x + view_width + 2); - } - - offset = offset.map_x(|_| 0).map_y(|_| self.max_size().y - 2); - - let status = self.status(); - printer.print(offset, &status.0); // left status - - let full = self.max_size().x; - offset = offset.map_x(|_| full - status.1.len()); - printer.print(offset, &status.1); // right status - } - - fn required_size(&mut self, _: Vec2) -> Vec2 { - let grid_width = CONFIGURATION.grid_width; - let view_width = CONFIGURATION.view_width; - let view_height = CONFIGURATION.view_height; - let width = { - if self.habits.len() > 0 { - grid_width * (view_width + 2) - } else { - 0 - } - }; - let height = { - if self.habits.len() > 0 { - (view_height as f64 * (self.habits.len() as f64 / grid_width as f64).ceil()) - as usize - + 2 // to acoomodate statusline and commandline - } else { - 0 - } - }; - Vec2::new(width, height) - } - - fn take_focus(&mut self, _: Direction) -> bool { - false - } - - fn on_event(&mut self, e: Event) -> EventResult { - match self.file_event_recv.try_recv() { - Ok(DebouncedEvent::Write(_)) => { - let read_from_file = |file: PathBuf| -> Vec<Box<dyn HabitWrapper>> { - if let Ok(ref mut f) = File::open(file) { - let mut j = String::new(); - f.read_to_string(&mut j); - return serde_json::from_str(&j).unwrap(); - } else { - return Vec::new(); - } - }; - let auto = read_from_file(utils::auto_habit_file()); - self.habits.retain(|x| !x.is_auto()); - self.habits.extend(auto); - } - _ => {} - }; - match e { - Event::Key(Key::Right) | Event::Key(Key::Tab) | Event::Char('l') => { - self.set_focus(Absolute::Right); - return EventResult::Consumed(None); - } - Event::Key(Key::Left) | Event::Shift(Key::Tab) | Event::Char('h') => { - self.set_focus(Absolute::Left); - return EventResult::Consumed(None); - } - Event::Key(Key::Up) | Event::Char('k') => { - self.set_focus(Absolute::Up); - return EventResult::Consumed(None); - } - Event::Key(Key::Down) | Event::Char('j') => { - self.set_focus(Absolute::Down); - return EventResult::Consumed(None); - } - Event::Char('d') => { - if self.habits.is_empty() { - return EventResult::Consumed(None); - } - self.habits.remove(self.focus); - self.focus = self.focus.checked_sub(1).unwrap_or(0); - return EventResult::Consumed(None); - } - Event::Char('w') => { - // helper bind to test write to file - let j = serde_json::to_string_pretty(&self.habits).unwrap(); - let mut file = File::create("foo.txt").unwrap(); - file.write_all(j.as_bytes()).unwrap(); - return EventResult::Consumed(None); - } - Event::Char('q') => { - self.save_state(); - return EventResult::with_cb(|s| s.quit()); - } - Event::Char('v') => { - if self.habits.is_empty() { - return EventResult::Consumed(None); - } - if self.habits[self.focus].view_mode() == ViewMode::Week { - self.set_mode(ViewMode::Day) - } else { - self.set_mode(ViewMode::Week) - } - return EventResult::Consumed(None); - } - Event::Char('V') => { - for habit in self.habits.iter_mut() { - habit.set_view_mode(ViewMode::Week); - } - return EventResult::Consumed(None); - } - Event::Key(Key::Esc) => { - for habit in self.habits.iter_mut() { - habit.set_view_mode(ViewMode::Day); - } - return EventResult::Consumed(None); - } - - /* We want sifting to be an app level function, - * that later trickles down into each habit - * */ - Event::Char(']') => { - self.sift_forward(); - return EventResult::Consumed(None); - } - Event::Char('[') => { - self.sift_backward(); - return EventResult::Consumed(None); - } - Event::Char('}') => { - self.set_view_month_offset(0); - return EventResult::Consumed(None); - } - - /* Every keybind that is not caught by App trickles - * down to the focused Habit We sift back to today - * before performing any action, "refocusing" the cursor - * */ - _ => { - if self.habits.is_empty() { - return EventResult::Ignored; - } - self.set_view_month_offset(0); - self.habits[self.focus].on_event(e) - } - } - } -} diff --git a/src/app/impl_view.rs b/src/app/impl_view.rs new file mode 100644 index 0000000..904403b --- /dev/null +++ b/src/app/impl_view.rs @@ -0,0 +1,175 @@ +use std::f64; +use std::fs::File; +use std::io::prelude::*; +use std::path::PathBuf; + +use cursive::direction::{Absolute, Direction}; +use cursive::event::{Event, EventResult, Key}; +use cursive::view::View; +use cursive::{Printer, Vec2}; +use notify::DebouncedEvent; + +use crate::app::App; +use crate::habit::{HabitWrapper, ViewMode}; +use crate::utils; +use crate::CONFIGURATION; + +impl View for App { + fn draw(&self, printer: &Printer) { + let grid_width = CONFIGURATION.grid_width; + let view_width = CONFIGURATION.view_width; + let view_height = CONFIGURATION.view_height; + let mut offset = Vec2::zero(); + for (idx, habit) in self.habits.iter().enumerate() { + if idx >= grid_width && idx % grid_width == 0 { + offset = offset.map_y(|y| y + view_height).map_x(|_| 0); + } + habit.draw(&printer.offset(offset).focused(self.focus == idx)); + offset = offset.map_x(|x| x + view_width + 2); + } + + offset = offset.map_x(|_| 0).map_y(|_| self.max_size().y - 2); + + let status = self.status(); + printer.print(offset, &status.0); // left status + + let full = self.max_size().x; + offset = offset.map_x(|_| full - status.1.len()); + printer.print(offset, &status.1); // right status + } + + fn required_size(&mut self, _: Vec2) -> Vec2 { + let grid_width = CONFIGURATION.grid_width; + let view_width = CONFIGURATION.view_width; + let view_height = CONFIGURATION.view_height; + let width = { + if self.habits.len() > 0 { + grid_width * (view_width + 2) + } else { + 0 + } + }; + let height = { + if self.habits.len() > 0 { + (view_height as f64 * (self.habits.len() as f64 / grid_width as f64).ceil()) + as usize + + 2 // to acoomodate statusline and message line + } else { + 0 + } + }; + Vec2::new(width, height) + } + + fn take_focus(&mut self, _: Direction) -> bool { + false + } + + fn on_event(&mut self, e: Event) -> EventResult { + match self.file_event_recv.try_recv() { + Ok(DebouncedEvent::Write(_)) => { + let read_from_file = |file: PathBuf| -> Vec<Box<dyn HabitWrapper>> { + if let Ok(ref mut f) = File::open(file) { + let mut j = String::new(); + f.read_to_string(&mut j); + return serde_json::from_str(&j).unwrap(); + } else { + return Vec::new(); + } + }; + let auto = read_from_file(utils::auto_habit_file()); + self.habits.retain(|x| !x.is_auto()); + self.habits.extend(auto); + } + _ => {} + }; + match e { + Event::Key(Key::Right) | Event::Key(Key::Tab) | Event::Char('l') => { + self.set_focus(Absolute::Right); + return EventResult::Consumed(None); + } + Event::Key(Key::Left) | Event::Shift(Key::Tab) | Event::Char('h') => { + self.set_focus(Absolute::Left); + return EventResult::Consumed(None); + } + Event::Key(Key::Up) | Event::Char('k') => { + self.set_focus(Absolute::Up); + return EventResult::Consumed(None); + } + Event::Key(Key::Down) | Event::Char('j') => { + self.set_focus(Absolute::Down); + return EventResult::Consumed(None); + } + Event::Char('d') => { + if self.habits.is_empty() { + return EventResult::Consumed(None); + } + self.habits.remove(self.focus); + self.focus = self.focus.checked_sub(1).unwrap_or(0); + return EventResult::Consumed(None); + } + Event::Char('w') => { + // helper bind to test write to file + let j = serde_json::to_string_pretty(&self.habits).unwrap(); + let mut file = File::create("foo.txt").unwrap(); + file.write_all(j.as_bytes()).unwrap(); + return EventResult::Consumed(None); + } + Event::Char('q') => { + self.save_state(); + return EventResult::with_cb(|s| s.quit()); + } + Event::Char('v') => { + if self.habits.is_empty() { + return EventResult::Consumed(None); + } + if self.habits[self.focus].view_mode() == ViewMode::Week { + self.set_mode(ViewMode::Day) + } else { + self.set_mode(ViewMode::Week) + } + return EventResult::Consumed(None); + } + Event::Char('V') => { + for habit in self.habits.iter_mut() { + habit.set_view_mode(ViewMode::Week); + } + return EventResult::Consumed(None); + } + Event::Key(Key::Esc) => { + for habit in self.habits.iter_mut() { + habit.set_view_mode(ViewMode::Day); + } + return EventResult::Consumed(None); + } + + /* We want sifting to be an app level function, + * that later trickles down into each habit + * */ + Event::Char(']') => { + self.sift_forward(); + return EventResult::Consumed(None); + } + Event::Char('[') => { + self.sift_backward(); + return EventResult::Consumed(None); + } + Event::Char('}') => { + self.set_view_month_offset(0); + return EventResult::Consumed(None); + } + + /* Every keybind that is not caught by App trickles + * down to the focused habit. We sift back to today + * before performing any action, "refocusing" the cursor + * */ + _ => { + if self.habits.is_empty() { + return EventResult::Ignored; + } + self.set_view_month_offset(0); + self.habits[self.focus].on_event(e) + } + } + } +} diff --git a/src/app/mod.rs b/src/app/mod.rs new file mode 100644 index 0000000..f00c936 --- /dev/null +++ b/src/app/mod.rs @@ -0,0 +1,27 @@ +use std::default::Default; +use std::sync::mpsc::Receiver; + +use notify::{DebouncedEvent, INotifyWatcher}; + +use crate::habit::HabitWrapper; + +mod impl_self; +mod impl_view; + +pub struct StatusLine(String, String); + +pub struct App { + // holds app data + habits: Vec<Box<dyn HabitWrapper>>, + + _file_watcher: INotifyWatcher, + file_event_recv: Receiver<DebouncedEvent>, + focus: usize, + view_month_offset: u32, +} + +impl Default for App { + fn default() -> Self { + App::new() + } +} |