aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--flake.nix2
-rw-r--r--src/app/cursor.rs51
-rw-r--r--src/app/impl_self.rs14
-rw-r--r--src/app/impl_view.rs18
-rw-r--r--src/app/message.rs3
-rw-r--r--src/app/mod.rs3
-rw-r--r--src/habit/bit.rs12
-rw-r--r--src/habit/count.rs12
-rw-r--r--src/habit/traits.rs15
-rw-r--r--src/theme.rs10
-rw-r--r--src/views.rs31
11 files changed, 151 insertions, 20 deletions
diff --git a/flake.nix b/flake.nix
index a046490..a51c554 100644
--- a/flake.nix
+++ b/flake.nix
@@ -38,7 +38,7 @@
38 nativeBuildInputs = [ 38 nativeBuildInputs = [
39 rust 39 rust
40 pkgs.cargo 40 pkgs.cargo
41 pkgs.cargo 41 pkgs.openssl
42 pkgs.ncurses 42 pkgs.ncurses
43 ]; 43 ];
44 }; 44 };
diff --git a/src/app/cursor.rs b/src/app/cursor.rs
new file mode 100644
index 0000000..ed6bd65
--- /dev/null
+++ b/src/app/cursor.rs
@@ -0,0 +1,51 @@
1use chrono::{Duration, Local, NaiveDate};
2use cursive::direction::Absolute;
3
4#[derive(Debug, Copy, Clone)]
5pub struct Cursor(pub NaiveDate);
6
7impl std::default::Default for Cursor {
8 fn default() -> Self {
9 Cursor::new()
10 }
11}
12
13impl Cursor {
14 pub fn new() -> Self {
15 Cursor {
16 0: Local::now().naive_local().date(),
17 }
18 }
19 pub fn do_move(&mut self, d: Absolute) {
20 let today = Local::now().naive_local().date();
21 let cursor = self.0;
22 match d {
23 Absolute::Right => {
24 // forward by 1 day
25 let next = cursor.succ_opt().unwrap_or(cursor);
26 if next <= today {
27 self.0 = next;
28 }
29 }
30 Absolute::Left => {
31 // backward by 1 day
32 // assumes an infinite past
33 self.0 = cursor.pred_opt().unwrap_or(cursor);
34 }
35 Absolute::Down => {
36 // forward by 1 week
37 let next = cursor.checked_add_signed(Duration::weeks(1)).unwrap();
38 if next <= today {
39 self.0 = next;
40 }
41 }
42 Absolute::Up => {
43 // backward by 1 week
44 // assumes an infinite past
45 let next = cursor.checked_sub_signed(Duration::weeks(1)).unwrap();
46 self.0 = next;
47 }
48 Absolute::None => {}
49 }
50 }
51}
diff --git a/src/app/impl_self.rs b/src/app/impl_self.rs
index 1114d50..abf5209 100644
--- a/src/app/impl_self.rs
+++ b/src/app/impl_self.rs
@@ -15,7 +15,7 @@ use crate::command::{Command, CommandLineError};
15use crate::habit::{Bit, Count, HabitWrapper, TrackEvent, ViewMode}; 15use crate::habit::{Bit, Count, HabitWrapper, TrackEvent, ViewMode};
16use crate::utils::{self, GRID_WIDTH, VIEW_HEIGHT, VIEW_WIDTH}; 16use crate::utils::{self, GRID_WIDTH, VIEW_HEIGHT, VIEW_WIDTH};
17 17
18use crate::app::{App, MessageKind, StatusLine}; 18use crate::app::{App, Cursor, Message, MessageKind, StatusLine};
19 19
20impl App { 20impl App {
21 pub fn new() -> Self { 21 pub fn new() -> Self {
@@ -28,7 +28,8 @@ impl App {
28 _file_watcher: watcher, 28 _file_watcher: watcher,
29 file_event_recv: rx, 29 file_event_recv: rx,
30 view_month_offset: 0, 30 view_month_offset: 0,
31 message: "Type :add <habit-name> <goal> to get started, Ctrl-L to dismiss".into(), 31 cursor: Cursor::new(),
32 message: Message::startup(),
32 }; 33 };
33 } 34 }
34 35
@@ -85,6 +86,13 @@ impl App {
85 } 86 }
86 } 87 }
87 88
89 pub fn move_cursor(&mut self, d: Absolute) {
90 self.cursor.do_move(d);
91 for v in self.habits.iter_mut() {
92 v.move_cursor(d);
93 }
94 }
95
88 pub fn set_focus(&mut self, d: Absolute) { 96 pub fn set_focus(&mut self, d: Absolute) {
89 match d { 97 match d {
90 Absolute::Right => { 98 Absolute::Right => {
@@ -129,7 +137,7 @@ impl App {
129 format!("{}", Local::now().naive_local().date().format("%d/%b/%y"),) 137 format!("{}", Local::now().naive_local().date().format("%d/%b/%y"),)
130 } else { 138 } else {
131 let months = self.view_month_offset; 139 let months = self.view_month_offset;
132 format!("{}", format!("{} months ago", months),) 140 format!("{}", format!("{} month(s) ago", months),)
133 }; 141 };
134 142
135 StatusLine { 143 StatusLine {
diff --git a/src/app/impl_view.rs b/src/app/impl_view.rs
index 395cac4..0ec47f1 100644
--- a/src/app/impl_view.rs
+++ b/src/app/impl_view.rs
@@ -95,6 +95,24 @@ impl View for App {
95 self.set_focus(Absolute::Down); 95 self.set_focus(Absolute::Down);
96 return EventResult::Consumed(None); 96 return EventResult::Consumed(None);
97 } 97 }
98
99 Event::Char('w') => {
100 self.move_cursor(Absolute::Up);
101 return EventResult::Consumed(None);
102 }
103 Event::Char('a') => {
104 self.move_cursor(Absolute::Left);
105 return EventResult::Consumed(None);
106 }
107 Event::Char('s') => {
108 self.move_cursor(Absolute::Down);
109 return EventResult::Consumed(None);
110 }
111 Event::Char('d') => {
112 self.move_cursor(Absolute::Right);
113 return EventResult::Consumed(None);
114 }
115
98 Event::Char('v') => { 116 Event::Char('v') => {
99 if self.habits.is_empty() { 117 if self.habits.is_empty() {
100 return EventResult::Consumed(None); 118 return EventResult::Consumed(None);
diff --git a/src/app/message.rs b/src/app/message.rs
index 65f0a5c..a1d3d57 100644
--- a/src/app/message.rs
+++ b/src/app/message.rs
@@ -35,6 +35,9 @@ pub struct Message {
35} 35}
36 36
37impl Message { 37impl Message {
38 pub fn startup() -> Self {
39 "Type :add <habit-name> <goal> to get started, Ctrl-L to dismiss".into()
40 }
38 pub fn contents(&self) -> &str { 41 pub fn contents(&self) -> &str {
39 &self.msg 42 &self.msg
40 } 43 }
diff --git a/src/app/mod.rs b/src/app/mod.rs
index 2aecb33..9432f0d 100644
--- a/src/app/mod.rs
+++ b/src/app/mod.rs
@@ -5,11 +5,13 @@ use notify::{DebouncedEvent, RecommendedWatcher};
5 5
6use crate::habit::HabitWrapper; 6use crate::habit::HabitWrapper;
7 7
8mod cursor;
8mod impl_self; 9mod impl_self;
9mod impl_view; 10mod impl_view;
10mod message; 11mod message;
11 12
12pub struct StatusLine(String, String); 13pub struct StatusLine(String, String);
14pub use cursor::Cursor;
13pub use message::{Message, MessageKind}; 15pub use message::{Message, MessageKind};
14 16
15pub struct App { 17pub struct App {
@@ -20,6 +22,7 @@ pub struct App {
20 file_event_recv: Receiver<DebouncedEvent>, 22 file_event_recv: Receiver<DebouncedEvent>,
21 focus: usize, 23 focus: usize,
22 view_month_offset: u32, 24 view_month_offset: u32,
25 cursor: Cursor,
23 message: Message, 26 message: Message,
24} 27}
25 28
diff --git a/src/habit/bit.rs b/src/habit/bit.rs
index 2bbb0ac..7fe6fd9 100644
--- a/src/habit/bit.rs
+++ b/src/habit/bit.rs
@@ -1,8 +1,10 @@
1use std::collections::HashMap; 1use std::collections::HashMap;
2 2
3use chrono::NaiveDate; 3use chrono::NaiveDate;
4use cursive::direction::Absolute;
4use serde::{Deserialize, Serialize}; 5use serde::{Deserialize, Serialize};
5 6
7use crate::app::Cursor;
6use crate::habit::prelude::default_auto; 8use crate::habit::prelude::default_auto;
7use crate::habit::traits::Habit; 9use crate::habit::traits::Habit;
8use crate::habit::{TrackEvent, ViewMode}; 10use crate::habit::{TrackEvent, ViewMode};
@@ -45,6 +47,9 @@ pub struct Bit {
45 view_month_offset: u32, 47 view_month_offset: u32,
46 48
47 #[serde(skip)] 49 #[serde(skip)]
50 cursor: Cursor,
51
52 #[serde(skip)]
48 view_mode: ViewMode, 53 view_mode: ViewMode,
49} 54}
50 55
@@ -56,6 +61,7 @@ impl Bit {
56 goal: CustomBool(true), 61 goal: CustomBool(true),
57 auto, 62 auto,
58 view_month_offset: 0, 63 view_month_offset: 0,
64 cursor: Cursor::new(),
59 view_mode: ViewMode::Day, 65 view_mode: ViewMode::Day,
60 }; 66 };
61 } 67 }
@@ -124,6 +130,12 @@ impl Habit for Bit {
124 fn view_month_offset(&self) -> u32 { 130 fn view_month_offset(&self) -> u32 {
125 self.view_month_offset 131 self.view_month_offset
126 } 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 }
127 fn set_view_mode(&mut self, mode: ViewMode) { 139 fn set_view_mode(&mut self, mode: ViewMode) {
128 self.view_mode = mode; 140 self.view_mode = mode;
129 } 141 }
diff --git a/src/habit/count.rs b/src/habit/count.rs
index d351758..b14354c 100644
--- a/src/habit/count.rs
+++ b/src/habit/count.rs
@@ -1,8 +1,10 @@
1use std::collections::HashMap; 1use std::collections::HashMap;
2 2
3use chrono::NaiveDate; 3use chrono::NaiveDate;
4use cursive::direction::Absolute;
4use serde::{Deserialize, Serialize}; 5use serde::{Deserialize, Serialize};
5 6
7use crate::app::Cursor;
6use crate::habit::prelude::default_auto; 8use crate::habit::prelude::default_auto;
7use crate::habit::traits::Habit; 9use crate::habit::traits::Habit;
8use crate::habit::{TrackEvent, ViewMode}; 10use crate::habit::{TrackEvent, ViewMode};
@@ -20,6 +22,9 @@ pub struct Count {
20 view_month_offset: u32, 22 view_month_offset: u32,
21 23
22 #[serde(skip)] 24 #[serde(skip)]
25 cursor: Cursor,
26
27 #[serde(skip)]
23 view_mode: ViewMode, 28 view_mode: ViewMode,
24} 29}
25 30
@@ -31,6 +36,7 @@ impl Count {
31 goal, 36 goal,
32 auto, 37 auto,
33 view_month_offset: 0, 38 view_month_offset: 0,
39 cursor: Cursor::new(),
34 view_mode: ViewMode::Day, 40 view_mode: ViewMode::Day,
35 }; 41 };
36 } 42 }
@@ -101,6 +107,12 @@ impl Habit for Count {
101 fn view_month_offset(&self) -> u32 { 107 fn view_month_offset(&self) -> u32 {
102 self.view_month_offset 108 self.view_month_offset
103 } 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 }
104 fn set_view_mode(&mut self, mode: ViewMode) { 116 fn set_view_mode(&mut self, mode: ViewMode) {
105 self.view_mode = mode; 117 self.view_mode = mode;
106 } 118 }
diff --git a/src/habit/traits.rs b/src/habit/traits.rs
index 74fd00b..289fd95 100644
--- a/src/habit/traits.rs
+++ b/src/habit/traits.rs
@@ -1,10 +1,11 @@
1use chrono::NaiveDate; 1use chrono::NaiveDate;
2use cursive::direction::Direction; 2use cursive::direction::{Absolute, 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, TrackEvent, ViewMode}; 9use crate::habit::{Bit, Count, TrackEvent, ViewMode};
9use crate::views::ShadowView; 10use crate::views::ShadowView;
10 11
@@ -24,6 +25,9 @@ pub trait Habit {
24 fn set_view_month_offset(&mut self, offset: u32); 25 fn set_view_month_offset(&mut self, offset: u32);
25 fn view_month_offset(&self) -> u32; 26 fn view_month_offset(&self) -> u32;
26 27
28 fn move_cursor(&mut self, d: Absolute);
29 fn cursor(&self) -> Cursor;
30
27 fn set_view_mode(&mut self, mode: ViewMode); 31 fn set_view_mode(&mut self, mode: ViewMode);
28 fn view_mode(&self) -> ViewMode; 32 fn view_mode(&self) -> ViewMode;
29 33
@@ -44,6 +48,9 @@ pub trait HabitWrapper: erased_serde::Serialize {
44 fn set_view_month_offset(&mut self, offset: u32); 48 fn set_view_month_offset(&mut self, offset: u32);
45 fn view_month_offset(&self) -> u32; 49 fn view_month_offset(&self) -> u32;
46 50
51 fn move_cursor(&mut self, d: Absolute);
52 fn cursor(&self) -> Cursor;
53
47 fn set_view_mode(&mut self, mode: ViewMode); 54 fn set_view_mode(&mut self, mode: ViewMode);
48 fn view_mode(&self) -> ViewMode; 55 fn view_mode(&self) -> ViewMode;
49 56
@@ -87,6 +94,12 @@ macro_rules! auto_habit_impl {
87 fn view_month_offset(&self) -> u32 { 94 fn view_month_offset(&self) -> u32 {
88 Habit::view_month_offset(self) 95 Habit::view_month_offset(self)
89 } 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 }
90 fn set_view_mode(&mut self, mode: ViewMode) { 103 fn set_view_mode(&mut self, mode: ViewMode) {
91 Habit::set_view_mode(self, mode) 104 Habit::set_view_mode(self, mode)
92 } 105 }
diff --git a/src/theme.rs b/src/theme.rs
index 1d2cc36..e373b72 100644
--- a/src/theme.rs
+++ b/src/theme.rs
@@ -1,6 +1,6 @@
1use cursive::theme::Color::*; 1use cursive::theme::Color::*;
2use cursive::theme::PaletteColor::*; 2use cursive::theme::PaletteColor::*;
3use cursive::theme::{BorderStyle, Palette, Theme}; 3use cursive::theme::{BorderStyle, ColorStyle, Palette, Style, Theme};
4 4
5pub fn pallete_gen() -> Palette { 5pub fn pallete_gen() -> Palette {
6 let mut p = Palette::default(); 6 let mut p = Palette::default();
@@ -24,3 +24,11 @@ pub fn theme_gen() -> Theme {
24 t.palette = pallete_gen(); 24 t.palette = pallete_gen();
25 return t; 25 return t;
26} 26}
27
28pub fn cursor_gen(foreground: Style) -> Style {
29 Style::from(ColorStyle::new(
30 TerminalDefault,
31 Light(cursive::theme::BaseColor::Blue),
32 ))
33 .combine(foreground)
34}
diff --git a/src/views.rs b/src/views.rs
index efd1391..a0beb2c 100644
--- a/src/views.rs
+++ b/src/views.rs
@@ -5,9 +5,10 @@ use cursive::view::View;
5use cursive::{Printer, Vec2}; 5use cursive::{Printer, Vec2};
6 6
7use chrono::prelude::*; 7use chrono::prelude::*;
8use chrono::{Duration, Local, NaiveDate}; 8use chrono::{Local, NaiveDate};
9 9
10use crate::habit::{Bit, Count, Habit, TrackEvent, ViewMode}; 10use crate::habit::{Bit, Count, Habit, TrackEvent, ViewMode};
11use crate::theme::cursor_gen;
11use crate::utils::VIEW_WIDTH; 12use crate::utils::VIEW_WIDTH;
12 13
13use crate::CONFIGURATION; 14use crate::CONFIGURATION;
@@ -27,13 +28,15 @@ where
27 T::HabitType: std::fmt::Display, 28 T::HabitType: std::fmt::Display,
28{ 29{
29 fn draw(&self, printer: &Printer) { 30 fn draw(&self, printer: &Printer) {
30 let now = if self.view_month_offset() == 0 { 31 // let now = if self.view_month_offset() == 0 {
31 Local::today() 32 // Local::today()
32 } else { 33 // } else {
33 Local::today() 34 // Local::today()
34 .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))
35 .unwrap() 36 // .unwrap()
36 }; 37 // };
38 let now = self.cursor().0;
39 let is_today = now == Local::now().naive_local().date();
37 let year = now.year(); 40 let year = now.year();
38 let month = now.month(); 41 let month = now.month();
39 42
@@ -43,8 +46,7 @@ where
43 46
44 let strikethrough = Style::from(Effect::Strikethrough); 47 let strikethrough = Style::from(Effect::Strikethrough);
45 48
46 let goal_status = 49 let goal_status = is_today && self.reached_goal(Local::now().naive_local().date());
47 self.view_month_offset() == 0 && self.reached_goal(Local::now().naive_local().date());
48 50
49 printer.with_style( 51 printer.with_style(
50 Style::merge(&[ 52 Style::merge(&[
@@ -110,11 +112,12 @@ where
110 let draw_day = |printer: &Printer| { 112 let draw_day = |printer: &Printer| {
111 let mut i = 0; 113 let mut i = 0;
112 while let Some(d) = NaiveDate::from_ymd_opt(year, month, i + 1) { 114 while let Some(d) = NaiveDate::from_ymd_opt(year, month, i + 1) {
113 let day_style; 115 let mut day_style = todo_style;
114 if self.reached_goal(d) { 116 if self.reached_goal(d) {
115 day_style = goal_reached_style; 117 day_style = goal_reached_style;
116 } else { 118 }
117 day_style = todo_style; 119 if d == now && printer.focused {
120 day_style = day_style.combine(cursor_gen(day_style));
118 } 121 }
119 let coords: Vec2 = ((i % 7) * 3, i / 7 + 2).into(); 122 let coords: Vec2 = ((i % 7) * 3, i / 7 + 2).into();
120 if let Some(c) = self.get_by_date(d) { 123 if let Some(c) = self.get_by_date(d) {
@@ -146,7 +149,7 @@ where
146 } 149 }
147 150
148 fn on_event(&mut self, e: Event) -> EventResult { 151 fn on_event(&mut self, e: Event) -> EventResult {
149 let now = Local::now().naive_local().date(); 152 let now = self.cursor().0;
150 if self.is_auto() { 153 if self.is_auto() {
151 return EventResult::Ignored; 154 return EventResult::Ignored;
152 } 155 }