diff options
Diffstat (limited to 'src/app')
-rw-r--r-- | src/app/impl_self.rs | 60 | ||||
-rw-r--r-- | src/app/impl_view.rs | 38 |
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 | ||
14 | use crate::command::{Command, CommandLineError}; | 14 | use crate::command::{Command, CommandLineError}; |
15 | use crate::habit::{Bit, Count, HabitWrapper, TrackEvent, ViewMode}; | 15 | use crate::habit::{Bit, Count, HabitWrapper, TrackEvent, ViewMode}; |
16 | use crate::utils; | 16 | use crate::utils::{self, GRID_WIDTH, VIEW_HEIGHT, VIEW_WIDTH}; |
17 | use crate::CONFIGURATION; | ||
18 | 17 | ||
19 | use crate::app::{App, MessageKind, StatusLine}; | 18 | use 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 | ||
13 | use crate::app::{App, MessageKind}; | 13 | use crate::app::{App, MessageKind}; |
14 | use crate::habit::{HabitWrapper, ViewMode}; | 14 | use crate::habit::{HabitWrapper, ViewMode}; |
15 | use crate::utils; | 15 | use crate::utils::{self, GRID_WIDTH, VIEW_HEIGHT, VIEW_WIDTH}; |
16 | use crate::CONFIGURATION; | ||
17 | 16 | ||
18 | impl View for App { | 17 | impl 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); |