1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
|
use cursive::direction::Direction;
use cursive::event::{Event, EventResult, Key};
use cursive::theme::{BaseColor, Color, Style};
use cursive::view::View;
use cursive::{Printer, Vec2};
use chrono::prelude::*;
use chrono::{Local, NaiveDate};
use crate::habit::{Habit, HabitTrait, HabitType, TrackEvent};
use serde::Serialize;
pub struct HabitView {
habit: Habit,
// characters to use
true_chr: char,
false_chr: char,
future_chr: char,
// view dimensions
view_width: u32,
view_height: u32,
// color config
reached_color: Color,
todo_color: Color,
future_color: Color,
}
impl HabitView {
pub fn new(habit: Habit) -> Self {
return HabitView {
habit,
true_chr: '·',
false_chr: '·',
future_chr: '·',
view_width: 25,
view_height: 8,
reached_color: Color::Dark(BaseColor::Cyan),
todo_color: Color::Dark(BaseColor::Magenta),
future_color: Color::Light(BaseColor::Black),
};
}
pub fn get_name(&self) -> String {
return self.habit.get_name().to_owned();
}
pub fn get_size(&self) -> Vec2 {
(self.view_width, self.view_height).into()
}
pub fn remaining(&self) -> u32 {
self.habit.remaining(Local::now().naive_utc().date())
}
pub fn total(&self) -> u32 {
self.habit.total()
}
}
impl View for HabitView {
fn draw(&self, printer: &Printer) {
let now = Local::now();
let year = now.year();
let month = now.month();
let goal_reached_style = Style::from(self.reached_color);
let todo_style = Style::from(self.todo_color);
let future_style = Style::from(self.future_color);
printer.with_style(
if !printer.focused {
future_style
} else {
goal_reached_style
},
|p| {
p.print(
(0, 0),
&format!("{:width$}", self.get_name(), width = self.get_size().x),
)
},
);
for i in 1..=31 {
let day = NaiveDate::from_ymd_opt(year, month, i);
let mut day_style;
if let Some(d) = day {
if self.habit.reached_goal(d) {
day_style = goal_reached_style;
} else {
day_style = todo_style;
}
let coords: Vec2 = ((i % 7) * 3, i / 7 + 2).into();
let day_chr: Box<dyn std::fmt::Display> = match self.habit.get_by_date(d) {
Some(val) => match val {
HabitType::Bit(b) => {
if *b {
Box::new(self.true_chr)
} else {
Box::new(self.false_chr)
}
}
HabitType::Count(c) => Box::new(c.to_string()),
},
None => {
day_style = future_style;
Box::new(self.future_chr)
}
};
printer.with_style(day_style, |p| {
p.print(coords, &format!("{:^3}", day_chr));
});
}
}
}
fn required_size(&mut self, _: Vec2) -> Vec2 {
(self.view_width, self.view_height).into()
}
fn take_focus(&mut self, _: Direction) -> bool {
true
}
fn on_event(&mut self, e: Event) -> EventResult {
let now = Local::now().naive_utc().date();
match e {
Event::Key(Key::Enter) | Event::Char('n') => {
self.habit.modify(now, TrackEvent::Increment);
return EventResult::Consumed(None);
}
Event::Key(Key::Backspace) | Event::Char('p') => {
self.habit.modify(now, TrackEvent::Decrement);
return EventResult::Consumed(None);
}
_ => return EventResult::Ignored,
}
}
}
|