summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAkshay <nerdy@peppe.rs>2020-07-18 14:35:59 +0530
committerAkshay <nerdy@peppe.rs>2020-07-18 14:35:59 +0530
commit7740a2ad558eb289e9d8c0b33fe43453942398e0 (patch)
tree7537d4b2aafe941ce1bca5b66c95e62d7a06f2f6
parent3eace50dfb39317fb08e5a95d6126b787c567a17 (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.rs175
-rw-r--r--src/app/mod.rs27
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()
+ }
+}