aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/app/cursor.rs22
-rw-r--r--src/app/impl_self.rs41
-rw-r--r--src/app/impl_view.rs10
-rw-r--r--src/habit/bit.rs37
-rw-r--r--src/habit/count.rs37
-rw-r--r--src/habit/mod.rs27
-rw-r--r--src/habit/traits.rs61
-rw-r--r--src/views.rs8
8 files changed, 112 insertions, 131 deletions
diff --git a/src/app/cursor.rs b/src/app/cursor.rs
index ed6bd65..f76d591 100644
--- a/src/app/cursor.rs
+++ b/src/app/cursor.rs
@@ -16,7 +16,7 @@ impl Cursor {
16 0: Local::now().naive_local().date(), 16 0: Local::now().naive_local().date(),
17 } 17 }
18 } 18 }
19 pub fn do_move(&mut self, d: Absolute) { 19 pub fn small_seek(&mut self, d: Absolute) {
20 let today = Local::now().naive_local().date(); 20 let today = Local::now().naive_local().date();
21 let cursor = self.0; 21 let cursor = self.0;
22 match d { 22 match d {
@@ -48,4 +48,24 @@ impl Cursor {
48 Absolute::None => {} 48 Absolute::None => {}
49 } 49 }
50 } 50 }
51 fn long_seek(&mut self, offset: Duration) {
52 let cursor = self.0;
53 let today = Local::now().naive_local().date();
54 let next = cursor.checked_add_signed(offset).unwrap_or(cursor);
55
56 if next <= today {
57 self.0 = next;
58 } else {
59 self.0 = today;
60 }
61 }
62 pub fn month_forward(&mut self) {
63 self.long_seek(Duration::weeks(4));
64 }
65 pub fn month_backward(&mut self) {
66 self.long_seek(Duration::weeks(-4));
67 }
68 pub fn reset(&mut self) {
69 self.0 = Local::now().naive_local().date();
70 }
51} 71}
diff --git a/src/app/impl_self.rs b/src/app/impl_self.rs
index abf5209..fec7219 100644
--- a/src/app/impl_self.rs
+++ b/src/app/impl_self.rs
@@ -6,7 +6,7 @@ use std::path::PathBuf;
6use std::sync::mpsc::channel; 6use std::sync::mpsc::channel;
7use std::time::Duration; 7use std::time::Duration;
8 8
9use chrono::Local; 9use chrono::{Local, NaiveDate};
10use cursive::direction::Absolute; 10use cursive::direction::Absolute;
11use cursive::Vec2; 11use cursive::Vec2;
12use notify::{watcher, RecursiveMode, Watcher}; 12use notify::{watcher, RecursiveMode, Watcher};
@@ -54,42 +54,42 @@ impl App {
54 if self.habits.is_empty() { 54 if self.habits.is_empty() {
55 return ViewMode::Day; 55 return ViewMode::Day;
56 } 56 }
57 return self.habits[self.focus].view_mode(); 57 return self.habits[self.focus].inner_data_ref().view_mode();
58 } 58 }
59 59
60 pub fn set_mode(&mut self, mode: ViewMode) { 60 pub fn set_mode(&mut self, mode: ViewMode) {
61 if !self.habits.is_empty() { 61 if !self.habits.is_empty() {
62 self.habits[self.focus].set_view_mode(mode); 62 self.habits[self.focus]
63 .inner_data_mut_ref()
64 .set_view_mode(mode);
63 } 65 }
64 } 66 }
65 67
66 pub fn set_view_month_offset(&mut self, offset: u32) { 68 pub fn sift_backward(&mut self) {
67 self.view_month_offset = offset; 69 self.cursor.month_backward();
68 for v in self.habits.iter_mut() { 70 for v in self.habits.iter_mut() {
69 v.set_view_month_offset(offset); 71 v.inner_data_mut_ref().cursor.month_backward();
70 } 72 }
71 } 73 }
72 74
73 pub fn sift_backward(&mut self) { 75 pub fn sift_forward(&mut self) {
74 self.view_month_offset += 1; 76 self.cursor.month_forward();
75 for v in self.habits.iter_mut() { 77 for v in self.habits.iter_mut() {
76 v.set_view_month_offset(self.view_month_offset); 78 v.inner_data_mut_ref().cursor.month_forward();
77 } 79 }
78 } 80 }
79 81
80 pub fn sift_forward(&mut self) { 82 pub fn reset_cursor(&mut self) {
81 if self.view_month_offset > 0 { 83 self.cursor.reset();
82 self.view_month_offset -= 1; 84 for v in self.habits.iter_mut() {
83 for v in self.habits.iter_mut() { 85 v.inner_data_mut_ref().cursor.reset();
84 v.set_view_month_offset(self.view_month_offset);
85 }
86 } 86 }
87 } 87 }
88 88
89 pub fn move_cursor(&mut self, d: Absolute) { 89 pub fn move_cursor(&mut self, d: Absolute) {
90 self.cursor.do_move(d); 90 self.cursor.small_seek(d);
91 for v in self.habits.iter_mut() { 91 for v in self.habits.iter_mut() {
92 v.move_cursor(d); 92 v.inner_data_mut_ref().move_cursor(d);
93 } 93 }
94 } 94 }
95 95
@@ -133,11 +133,12 @@ impl App {
133 let total = self.habits.iter().map(|h| h.goal()).sum::<u32>(); 133 let total = self.habits.iter().map(|h| h.goal()).sum::<u32>();
134 let completed = total - remaining; 134 let completed = total - remaining;
135 135
136 let timestamp = if self.view_month_offset == 0 { 136 let timestamp = if self.cursor.0 == today {
137 format!("{}", Local::now().naive_local().date().format("%d/%b/%y"),) 137 format!("{}", Local::now().naive_local().date().format("%d/%b/%y"),)
138 } else { 138 } else {
139 let months = self.view_month_offset; 139 let since = NaiveDate::signed_duration_since(today, self.cursor.0).num_days();
140 format!("{}", format!("{} month(s) ago", months),) 140 let plural = if since == 1 { "" } else { "s" };
141 format!("{} ({} day{} ago)", self.cursor.0, since, plural)
141 }; 142 };
142 143
143 StatusLine { 144 StatusLine {
diff --git a/src/app/impl_view.rs b/src/app/impl_view.rs
index 0ec47f1..db05432 100644
--- a/src/app/impl_view.rs
+++ b/src/app/impl_view.rs
@@ -117,7 +117,7 @@ impl View for App {
117 if self.habits.is_empty() { 117 if self.habits.is_empty() {
118 return EventResult::Consumed(None); 118 return EventResult::Consumed(None);
119 } 119 }
120 if self.habits[self.focus].view_mode() == ViewMode::Week { 120 if self.habits[self.focus].inner_data_ref().view_mode() == ViewMode::Week {
121 self.set_mode(ViewMode::Day) 121 self.set_mode(ViewMode::Day)
122 } else { 122 } else {
123 self.set_mode(ViewMode::Week) 123 self.set_mode(ViewMode::Week)
@@ -126,13 +126,13 @@ impl View for App {
126 } 126 }
127 Event::Char('V') => { 127 Event::Char('V') => {
128 for habit in self.habits.iter_mut() { 128 for habit in self.habits.iter_mut() {
129 habit.set_view_mode(ViewMode::Week); 129 habit.inner_data_mut_ref().set_view_mode(ViewMode::Week);
130 } 130 }
131 return EventResult::Consumed(None); 131 return EventResult::Consumed(None);
132 } 132 }
133 Event::Key(Key::Esc) => { 133 Event::Key(Key::Esc) => {
134 for habit in self.habits.iter_mut() { 134 for habit in self.habits.iter_mut() {
135 habit.set_view_mode(ViewMode::Day); 135 habit.inner_data_mut_ref().set_view_mode(ViewMode::Day);
136 } 136 }
137 return EventResult::Consumed(None); 137 return EventResult::Consumed(None);
138 } 138 }
@@ -149,7 +149,7 @@ impl View for App {
149 return EventResult::Consumed(None); 149 return EventResult::Consumed(None);
150 } 150 }
151 Event::Char('}') => { 151 Event::Char('}') => {
152 self.set_view_month_offset(0); 152 self.reset_cursor();
153 return EventResult::Consumed(None); 153 return EventResult::Consumed(None);
154 } 154 }
155 Event::CtrlChar('l') => { 155 Event::CtrlChar('l') => {
@@ -166,7 +166,7 @@ impl View for App {
166 if self.habits.is_empty() { 166 if self.habits.is_empty() {
167 return EventResult::Ignored; 167 return EventResult::Ignored;
168 } 168 }
169 self.set_view_month_offset(0); 169 self.reset_cursor();
170 self.habits[self.focus].on_event(e) 170 self.habits[self.focus].on_event(e)
171 } 171 }
172 } 172 }
diff --git a/src/habit/bit.rs b/src/habit/bit.rs
index 7fe6fd9..da64ece 100644
--- a/src/habit/bit.rs
+++ b/src/habit/bit.rs
@@ -1,13 +1,12 @@
1use std::collections::HashMap; 1use std::collections::HashMap;
2use std::default::Default;
2 3
3use chrono::NaiveDate; 4use chrono::NaiveDate;
4use cursive::direction::Absolute;
5use serde::{Deserialize, Serialize}; 5use serde::{Deserialize, Serialize};
6 6
7use crate::app::Cursor;
8use crate::habit::prelude::default_auto; 7use crate::habit::prelude::default_auto;
9use crate::habit::traits::Habit; 8use crate::habit::traits::Habit;
10use crate::habit::{TrackEvent, ViewMode}; 9use crate::habit::{InnerData, TrackEvent};
11use crate::CONFIGURATION; 10use crate::CONFIGURATION;
12 11
13#[derive(Copy, Clone, Debug, Serialize, Deserialize)] 12#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
@@ -44,13 +43,7 @@ pub struct Bit {
44 auto: bool, 43 auto: bool,
45 44
46 #[serde(skip)] 45 #[serde(skip)]
47 view_month_offset: u32, 46 inner_data: InnerData,
48
49 #[serde(skip)]
50 cursor: Cursor,
51
52 #[serde(skip)]
53 view_mode: ViewMode,
54} 47}
55 48
56impl Bit { 49impl Bit {
@@ -60,9 +53,7 @@ impl Bit {
60 stats: HashMap::new(), 53 stats: HashMap::new(),
61 goal: CustomBool(true), 54 goal: CustomBool(true),
62 auto, 55 auto,
63 view_month_offset: 0, 56 inner_data: Default::default(),
64 cursor: Cursor::new(),
65 view_mode: ViewMode::Day,
66 }; 57 };
67 } 58 }
68} 59}
@@ -124,23 +115,11 @@ impl Habit for Bit {
124 } 115 }
125 } 116 }
126 } 117 }
127 fn set_view_month_offset(&mut self, offset: u32) { 118 fn inner_data_ref(&self) -> &InnerData {
128 self.view_month_offset = offset; 119 &self.inner_data
129 }
130 fn view_month_offset(&self) -> u32 {
131 self.view_month_offset
132 }
133 fn move_cursor(&mut self, d: Absolute) {
134 self.cursor.do_move(d);
135 }
136 fn cursor(&self) -> Cursor {
137 self.cursor
138 }
139 fn set_view_mode(&mut self, mode: ViewMode) {
140 self.view_mode = mode;
141 } 120 }
142 fn view_mode(&self) -> ViewMode { 121 fn inner_data_mut_ref(&mut self) -> &mut InnerData {
143 self.view_mode 122 &mut self.inner_data
144 } 123 }
145 fn is_auto(&self) -> bool { 124 fn is_auto(&self) -> bool {
146 self.auto 125 self.auto
diff --git a/src/habit/count.rs b/src/habit/count.rs
index b14354c..09fd399 100644
--- a/src/habit/count.rs
+++ b/src/habit/count.rs
@@ -1,13 +1,12 @@
1use std::collections::HashMap; 1use std::collections::HashMap;
2use std::default::Default;
2 3
3use chrono::NaiveDate; 4use chrono::NaiveDate;
4use cursive::direction::Absolute;
5use serde::{Deserialize, Serialize}; 5use serde::{Deserialize, Serialize};
6 6
7use crate::app::Cursor;
8use crate::habit::prelude::default_auto; 7use crate::habit::prelude::default_auto;
9use crate::habit::traits::Habit; 8use crate::habit::traits::Habit;
10use crate::habit::{TrackEvent, ViewMode}; 9use crate::habit::{InnerData, TrackEvent};
11 10
12#[derive(Debug, Serialize, Deserialize)] 11#[derive(Debug, Serialize, Deserialize)]
13pub struct Count { 12pub struct Count {
@@ -19,13 +18,7 @@ pub struct Count {
19 auto: bool, 18 auto: bool,
20 19
21 #[serde(skip)] 20 #[serde(skip)]
22 view_month_offset: u32, 21 inner_data: InnerData,
23
24 #[serde(skip)]
25 cursor: Cursor,
26
27 #[serde(skip)]
28 view_mode: ViewMode,
29} 22}
30 23
31impl Count { 24impl Count {
@@ -35,9 +28,7 @@ impl Count {
35 stats: HashMap::new(), 28 stats: HashMap::new(),
36 goal, 29 goal,
37 auto, 30 auto,
38 view_month_offset: 0, 31 inner_data: Default::default(),
39 cursor: Cursor::new(),
40 view_mode: ViewMode::Day,
41 }; 32 };
42 } 33 }
43} 34}
@@ -101,23 +92,11 @@ impl Habit for Count {
101 }; 92 };
102 } 93 }
103 } 94 }
104 fn set_view_month_offset(&mut self, offset: u32) { 95 fn inner_data_ref(&self) -> &InnerData {
105 self.view_month_offset = offset; 96 &self.inner_data
106 }
107 fn view_month_offset(&self) -> u32 {
108 self.view_month_offset
109 }
110 fn move_cursor(&mut self, d: Absolute) {
111 self.cursor.do_move(d);
112 }
113 fn cursor(&self) -> Cursor {
114 self.cursor
115 }
116 fn set_view_mode(&mut self, mode: ViewMode) {
117 self.view_mode = mode;
118 } 97 }
119 fn view_mode(&self) -> ViewMode { 98 fn inner_data_mut_ref(&mut self) -> &mut InnerData {
120 self.view_mode 99 &mut self.inner_data
121 } 100 }
122 fn is_auto(&self) -> bool { 101 fn is_auto(&self) -> bool {
123 self.auto 102 self.auto
diff --git a/src/habit/mod.rs b/src/habit/mod.rs
index 75e734a..d51abe5 100644
--- a/src/habit/mod.rs
+++ b/src/habit/mod.rs
@@ -1,3 +1,5 @@
1use std::default::Default;
2
1mod traits; 3mod traits;
2pub use traits::{Habit, HabitWrapper}; 4pub use traits::{Habit, HabitWrapper};
3 5
@@ -9,3 +11,28 @@ pub use bit::Bit;
9 11
10mod prelude; 12mod prelude;
11pub use prelude::{TrackEvent, ViewMode}; 13pub use prelude::{TrackEvent, ViewMode};
14
15use crate::app::Cursor;
16
17use cursive::direction::Absolute;
18
19#[derive(Debug, Default)]
20pub struct InnerData {
21 pub cursor: Cursor,
22 pub view_mode: ViewMode,
23}
24
25impl InnerData {
26 pub fn move_cursor(&mut self, d: Absolute) {
27 self.cursor.small_seek(d);
28 }
29 pub fn cursor(&self) -> Cursor {
30 self.cursor
31 }
32 pub fn set_view_mode(&mut self, mode: ViewMode) {
33 self.view_mode = mode;
34 }
35 pub fn view_mode(&self) -> ViewMode {
36 self.view_mode
37 }
38}
diff --git a/src/habit/traits.rs b/src/habit/traits.rs
index 289fd95..24d941d 100644
--- a/src/habit/traits.rs
+++ b/src/habit/traits.rs
@@ -1,58 +1,45 @@
1use chrono::NaiveDate; 1use chrono::NaiveDate;
2use cursive::direction::{Absolute, Direction}; 2use cursive::direction::Direction;
3use cursive::event::{Event, EventResult}; 3use cursive::event::{Event, EventResult};
4use cursive::{Printer, Vec2}; 4use cursive::{Printer, Vec2};
5 5
6use typetag; 6use typetag;
7 7
8use crate::app::Cursor; 8use crate::habit::{Bit, Count, InnerData, TrackEvent};
9use crate::habit::{Bit, Count, TrackEvent, ViewMode};
10use crate::views::ShadowView; 9use crate::views::ShadowView;
11 10
12pub trait Habit { 11pub trait Habit {
13 type HabitType; 12 type HabitType;
14 13
15 fn set_name(&mut self, name: impl AsRef<str>);
16 fn set_goal(&mut self, goal: Self::HabitType);
17 fn name(&self) -> String;
18 fn get_by_date(&self, date: NaiveDate) -> Option<&Self::HabitType>; 14 fn get_by_date(&self, date: NaiveDate) -> Option<&Self::HabitType>;
15 fn goal(&self) -> u32;
19 fn insert_entry(&mut self, date: NaiveDate, val: Self::HabitType); 16 fn insert_entry(&mut self, date: NaiveDate, val: Self::HabitType);
17 fn modify(&mut self, date: NaiveDate, event: TrackEvent);
18 fn name(&self) -> String;
20 fn reached_goal(&self, date: NaiveDate) -> bool; 19 fn reached_goal(&self, date: NaiveDate) -> bool;
21 fn remaining(&self, date: NaiveDate) -> u32; 20 fn remaining(&self, date: NaiveDate) -> u32;
22 fn goal(&self) -> u32; 21 fn set_goal(&mut self, goal: Self::HabitType);
23 fn modify(&mut self, date: NaiveDate, event: TrackEvent); 22 fn set_name(&mut self, name: impl AsRef<str>);
24
25 fn set_view_month_offset(&mut self, offset: u32);
26 fn view_month_offset(&self) -> u32;
27
28 fn move_cursor(&mut self, d: Absolute);
29 fn cursor(&self) -> Cursor;
30 23
31 fn set_view_mode(&mut self, mode: ViewMode); 24 fn inner_data_ref(&self) -> &InnerData;
32 fn view_mode(&self) -> ViewMode; 25 fn inner_data_mut_ref(&mut self) -> &mut InnerData;
33 26
34 fn is_auto(&self) -> bool; 27 fn is_auto(&self) -> bool;
35} 28}
36 29
37#[typetag::serde(tag = "type")] 30#[typetag::serde(tag = "type")]
38pub trait HabitWrapper: erased_serde::Serialize { 31pub trait HabitWrapper: erased_serde::Serialize {
39 fn remaining(&self, date: NaiveDate) -> u32; 32 fn draw(&self, printer: &Printer);
40 fn goal(&self) -> u32; 33 fn goal(&self) -> u32;
41 fn modify(&mut self, date: NaiveDate, event: TrackEvent); 34 fn modify(&mut self, date: NaiveDate, event: TrackEvent);
42 fn draw(&self, printer: &Printer); 35 fn name(&self) -> String;
43 fn on_event(&mut self, event: Event) -> EventResult; 36 fn on_event(&mut self, event: Event) -> EventResult;
37 fn remaining(&self, date: NaiveDate) -> u32;
44 fn required_size(&mut self, _: Vec2) -> Vec2; 38 fn required_size(&mut self, _: Vec2) -> Vec2;
45 fn take_focus(&mut self, _: Direction) -> bool; 39 fn take_focus(&mut self, _: Direction) -> bool;
46 fn name(&self) -> String;
47 40
48 fn set_view_month_offset(&mut self, offset: u32); 41 fn inner_data_ref(&self) -> &InnerData;
49 fn view_month_offset(&self) -> u32; 42 fn inner_data_mut_ref(&mut self) -> &mut InnerData;
50
51 fn move_cursor(&mut self, d: Absolute);
52 fn cursor(&self) -> Cursor;
53
54 fn set_view_mode(&mut self, mode: ViewMode);
55 fn view_mode(&self) -> ViewMode;
56 43
57 fn is_auto(&self) -> bool; 44 fn is_auto(&self) -> bool;
58} 45}
@@ -88,23 +75,11 @@ macro_rules! auto_habit_impl {
88 fn name(&self) -> String { 75 fn name(&self) -> String {
89 Habit::name(self) 76 Habit::name(self)
90 } 77 }
91 fn set_view_month_offset(&mut self, offset: u32) { 78 fn inner_data_ref(&self) -> &InnerData {
92 Habit::set_view_month_offset(self, offset) 79 Habit::inner_data_ref(self)
93 }
94 fn view_month_offset(&self) -> u32 {
95 Habit::view_month_offset(self)
96 }
97 fn move_cursor(&mut self, d: Absolute) {
98 Habit::move_cursor(self, d)
99 }
100 fn cursor(&self) -> Cursor {
101 Habit::cursor(self)
102 }
103 fn set_view_mode(&mut self, mode: ViewMode) {
104 Habit::set_view_mode(self, mode)
105 } 80 }
106 fn view_mode(&self) -> ViewMode { 81 fn inner_data_mut_ref(&mut self) -> &mut InnerData {
107 Habit::view_mode(self) 82 Habit::inner_data_mut_ref(self)
108 } 83 }
109 fn is_auto(&self) -> bool { 84 fn is_auto(&self) -> bool {
110 Habit::is_auto(self) 85 Habit::is_auto(self)
diff --git a/src/views.rs b/src/views.rs
index a306602..f02eb83 100644
--- a/src/views.rs
+++ b/src/views.rs
@@ -1,6 +1,6 @@
1use cursive::direction::Direction; 1use cursive::direction::Direction;
2use cursive::event::{Event, EventResult, Key}; 2use cursive::event::{Event, EventResult, Key};
3use cursive::theme::{ColorStyle, ColorType, Effect, Style}; 3use cursive::theme::{ColorStyle, Effect, Style};
4use cursive::view::View; 4use cursive::view::View;
5use cursive::{Printer, Vec2}; 5use cursive::{Printer, Vec2};
6 6
@@ -35,7 +35,7 @@ where
35 // .checked_sub_signed(Duration::weeks(4 * self.view_month_offset() as i64)) 35 // .checked_sub_signed(Duration::weeks(4 * self.view_month_offset() as i64))
36 // .unwrap() 36 // .unwrap()
37 // }; 37 // };
38 let now = self.cursor().0; 38 let now = self.inner_data_ref().cursor().0;
39 let is_today = now == Local::now().naive_local().date(); 39 let is_today = now == Local::now().naive_local().date();
40 let year = now.year(); 40 let year = now.year();
41 let month = now.month(); 41 let month = now.month();
@@ -141,7 +141,7 @@ where
141 } 141 }
142 }; 142 };
143 143
144 match self.view_mode() { 144 match self.inner_data_ref().view_mode() {
145 ViewMode::Day => draw_day(printer), 145 ViewMode::Day => draw_day(printer),
146 ViewMode::Week => draw_week(printer), 146 ViewMode::Week => draw_week(printer),
147 _ => draw_day(printer), 147 _ => draw_day(printer),
@@ -157,7 +157,7 @@ where
157 } 157 }
158 158
159 fn on_event(&mut self, e: Event) -> EventResult { 159 fn on_event(&mut self, e: Event) -> EventResult {
160 let now = self.cursor().0; 160 let now = self.inner_data_mut_ref().cursor().0;
161 if self.is_auto() { 161 if self.is_auto() {
162 return EventResult::Ignored; 162 return EventResult::Ignored;
163 } 163 }