aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/main.yml118
-rw-r--r--Cargo.lock164
-rw-r--r--Cargo.toml9
-rw-r--r--readme.md8
-rw-r--r--src/app/impl_self.rs60
-rw-r--r--src/app/impl_view.rs38
-rw-r--r--src/command.rs87
-rw-r--r--src/habit/bit.rs4
-rw-r--r--src/main.rs23
-rw-r--r--src/theme.rs20
-rw-r--r--src/utils.rs130
-rw-r--r--src/views.rs35
12 files changed, 568 insertions, 128 deletions
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
new file mode 100644
index 0000000..8786029
--- /dev/null
+++ b/.github/workflows/main.yml
@@ -0,0 +1,118 @@
1name: Rust
2
3on:
4 push:
5 tags:
6 - '*'
7
8jobs:
9 rustfmt:
10 runs-on: ubuntu-latest
11 steps:
12 - uses: actions/checkout@v1
13 - run: rustup component add rustfmt
14 - run: cargo fmt -- --check
15
16 build-linux:
17 runs-on: ubuntu-latest
18
19 steps:
20 - name: Checkout
21 uses: actions/checkout@v1
22 # cache the build assets so they dont recompile every time.
23 - name: Cache Rust dependencies
24 uses: actions/[email protected]
25 with:
26 path: target
27 key: ${{ runner.OS }}-build-${{ hashFiles('**/Cargo.lock') }}
28 restore-keys: |
29 ${{ runner.OS }}-build-
30 - name: Install latest rust toolchain
31 uses: actions-rs/toolchain@v1
32 with:
33 toolchain: beta
34 default: true
35 override: true
36 - name: Install system dependencies
37 run: |
38 sudo apt-get update \
39 && sudo apt-get install -y \
40 libdbus-1-dev libncurses5-dev libncursesw5-dev
41 - name: Build
42 run: cargo build --release && strip target/release/dijo
43
44 - name: Upload binaries to release
45 uses: svenstaro/upload-release-action@v1-release
46 with:
47 repo_token: ${{ secrets.GITHUB_TOKEN }}
48 file: target/release/dijo
49 asset_name: dijo-x86_64-linux
50 tag: ${{ github.ref }}
51 overwrite: true
52
53 build-apple:
54 runs-on: macos-latest
55
56 steps:
57 - name: Checkout
58 uses: actions/checkout@v1
59 - name: Cache Rust dependencies
60 uses: actions/[email protected]
61 with:
62 path: target
63 key: ${{ runner.OS }}-build-${{ hashFiles('**/Cargo.lock') }}
64 restore-keys: |
65 ${{ runner.OS }}-build-
66 - name: Install latest rust toolchain
67 uses: actions-rs/toolchain@v1
68 with:
69 toolchain: beta
70 target: x86_64-apple-darwin
71 default: true
72 override: true
73
74 - name: Build for mac
75 run: cargo build --release && strip target/release/dijo
76
77 - name: Upload binaries to release
78 uses: svenstaro/upload-release-action@v1-release
79 with:
80 repo_token: ${{ secrets.GITHUB_TOKEN }}
81 file: target/release/dijo
82 asset_name: dijo-x86_64-apple
83 tag: ${{ github.ref }}
84 overwrite: true
85
86 build-windows:
87 runs-on: windows-latest
88
89 steps:
90 - name: Checkout
91 uses: actions/checkout@v1
92 - name: Cache Rust dependencies
93 uses: actions/[email protected]
94 with:
95 path: target
96 key: ${{ runner.OS }}-build-${{ hashFiles('**/Cargo.lock') }}
97 restore-keys: |
98 ${{ runner.OS }}-build-
99 - name: Install latest rust toolchain
100 uses: actions-rs/toolchain@v1
101 with:
102 toolchain: beta
103 target: x86_64-pc-windows-msvc
104 default: true
105 override: true
106
107 - name: Build for windows
108 shell: bash
109 run: cargo build --release --no-default-features --features "crossterm-backend"
110
111 - name: Upload binaries to release
112 uses: svenstaro/upload-release-action@v1-release
113 with:
114 repo_token: ${{ secrets.GITHUB_TOKEN }}
115 file: target/release/dijo.exe
116 asset_name: dijo-x86_64-windows.exe
117 tag: ${{ github.ref }}
118 overwrite: true
diff --git a/Cargo.lock b/Cargo.lock
index ade0cc4..fd66bcd 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -125,6 +125,15 @@ dependencies = [
125] 125]
126 126
127[[package]] 127[[package]]
128name = "cloudabi"
129version = "0.0.3"
130source = "registry+https://github.com/rust-lang/crates.io-index"
131checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
132dependencies = [
133 "bitflags",
134]
135
136[[package]]
128name = "const-random" 137name = "const-random"
129version = "0.1.8" 138version = "0.1.8"
130source = "registry+https://github.com/rust-lang/crates.io-index" 139source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -152,12 +161,12 @@ checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
152 161
153[[package]] 162[[package]]
154name = "crossbeam-channel" 163name = "crossbeam-channel"
155version = "0.4.2" 164version = "0.4.3"
156source = "registry+https://github.com/rust-lang/crates.io-index" 165source = "registry+https://github.com/rust-lang/crates.io-index"
157checksum = "cced8691919c02aac3cb0a1bc2e9b73d89e832bf9a06fc579d4e71b68a2da061" 166checksum = "09ee0cc8804d5393478d743b035099520087a5186f3b93fa58cec08fa62407b6"
158dependencies = [ 167dependencies = [
168 "cfg-if",
159 "crossbeam-utils", 169 "crossbeam-utils",
160 "maybe-uninit",
161] 170]
162 171
163[[package]] 172[[package]]
@@ -172,6 +181,31 @@ dependencies = [
172] 181]
173 182
174[[package]] 183[[package]]
184name = "crossterm"
185version = "0.17.7"
186source = "registry+https://github.com/rust-lang/crates.io-index"
187checksum = "6f4919d60f26ae233e14233cc39746c8c8bb8cd7b05840ace83604917b51b6c7"
188dependencies = [
189 "bitflags",
190 "crossterm_winapi",
191 "lazy_static",
192 "libc",
193 "mio 0.7.0",
194 "parking_lot",
195 "signal-hook",
196 "winapi 0.3.9",
197]
198
199[[package]]
200name = "crossterm_winapi"
201version = "0.6.1"
202source = "registry+https://github.com/rust-lang/crates.io-index"
203checksum = "057b7146d02fb50175fd7dbe5158f6097f33d02831f43b4ee8ae4ddf67b68f5c"
204dependencies = [
205 "winapi 0.3.9",
206]
207
208[[package]]
175name = "ctor" 209name = "ctor"
176version = "0.1.15" 210version = "0.1.15"
177source = "registry+https://github.com/rust-lang/crates.io-index" 211source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -190,6 +224,7 @@ dependencies = [
190 "ahash 0.3.8", 224 "ahash 0.3.8",
191 "cfg-if", 225 "cfg-if",
192 "crossbeam-channel", 226 "crossbeam-channel",
227 "crossterm",
193 "cursive_core", 228 "cursive_core",
194 "enumset", 229 "enumset",
195 "lazy_static", 230 "lazy_static",
@@ -260,7 +295,7 @@ dependencies = [
260 295
261[[package]] 296[[package]]
262name = "dijo" 297name = "dijo"
263version = "0.1.4" 298version = "0.2.3"
264dependencies = [ 299dependencies = [
265 "chrono", 300 "chrono",
266 "clap", 301 "clap",
@@ -271,6 +306,7 @@ dependencies = [
271 "notify", 306 "notify",
272 "serde", 307 "serde",
273 "serde_json", 308 "serde_json",
309 "toml",
274 "typetag", 310 "typetag",
275] 311]
276 312
@@ -522,6 +558,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
522checksum = "bd7d4bd64732af4bf3a67f367c27df8520ad7e230c5817b8ff485864d80242b9" 558checksum = "bd7d4bd64732af4bf3a67f367c27df8520ad7e230c5817b8ff485864d80242b9"
523 559
524[[package]] 560[[package]]
561name = "lock_api"
562version = "0.3.4"
563source = "registry+https://github.com/rust-lang/crates.io-index"
564checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75"
565dependencies = [
566 "scopeguard",
567]
568
569[[package]]
525name = "log" 570name = "log"
526version = "0.4.11" 571version = "0.4.11"
527source = "registry+https://github.com/rust-lang/crates.io-index" 572source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -531,12 +576,6 @@ dependencies = [
531] 576]
532 577
533[[package]] 578[[package]]
534name = "maybe-uninit"
535version = "2.0.0"
536source = "registry+https://github.com/rust-lang/crates.io-index"
537checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
538
539[[package]]
540name = "mio" 579name = "mio"
541version = "0.6.22" 580version = "0.6.22"
542source = "registry+https://github.com/rust-lang/crates.io-index" 581source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -549,13 +588,27 @@ dependencies = [
549 "kernel32-sys", 588 "kernel32-sys",
550 "libc", 589 "libc",
551 "log", 590 "log",
552 "miow", 591 "miow 0.2.1",
553 "net2", 592 "net2",
554 "slab", 593 "slab",
555 "winapi 0.2.8", 594 "winapi 0.2.8",
556] 595]
557 596
558[[package]] 597[[package]]
598name = "mio"
599version = "0.7.0"
600source = "registry+https://github.com/rust-lang/crates.io-index"
601checksum = "6e9971bc8349a361217a8f2a41f5d011274686bd4436465ba51730921039d7fb"
602dependencies = [
603 "lazy_static",
604 "libc",
605 "log",
606 "miow 0.3.5",
607 "ntapi",
608 "winapi 0.3.9",
609]
610
611[[package]]
559name = "mio-extras" 612name = "mio-extras"
560version = "2.0.6" 613version = "2.0.6"
561source = "registry+https://github.com/rust-lang/crates.io-index" 614source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -563,7 +616,7 @@ checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19"
563dependencies = [ 616dependencies = [
564 "lazycell", 617 "lazycell",
565 "log", 618 "log",
566 "mio", 619 "mio 0.6.22",
567 "slab", 620 "slab",
568] 621]
569 622
@@ -580,6 +633,16 @@ dependencies = [
580] 633]
581 634
582[[package]] 635[[package]]
636name = "miow"
637version = "0.3.5"
638source = "registry+https://github.com/rust-lang/crates.io-index"
639checksum = "07b88fb9795d4d36d62a012dfbf49a8f5cf12751f36d31a9dbe66d528e58979e"
640dependencies = [
641 "socket2",
642 "winapi 0.3.9",
643]
644
645[[package]]
583name = "net2" 646name = "net2"
584version = "0.2.34" 647version = "0.2.34"
585source = "registry+https://github.com/rust-lang/crates.io-index" 648source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -602,13 +665,22 @@ dependencies = [
602 "fsevent-sys", 665 "fsevent-sys",
603 "inotify", 666 "inotify",
604 "libc", 667 "libc",
605 "mio", 668 "mio 0.6.22",
606 "mio-extras", 669 "mio-extras",
607 "walkdir", 670 "walkdir",
608 "winapi 0.3.9", 671 "winapi 0.3.9",
609] 672]
610 673
611[[package]] 674[[package]]
675name = "ntapi"
676version = "0.3.4"
677source = "registry+https://github.com/rust-lang/crates.io-index"
678checksum = "7a31937dea023539c72ddae0e3571deadc1414b300483fa7aaec176168cfa9d2"
679dependencies = [
680 "winapi 0.3.9",
681]
682
683[[package]]
612name = "num" 684name = "num"
613version = "0.3.0" 685version = "0.3.0"
614source = "registry+https://github.com/rust-lang/crates.io-index" 686source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -687,6 +759,30 @@ dependencies = [
687] 759]
688 760
689[[package]] 761[[package]]
762name = "parking_lot"
763version = "0.10.2"
764source = "registry+https://github.com/rust-lang/crates.io-index"
765checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e"
766dependencies = [
767 "lock_api",
768 "parking_lot_core",
769]
770
771[[package]]
772name = "parking_lot_core"
773version = "0.7.2"
774source = "registry+https://github.com/rust-lang/crates.io-index"
775checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3"
776dependencies = [
777 "cfg-if",
778 "cloudabi",
779 "libc",
780 "redox_syscall",
781 "smallvec",
782 "winapi 0.3.9",
783]
784
785[[package]]
690name = "proc-macro-hack" 786name = "proc-macro-hack"
691version = "0.5.16" 787version = "0.5.16"
692source = "registry+https://github.com/rust-lang/crates.io-index" 788source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -694,9 +790,9 @@ checksum = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4"
694 790
695[[package]] 791[[package]]
696name = "proc-macro2" 792name = "proc-macro2"
697version = "1.0.18" 793version = "1.0.19"
698source = "registry+https://github.com/rust-lang/crates.io-index" 794source = "registry+https://github.com/rust-lang/crates.io-index"
699checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa" 795checksum = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12"
700dependencies = [ 796dependencies = [
701 "unicode-xid", 797 "unicode-xid",
702] 798]
@@ -764,6 +860,12 @@ dependencies = [
764] 860]
765 861
766[[package]] 862[[package]]
863name = "scopeguard"
864version = "1.1.0"
865source = "registry+https://github.com/rust-lang/crates.io-index"
866checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
867
868[[package]]
767name = "serde" 869name = "serde"
768version = "1.0.114" 870version = "1.0.114"
769source = "registry+https://github.com/rust-lang/crates.io-index" 871source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -801,6 +903,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
801checksum = "604508c1418b99dfe1925ca9224829bb2a8a9a04dda655cc01fcad46f4ab05ed" 903checksum = "604508c1418b99dfe1925ca9224829bb2a8a9a04dda655cc01fcad46f4ab05ed"
802dependencies = [ 904dependencies = [
803 "libc", 905 "libc",
906 "mio 0.7.0",
804 "signal-hook-registry", 907 "signal-hook-registry",
805] 908]
806 909
@@ -821,6 +924,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
821checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" 924checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
822 925
823[[package]] 926[[package]]
927name = "smallvec"
928version = "1.4.1"
929source = "registry+https://github.com/rust-lang/crates.io-index"
930checksum = "3757cb9d89161a2f24e1cf78efa0c1fcff485d18e3f55e0aa3480824ddaa0f3f"
931
932[[package]]
933name = "socket2"
934version = "0.3.12"
935source = "registry+https://github.com/rust-lang/crates.io-index"
936checksum = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918"
937dependencies = [
938 "cfg-if",
939 "libc",
940 "redox_syscall",
941 "winapi 0.3.9",
942]
943
944[[package]]
824name = "stable_deref_trait" 945name = "stable_deref_trait"
825version = "1.2.0" 946version = "1.2.0"
826source = "registry+https://github.com/rust-lang/crates.io-index" 947source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -840,9 +961,9 @@ checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c"
840 961
841[[package]] 962[[package]]
842name = "syn" 963name = "syn"
843version = "1.0.34" 964version = "1.0.35"
844source = "registry+https://github.com/rust-lang/crates.io-index" 965source = "registry+https://github.com/rust-lang/crates.io-index"
845checksum = "936cae2873c940d92e697597c5eee105fb570cd5689c695806f672883653349b" 966checksum = "fb7f4c519df8c117855e19dd8cc851e89eb746fe7a73f0157e0d95fdec5369b0"
846dependencies = [ 967dependencies = [
847 "proc-macro2", 968 "proc-macro2",
848 "quote", 969 "quote",
@@ -881,6 +1002,15 @@ dependencies = [
881] 1002]
882 1003
883[[package]] 1004[[package]]
1005name = "toml"
1006version = "0.5.6"
1007source = "registry+https://github.com/rust-lang/crates.io-index"
1008checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a"
1009dependencies = [
1010 "serde",
1011]
1012
1013[[package]]
884name = "typetag" 1014name = "typetag"
885version = "0.1.5" 1015version = "0.1.5"
886source = "registry+https://github.com/rust-lang/crates.io-index" 1016source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 9cf3639..fc18bb9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
1[package] 1[package]
2name = "dijo" 2name = "dijo"
3version = "0.1.4" 3version = "0.2.3"
4authors = ["Akshay <[email protected]>"] 4authors = ["Akshay <[email protected]>"]
5edition = "2018" 5edition = "2018"
6description = "Scriptable, curses-based, digital habit tracker" 6description = "Scriptable, curses-based, digital habit tracker"
@@ -19,11 +19,11 @@ typetag = "0.1.4"
19directories = "3.0.1" 19directories = "3.0.1"
20clap = "2.33" 20clap = "2.33"
21notify = "4.0" 21notify = "4.0"
22toml = "0.5.6"
22 23
23[dependencies.cursive] 24[dependencies.cursive]
24version = "0.15" 25version = "0.15"
25default-features = false 26default-features = false
26features = ["termion-backend"]
27 27
28[dependencies.chrono] 28[dependencies.chrono]
29version = "0.4" 29version = "0.4"
@@ -32,3 +32,8 @@ features = ["serde"]
32[dependencies.serde] 32[dependencies.serde]
33version = "1.0.103" 33version = "1.0.103"
34features = ["derive"] 34features = ["derive"]
35
36[features]
37default = ["termion-backend"]
38termion-backend = ["cursive/termion-backend"]
39crossterm-backend = ["cursive/crossterm-backend"]
diff --git a/readme.md b/readme.md
index fbca787..c626432 100644
--- a/readme.md
+++ b/readme.md
@@ -19,7 +19,7 @@ much like a certain text editor.
19 - **vim like command mode**: add with `:add`, delete with 19 - **vim like command mode**: add with `:add`, delete with
20 `:delete` and above all, quit with `:q`!. 20 `:delete` and above all, quit with `:q`!.
21 - **fully scriptable**: [configure `dijo` to 21 - **fully scriptable**: [configure `dijo` to
22 track your `git` commits](./Auto-Habits)! 22 track your `git` commits](https://github.com/NerdyPepper/dijo/wiki/Auto-Habits)!
23 23
24### Install 24### Install
25 25
@@ -32,6 +32,12 @@ $ rustup update
32$ cargo install dijo 32$ cargo install dijo
33``` 33```
34 34
35`dijo` on nixpkgs (maintained by [@Infinisil](https://github.com/Infinisil)):
36
37```
38$ nix-env -f channel:nixpkgs-unstable -iA dijo
39```
40
35If you aren't familiar with `cargo` or Rust, read the [complete 41If you aren't familiar with `cargo` or Rust, read the [complete
36installation](https://github.com/NerdyPepper/dijo/wiki/Install) 42installation](https://github.com/NerdyPepper/dijo/wiki/Install)
37guide. 43guide.
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);
diff --git a/src/command.rs b/src/command.rs
index f856b00..4f3e491 100644
--- a/src/command.rs
+++ b/src/command.rs
@@ -1,21 +1,73 @@
1use std::fmt; 1use std::fmt;
2 2
3use cursive::event::{Event, EventResult, Key};
3use cursive::theme::{BaseColor, Color, ColorStyle}; 4use cursive::theme::{BaseColor, Color, ColorStyle};
4use cursive::view::Resizable; 5use cursive::view::Resizable;
5use cursive::views::{EditView, LinearLayout, TextView}; 6use cursive::views::{EditView, LinearLayout, OnEventView, TextView};
6use cursive::Cursive; 7use cursive::Cursive;
7 8
8use crate::{app::App, CONFIGURATION}; 9use crate::app::App;
10use crate::utils::{GRID_WIDTH, VIEW_WIDTH};
11
12static COMMANDS: &'static [&'static str] = &[
13 "add",
14 "add-auto",
15 "delete",
16 "track-up",
17 "track-down",
18 "month-prev",
19 "month-next",
20 "quit",
21 "write",
22 "help",
23];
24
25fn get_command_completion(prefix: &str) -> Option<String> {
26 let first_match = COMMANDS.iter().filter(|&x| x.starts_with(prefix)).next();
27 return first_match.map(|&x| x.into());
28}
29
30fn get_habit_completion(prefix: &str, habit_names: &[String]) -> Option<String> {
31 let first_match = habit_names.iter().filter(|&x| x.starts_with(prefix)).next();
32 return first_match.map(|x| x.into());
33}
9 34
10pub fn open_command_window(s: &mut Cursive) { 35pub fn open_command_window(s: &mut Cursive) {
11 let command_window = EditView::new() 36 let habit_list: Vec<String> = s
12 .filler(" ") 37 .call_on_name("Main", |view: &mut App| {
13 .on_submit(call_on_app) 38 return view.list_habits();
14 .style(ColorStyle::new( 39 })
15 Color::Dark(BaseColor::Black), 40 .unwrap();
16 Color::Dark(BaseColor::White), 41 let style = ColorStyle::new(Color::Dark(BaseColor::Black), Color::Dark(BaseColor::White));
17 )) 42 let command_window = OnEventView::new(
18 .fixed_width(CONFIGURATION.view_width * CONFIGURATION.grid_width); 43 EditView::new()
44 .filler(" ")
45 .on_submit(call_on_app)
46 .style(style),
47 )
48 .on_event_inner(
49 Event::Key(Key::Tab),
50 move |view: &mut EditView, _: &Event| {
51 let contents = view.get_content();
52 if !contents.contains(" ") {
53 let completion = get_command_completion(&*contents);
54 if let Some(c) = completion {
55 let cb = view.set_content(c);
56 return Some(EventResult::Consumed(Some(cb)));
57 };
58 return None;
59 } else {
60 let word = contents.split(' ').last().unwrap();
61 let completion = get_habit_completion(word, &habit_list);
62 if let Some(c) = completion {
63 let cb = view.set_content(format!("{}", contents) + &c[word.len()..]);
64 return Some(EventResult::Consumed(Some(cb)));
65 };
66 return None;
67 }
68 },
69 )
70 .fixed_width(VIEW_WIDTH * GRID_WIDTH);
19 s.call_on_name("Frame", |view: &mut LinearLayout| { 71 s.call_on_name("Frame", |view: &mut LinearLayout| {
20 let mut commandline = LinearLayout::horizontal() 72 let mut commandline = LinearLayout::horizontal()
21 .child(TextView::new(":")) 73 .child(TextView::new(":"))
@@ -59,15 +111,17 @@ pub enum Command {
59 Delete(String), 111 Delete(String),
60 TrackUp(String), 112 TrackUp(String),
61 TrackDown(String), 113 TrackDown(String),
114 Help(Option<String>),
115 Write,
62 Quit, 116 Quit,
63 Blank, 117 Blank,
64} 118}
65 119
66#[derive(Debug)] 120#[derive(Debug)]
67pub enum CommandLineError { 121pub enum CommandLineError {
68 InvalidCommand(String), 122 InvalidCommand(String), // command name
69 InvalidArg(u32), // position 123 InvalidArg(u32), // position
70 NotEnoughArgs(String, u32), 124 NotEnoughArgs(String, u32), // command name, required no. of args
71} 125}
72 126
73impl std::error::Error for CommandLineError {} 127impl std::error::Error for CommandLineError {}
@@ -134,9 +188,16 @@ impl Command {
134 } 188 }
135 return Ok(Command::TrackDown(args[0].to_string())); 189 return Ok(Command::TrackDown(args[0].to_string()));
136 } 190 }
191 "h" | "?" | "help" => {
192 if args.is_empty() {
193 return Ok(Command::Help(None));
194 }
195 return Ok(Command::Help(Some(args[0].to_string())));
196 }
137 "mprev" | "month-prev" => return Ok(Command::MonthPrev), 197 "mprev" | "month-prev" => return Ok(Command::MonthPrev),
138 "mnext" | "month-next" => return Ok(Command::MonthNext), 198 "mnext" | "month-next" => return Ok(Command::MonthNext),
139 "q" | "quit" => return Ok(Command::Quit), 199 "q" | "quit" => return Ok(Command::Quit),
200 "w" | "write" => return Ok(Command::Write),
140 "" => return Ok(Command::Blank), 201 "" => return Ok(Command::Blank),
141 s => return Err(CommandLineError::InvalidCommand(s.into())), 202 s => return Err(CommandLineError::InvalidCommand(s.into())),
142 } 203 }
diff --git a/src/habit/bit.rs b/src/habit/bit.rs
index 8fa14c2..2bbb0ac 100644
--- a/src/habit/bit.rs
+++ b/src/habit/bit.rs
@@ -18,9 +18,9 @@ impl fmt::Display for CustomBool {
18 f, 18 f,
19 "{:^3}", 19 "{:^3}",
20 if self.0 { 20 if self.0 {
21 CONFIGURATION.true_chr 21 CONFIGURATION.look.true_chr
22 } else { 22 } else {
23 CONFIGURATION.false_chr 23 CONFIGURATION.look.false_chr
24 } 24 }
25 ) 25 )
26 } 26 }
diff --git a/src/main.rs b/src/main.rs
index 14ddf03..5280b35 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -12,7 +12,13 @@ use crate::command::{open_command_window, Command};
12use crate::utils::{load_configuration_file, AppConfig}; 12use crate::utils::{load_configuration_file, AppConfig};
13 13
14use clap::{App as ClapApp, Arg}; 14use clap::{App as ClapApp, Arg};
15
16#[cfg(any(feature = "termion-backend", feature = "default"))]
15use cursive::termion; 17use cursive::termion;
18
19#[cfg(feature = "crossterm-backend")]
20use cursive::crossterm;
21
16use cursive::views::{LinearLayout, NamedView}; 22use cursive::views::{LinearLayout, NamedView};
17use lazy_static::lazy_static; 23use lazy_static::lazy_static;
18 24
@@ -33,6 +39,14 @@ fn main() {
33 .value_name("CMD") 39 .value_name("CMD")
34 .help("run a dijo command"), 40 .help("run a dijo command"),
35 ) 41 )
42 .arg(
43 Arg::with_name("list")
44 .short("l")
45 .long("list")
46 .takes_value(false)
47 .help("list dijo habits")
48 .conflicts_with("command"),
49 )
36 .get_matches(); 50 .get_matches();
37 if let Some(c) = matches.value_of("command") { 51 if let Some(c) = matches.value_of("command") {
38 let command = Command::from_string(c); 52 let command = Command::from_string(c);
@@ -49,8 +63,17 @@ fn main() {
49 "Commands other than `track-up` and `track-down` are currently not supported!" 63 "Commands other than `track-up` and `track-down` are currently not supported!"
50 ), 64 ),
51 } 65 }
66 } else if matches.is_present("list") {
67 for h in App::load_state().list_habits() {
68 println!("{}", h);
69 }
52 } else { 70 } else {
71 #[cfg(any(feature = "termion-backend", feature = "default"))]
53 let mut s = termion().unwrap(); 72 let mut s = termion().unwrap();
73
74 #[cfg(feature = "crossterm-backend")]
75 let mut s = crossterm().unwrap();
76
54 let app = App::load_state(); 77 let app = App::load_state();
55 let layout = NamedView::new( 78 let layout = NamedView::new(
56 "Frame", 79 "Frame",
diff --git a/src/theme.rs b/src/theme.rs
index 4194777..1d2cc36 100644
--- a/src/theme.rs
+++ b/src/theme.rs
@@ -1,18 +1,18 @@
1use cursive::theme::Color::*; 1use cursive::theme::Color::*;
2use cursive::theme::PaletteColor::*; 2use cursive::theme::PaletteColor::*;
3use cursive::theme::{BaseColor, BorderStyle, Palette, Theme}; 3use cursive::theme::{BorderStyle, Palette, 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();
7 p[Background] = Dark(BaseColor::Black); 7 p[Background] = TerminalDefault;
8 p[Shadow] = Light(BaseColor::Black); 8 p[Shadow] = TerminalDefault;
9 p[View] = Dark(BaseColor::Black); 9 p[View] = TerminalDefault;
10 p[Primary] = Dark(BaseColor::White); 10 p[Primary] = TerminalDefault;
11 p[Secondary] = Dark(BaseColor::Black); 11 p[Secondary] = TerminalDefault;
12 p[Tertiary] = Dark(BaseColor::Green); 12 p[Tertiary] = TerminalDefault;
13 p[TitlePrimary] = Light(BaseColor::White); 13 p[TitlePrimary] = TerminalDefault;
14 p[Highlight] = Dark(BaseColor::Red); 14 p[Highlight] = TerminalDefault;
15 p[HighlightInactive] = Dark(BaseColor::Black); 15 p[HighlightInactive] = TerminalDefault;
16 16
17 return p; 17 return p;
18} 18}
diff --git a/src/utils.rs b/src/utils.rs
index e6ec6ac..2453aa6 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -1,37 +1,117 @@
1use cursive::theme::{BaseColor, Color}; 1use cursive::theme::{BaseColor, Color};
2use directories::ProjectDirs; 2use directories::ProjectDirs;
3use std::fs; 3use serde::{Deserialize, Serialize};
4
5use std;
6use std::default::Default;
7use std::fs::{self, File, OpenOptions};
8use std::io::{Read, Write};
4use std::path::PathBuf; 9use std::path::PathBuf;
5 10
6pub struct AppConfig { 11pub const VIEW_WIDTH: usize = 25;
12pub const VIEW_HEIGHT: usize = 8;
13pub const GRID_WIDTH: usize = 3;
14
15#[derive(Serialize, Deserialize)]
16pub struct Characters {
17 #[serde(default = "base_char")]
7 pub true_chr: char, 18 pub true_chr: char,
19 #[serde(default = "base_char")]
8 pub false_chr: char, 20 pub false_chr: char,
21 #[serde(default = "base_char")]
9 pub future_chr: char, 22 pub future_chr: char,
23}
10 24
11 // view dimensions 25fn base_char() -> char {
12 pub view_width: usize, 26 '·'
13 pub view_height: usize, 27}
14 28
15 // app dimensions 29impl Default for Characters {
16 pub grid_width: usize, 30 fn default() -> Self {
31 Characters {
32 true_chr: '·',
33 false_chr: '·',
34 future_chr: '·',
35 }
36 }
37}
17 38
18 pub reached_color: Color, 39#[derive(Serialize, Deserialize)]
19 pub todo_color: Color, 40pub struct Colors {
20 pub future_color: Color, 41 #[serde(default = "cyan")]
42 pub reached: String,
43 #[serde(default = "magenta")]
44 pub todo: String,
45 #[serde(default = "light_black")]
46 pub inactive: String,
47}
48
49fn cyan() -> String {
50 "cyan".into()
51}
52fn magenta() -> String {
53 "magenta".into()
54}
55fn light_black() -> String {
56 "light black".into()
57}
58
59impl Default for Colors {
60 fn default() -> Self {
61 Colors {
62 reached: cyan(),
63 todo: magenta(),
64 inactive: light_black(),
65 }
66 }
67}
68
69#[derive(Serialize, Deserialize)]
70pub struct AppConfig {
71 #[serde(default)]
72 pub look: Characters,
73
74 #[serde(default)]
75 pub colors: Colors,
76}
77
78impl Default for AppConfig {
79 fn default() -> Self {
80 AppConfig {
81 look: Default::default(),
82 colors: Default::default(),
83 }
84 }
85}
86
87impl AppConfig {
88 // TODO: implement string parsing from config.json
89 pub fn reached_color(&self) -> Color {
90 return Color::parse(&self.colors.reached).unwrap_or(Color::Dark(BaseColor::Cyan));
91 }
92 pub fn todo_color(&self) -> Color {
93 return Color::parse(&self.colors.todo).unwrap_or(Color::Dark(BaseColor::Magenta));
94 }
95 pub fn inactive_color(&self) -> Color {
96 return Color::parse(&self.colors.inactive).unwrap_or(Color::Light(BaseColor::Black));
97 }
21} 98}
22 99
23pub fn load_configuration_file() -> AppConfig { 100pub fn load_configuration_file() -> AppConfig {
24 return AppConfig { 101 let cf = config_file();
25 true_chr: '·', 102 if let Ok(ref mut f) = File::open(&cf) {
26 false_chr: '·', 103 let mut j = String::new();
27 future_chr: '·', 104 f.read_to_string(&mut j);
28 view_width: 25, 105 return toml::from_str(&j).unwrap();
29 view_height: 8, 106 } else {
30 grid_width: 3, 107 if let Ok(dc) = toml::to_string(&AppConfig::default()) {
31 reached_color: Color::Dark(BaseColor::Cyan), 108 match OpenOptions::new().create(true).write(true).open(&cf) {
32 todo_color: Color::Dark(BaseColor::Magenta), 109 Ok(ref mut file) => file.write(dc.as_bytes()).unwrap(),
33 future_color: Color::Light(BaseColor::Black), 110 Err(_) => 0,
34 }; 111 };
112 }
113 return Default::default();
114 }
35} 115}
36 116
37fn project_dirs() -> ProjectDirs { 117fn project_dirs() -> ProjectDirs {
@@ -39,6 +119,14 @@ fn project_dirs() -> ProjectDirs {
39 .unwrap_or_else(|| panic!("Invalid home directory!")) 119 .unwrap_or_else(|| panic!("Invalid home directory!"))
40} 120}
41 121
122pub fn config_file() -> PathBuf {
123 let proj_dirs = project_dirs();
124 let mut data_file = PathBuf::from(proj_dirs.config_dir());
125 fs::create_dir_all(&data_file);
126 data_file.push("config.toml");
127 return data_file;
128}
129
42pub fn habit_file() -> PathBuf { 130pub fn habit_file() -> PathBuf {
43 let proj_dirs = project_dirs(); 131 let proj_dirs = project_dirs();
44 let mut data_file = PathBuf::from(proj_dirs.data_dir()); 132 let mut data_file = PathBuf::from(proj_dirs.data_dir());
diff --git a/src/views.rs b/src/views.rs
index 24c8a4d..efd1391 100644
--- a/src/views.rs
+++ b/src/views.rs
@@ -8,6 +8,7 @@ use chrono::prelude::*;
8use chrono::{Duration, Local, NaiveDate}; 8use chrono::{Duration, Local, NaiveDate};
9 9
10use crate::habit::{Bit, Count, Habit, TrackEvent, ViewMode}; 10use crate::habit::{Bit, Count, Habit, TrackEvent, ViewMode};
11use crate::utils::VIEW_WIDTH;
11 12
12use crate::CONFIGURATION; 13use crate::CONFIGURATION;
13 14
@@ -36,14 +37,14 @@ where
36 let year = now.year(); 37 let year = now.year();
37 let month = now.month(); 38 let month = now.month();
38 39
39 let goal_reached_style = Style::from(CONFIGURATION.reached_color); 40 let goal_reached_style = Style::from(CONFIGURATION.reached_color());
40 let todo_style = Style::from(CONFIGURATION.todo_color); 41 let todo_style = Style::from(CONFIGURATION.todo_color());
41 let future_style = Style::from(CONFIGURATION.future_color); 42 let future_style = Style::from(CONFIGURATION.inactive_color());
42 43
43 let strikethrough = Style::from(Effect::Strikethrough); 44 let strikethrough = Style::from(Effect::Strikethrough);
44 45
45 let goal_status = 46 let goal_status =
46 self.view_month_offset() == 0 && self.reached_goal(Local::now().naive_utc().date()); 47 self.view_month_offset() == 0 && self.reached_goal(Local::now().naive_local().date());
47 48
48 printer.with_style( 49 printer.with_style(
49 Style::merge(&[ 50 Style::merge(&[
@@ -61,11 +62,7 @@ where
61 |p| { 62 |p| {
62 p.print( 63 p.print(
63 (0, 0), 64 (0, 0),
64 &format!( 65 &format!(" {:.width$} ", self.name(), width = VIEW_WIDTH - 6),
65 " {:.width$} ",
66 self.name(),
67 width = CONFIGURATION.view_width - 6
68 ),
69 ); 66 );
70 }, 67 },
71 ); 68 );
@@ -77,12 +74,20 @@ where
77 .collect::<Vec<_>>(); 74 .collect::<Vec<_>>();
78 for (week, line_nr) in days.chunks(7).zip(2..) { 75 for (week, line_nr) in days.chunks(7).zip(2..) {
79 let weekly_goal = self.goal() * week.len() as u32; 76 let weekly_goal = self.goal() * week.len() as u32;
80 let is_this_week = week.contains(&Local::now().naive_utc().date()); 77 let is_this_week = week.contains(&Local::now().naive_local().date());
81 let remaining = week.iter().map(|&i| self.remaining(i)).sum::<u32>(); 78 let remaining = week.iter().map(|&i| self.remaining(i)).sum::<u32>();
82 let completions = weekly_goal - remaining; 79 let completions = weekly_goal - remaining;
83 let full = CONFIGURATION.view_width - 8; 80 let full = VIEW_WIDTH - 8;
84 let bars_to_fill = if weekly_goal > 0 {(completions * full as u32) / weekly_goal} else {0}; 81 let bars_to_fill = if weekly_goal > 0 {
85 let percentage = if weekly_goal > 0 {(completions as f64 * 100.) / weekly_goal as f64} else {0.0}; 82 (completions * full as u32) / weekly_goal
83 } else {
84 0
85 };
86 let percentage = if weekly_goal > 0 {
87 (completions as f64 * 100.) / weekly_goal as f64
88 } else {
89 0.0
90 };
86 printer.with_style(future_style, |p| { 91 printer.with_style(future_style, |p| {
87 p.print((4, line_nr), &"─".repeat(full)); 92 p.print((4, line_nr), &"─".repeat(full));
88 }); 93 });
@@ -118,7 +123,7 @@ where
118 }); 123 });
119 } else { 124 } else {
120 printer.with_style(future_style, |p| { 125 printer.with_style(future_style, |p| {
121 p.print(coords, &format!("{:^3}", CONFIGURATION.future_chr)); 126 p.print(coords, &format!("{:^3}", CONFIGURATION.look.future_chr));
122 }); 127 });
123 } 128 }
124 i += 1; 129 i += 1;
@@ -141,7 +146,7 @@ where
141 } 146 }
142 147
143 fn on_event(&mut self, e: Event) -> EventResult { 148 fn on_event(&mut self, e: Event) -> EventResult {
144 let now = Local::now().naive_utc().date(); 149 let now = Local::now().naive_local().date();
145 if self.is_auto() { 150 if self.is_auto() {
146 return EventResult::Ignored; 151 return EventResult::Ignored;
147 } 152 }