aboutsummaryrefslogtreecommitdiff
path: root/src/app
diff options
context:
space:
mode:
Diffstat (limited to 'src/app')
-rw-r--r--src/app/impl_self.rs60
-rw-r--r--src/app/impl_view.rs38
2 files changed, 51 insertions, 47 deletions
diff --git a/src/app/impl_self.rs b/src/app/impl_self.rs
index 744f906..5cd9616 100644
--- a/src/app/impl_self.rs
+++ b/src/app/impl_self.rs
@@ -13,8 +13,7 @@ use notify::{watcher, RecursiveMode, Watcher};
13 13
14use crate::command::{Command, CommandLineError}; 14use crate::command::{Command, CommandLineError};
15use crate::habit::{Bit, Count, HabitWrapper, TrackEvent, ViewMode}; 15use crate::habit::{Bit, Count, HabitWrapper, TrackEvent, ViewMode};
16use crate::utils; 16use crate::utils::{self, GRID_WIDTH, VIEW_HEIGHT, VIEW_WIDTH};
17use crate::CONFIGURATION;
18 17
19use crate::app::{App, MessageKind, StatusLine}; 18use crate::app::{App, MessageKind, StatusLine};
20 19
@@ -37,6 +36,10 @@ impl App {
37 self.habits.push(h); 36 self.habits.push(h);
38 } 37 }
39 38
39 pub fn list_habits(&self) -> Vec<String> {
40 self.habits.iter().map(|x| x.name()).collect::<Vec<_>>()
41 }
42
40 pub fn delete_by_name(&mut self, name: &str) { 43 pub fn delete_by_name(&mut self, name: &str) {
41 let old_len = self.habits.len(); 44 let old_len = self.habits.len();
42 self.habits.retain(|h| h.name() != name); 45 self.habits.retain(|h| h.name() != name);
@@ -83,7 +86,6 @@ impl App {
83 } 86 }
84 87
85 pub fn set_focus(&mut self, d: Absolute) { 88 pub fn set_focus(&mut self, d: Absolute) {
86 let grid_width = CONFIGURATION.grid_width;
87 match d { 89 match d {
88 Absolute::Right => { 90 Absolute::Right => {
89 if self.focus != self.habits.len() - 1 { 91 if self.focus != self.habits.len() - 1 {
@@ -96,15 +98,15 @@ impl App {
96 } 98 }
97 } 99 }
98 Absolute::Down => { 100 Absolute::Down => {
99 if self.focus + grid_width < self.habits.len() - 1 { 101 if self.focus + GRID_WIDTH < self.habits.len() - 1 {
100 self.focus += grid_width; 102 self.focus += GRID_WIDTH;
101 } else { 103 } else {
102 self.focus = self.habits.len() - 1; 104 self.focus = self.habits.len() - 1;
103 } 105 }
104 } 106 }
105 Absolute::Up => { 107 Absolute::Up => {
106 if self.focus as isize - grid_width as isize >= 0 { 108 if self.focus as isize - GRID_WIDTH as isize >= 0 {
107 self.focus -= grid_width; 109 self.focus -= GRID_WIDTH;
108 } else { 110 } else {
109 self.focus = 0; 111 self.focus = 0;
110 } 112 }
@@ -118,13 +120,13 @@ impl App {
118 } 120 }
119 121
120 pub fn status(&self) -> StatusLine { 122 pub fn status(&self) -> StatusLine {
121 let today = chrono::Local::now().naive_utc().date(); 123 let today = chrono::Local::now().naive_local().date();
122 let remaining = self.habits.iter().map(|h| h.remaining(today)).sum::<u32>(); 124 let remaining = self.habits.iter().map(|h| h.remaining(today)).sum::<u32>();
123 let total = self.habits.iter().map(|h| h.goal()).sum::<u32>(); 125 let total = self.habits.iter().map(|h| h.goal()).sum::<u32>();
124 let completed = total - remaining; 126 let completed = total - remaining;
125 127
126 let timestamp = if self.view_month_offset == 0 { 128 let timestamp = if self.view_month_offset == 0 {
127 format!("{}", Local::now().date().format("%d/%b/%y"),) 129 format!("{}", Local::now().naive_local().date().format("%d/%b/%y"),)
128 } else { 130 } else {
129 let months = self.view_month_offset; 131 let months = self.view_month_offset;
130 format!("{}", format!("{} months ago", months),) 132 format!("{}", format!("{} months ago", months),)
@@ -142,12 +144,10 @@ impl App {
142 } 144 }
143 145
144 pub fn max_size(&self) -> Vec2 { 146 pub fn max_size(&self) -> Vec2 {
145 let grid_width = CONFIGURATION.grid_width; 147 let width = GRID_WIDTH * VIEW_WIDTH;
146 let width = grid_width * CONFIGURATION.view_width;
147 let height = { 148 let height = {
148 if !self.habits.is_empty() { 149 if !self.habits.is_empty() {
149 (CONFIGURATION.view_height as f64 150 (VIEW_HEIGHT as f64 * (self.habits.len() as f64 / GRID_WIDTH as f64).ceil())
150 * (self.habits.len() as f64 / grid_width as f64).ceil())
151 as usize 151 as usize
152 } else { 152 } else {
153 0 153 0
@@ -207,12 +207,18 @@ impl App {
207 .iter_mut() 207 .iter_mut()
208 .find(|x| x.name() == name && x.is_auto()); 208 .find(|x| x.name() == name && x.is_auto());
209 if let Some(h) = target_habit { 209 if let Some(h) = target_habit {
210 h.modify(Local::now().naive_utc().date(), event); 210 h.modify(Local::now().naive_local().date(), event);
211 } 211 }
212 }; 212 };
213 match result { 213 match result {
214 Ok(c) => match c { 214 Ok(c) => match c {
215 Command::Add(name, goal, auto) => { 215 Command::Add(name, goal, auto) => {
216 if let Some(_) = self.habits.iter().find(|x| x.name() == name) {
217 self.message.set_kind(MessageKind::Error);
218 self.message
219 .set_message(format!("Habit `{}` already exist", &name));
220 return;
221 }
216 let kind = if goal == Some(1) { "bit" } else { "count" }; 222 let kind = if goal == Some(1) { "bit" } else { "count" };
217 if kind == "count" { 223 if kind == "count" {
218 self.add_habit(Box::new(Count::new(name, goal.unwrap_or(0), auto))); 224 self.add_habit(Box::new(Count::new(name, goal.unwrap_or(0), auto)));
@@ -230,7 +236,31 @@ impl App {
230 Command::TrackDown(name) => { 236 Command::TrackDown(name) => {
231 _track(&name, TrackEvent::Decrement); 237 _track(&name, TrackEvent::Decrement);
232 } 238 }
233 Command::Quit => self.save_state(), 239 Command::Help(input) => {
240 if let Some(topic) = input.as_ref().map(String::as_ref) {
241 self.message.set_message(
242 match topic {
243 "a" | "add" => "add <habit-name> [goal] (alias: a)",
244 "aa" | "add-auto" => "add-auto <habit-name> [goal] (alias: aa)",
245 "d" | "delete" => "delete <habit-name> (alias: d)",
246 "mprev" | "month-prev" => "month-prev (alias: mprev)",
247 "mnext" | "month-next" => "month-next (alias: mnext)",
248 "tup" | "track-up" => "track-up <auto-habit-name> (alias: tup)",
249 "tdown" | "track-down" => "track-down <auto-habit-name> (alias: tdown)",
250 "q" | "quit" => "quit dijo",
251 "w" | "write" => "write current state to disk (alias: w)",
252 "h"|"?" | "help" => "help [<command>|commands|keys] (aliases: h, ?)",
253 "cmds" | "commands" => "add, add-auto, delete, month-{prev,next}, track-{up,down}, help, quit",
254 "keys" => "TODO", // TODO (view?)
255 _ => "unknown command or help topic.",
256 }
257 )
258 } else {
259 // TODO (view?)
260 self.message.set_message("help <command>|commands|keys")
261 }
262 }
263 Command::Quit | Command::Write => self.save_state(),
234 Command::MonthNext => self.sift_forward(), 264 Command::MonthNext => self.sift_forward(),
235 Command::MonthPrev => self.sift_backward(), 265 Command::MonthPrev => self.sift_backward(),
236 Command::Blank => {} 266 Command::Blank => {}
diff --git a/src/app/impl_view.rs b/src/app/impl_view.rs
index 892b00c..395cac4 100644
--- a/src/app/impl_view.rs
+++ b/src/app/impl_view.rs
@@ -12,21 +12,17 @@ use notify::DebouncedEvent;
12 12
13use crate::app::{App, MessageKind}; 13use crate::app::{App, MessageKind};
14use crate::habit::{HabitWrapper, ViewMode}; 14use crate::habit::{HabitWrapper, ViewMode};
15use crate::utils; 15use crate::utils::{self, GRID_WIDTH, VIEW_HEIGHT, VIEW_WIDTH};
16use crate::CONFIGURATION;
17 16
18impl View for App { 17impl View for App {
19 fn draw(&self, printer: &Printer) { 18 fn draw(&self, printer: &Printer) {
20 let grid_width = CONFIGURATION.grid_width;
21 let view_width = CONFIGURATION.view_width;
22 let view_height = CONFIGURATION.view_height;
23 let mut offset = Vec2::zero(); 19 let mut offset = Vec2::zero();
24 for (idx, habit) in self.habits.iter().enumerate() { 20 for (idx, habit) in self.habits.iter().enumerate() {
25 if idx >= grid_width && idx % grid_width == 0 { 21 if idx >= GRID_WIDTH && idx % GRID_WIDTH == 0 {
26 offset = offset.map_y(|y| y + view_height).map_x(|_| 0); 22 offset = offset.map_y(|y| y + VIEW_HEIGHT).map_x(|_| 0);
27 } 23 }
28 habit.draw(&printer.offset(offset).focused(self.focus == idx)); 24 habit.draw(&printer.offset(offset).focused(self.focus == idx));
29 offset = offset.map_x(|x| x + view_width + 2); 25 offset = offset.map_x(|x| x + VIEW_WIDTH + 2);
30 } 26 }
31 27
32 offset = offset.map_x(|_| 0).map_y(|_| self.max_size().y - 2); 28 offset = offset.map_x(|_| 0).map_y(|_| self.max_size().y - 2);
@@ -45,13 +41,10 @@ impl View for App {
45 } 41 }
46 42
47 fn required_size(&mut self, _: Vec2) -> Vec2 { 43 fn required_size(&mut self, _: Vec2) -> Vec2 {
48 let grid_width = CONFIGURATION.grid_width; 44 let width = GRID_WIDTH * (VIEW_WIDTH + 2);
49 let view_width = CONFIGURATION.view_width;
50 let view_height = CONFIGURATION.view_height;
51 let width = grid_width * (view_width + 2);
52 let height = { 45 let height = {
53 if self.habits.len() > 0 { 46 if self.habits.len() > 0 {
54 (view_height as f64 * (self.habits.len() as f64 / grid_width as f64).ceil()) 47 (VIEW_HEIGHT as f64 * (self.habits.len() as f64 / GRID_WIDTH as f64).ceil())
55 as usize 48 as usize
56 } else { 49 } else {
57 0 50 0
@@ -102,25 +95,6 @@ impl View for App {
102 self.set_focus(Absolute::Down); 95 self.set_focus(Absolute::Down);
103 return EventResult::Consumed(None); 96 return EventResult::Consumed(None);
104 } 97 }
105 Event::Char('d') => {
106 if self.habits.is_empty() {
107 return EventResult::Consumed(None);
108 }
109 self.habits.remove(self.focus);
110 self.focus = self.focus.checked_sub(1).unwrap_or(0);
111 return EventResult::Consumed(None);
112 }
113 Event::Char('w') => {
114 // helper bind to test write to file
115 let j = serde_json::to_string_pretty(&self.habits).unwrap();
116 let mut file = File::create("foo.txt").unwrap();
117 file.write_all(j.as_bytes()).unwrap();
118 return EventResult::Consumed(None);
119 }
120 Event::Char('q') => {
121 self.save_state();
122 return EventResult::with_cb(|s| s.quit());
123 }
124 Event::Char('v') => { 98 Event::Char('v') => {
125 if self.habits.is_empty() { 99 if self.habits.is_empty() {
126 return EventResult::Consumed(None); 100 return EventResult::Consumed(None);