mirror of
https://github.com/kdheepak/taskwarrior-tui.git
synced 2025-08-26 21:27:19 +02:00
Add initial calendar support
This commit is contained in:
parent
eb8e5d376e
commit
2513642aa3
5 changed files with 317 additions and 42 deletions
16
Cargo.lock
generated
16
Cargo.lock
generated
|
@ -199,6 +199,12 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd56b59865bce947ac5958779cfa508f6c3b9497cc762b7e24a12d11ccde2c4f"
|
||||
|
||||
[[package]]
|
||||
name = "failure"
|
||||
version = "0.1.8"
|
||||
|
@ -259,6 +265,15 @@ version = "1.0.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.6"
|
||||
|
@ -608,6 +623,7 @@ dependencies = [
|
|||
"chrono",
|
||||
"clap",
|
||||
"crossterm",
|
||||
"itertools",
|
||||
"rand",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
|
|
@ -18,6 +18,7 @@ default = ["crossterm-backend"]
|
|||
crossterm-backend = ["tui/crossterm", "crossterm"]
|
||||
|
||||
[dependencies]
|
||||
itertools = "0.9"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
clap = "2.33"
|
||||
|
|
76
src/app.rs
76
src/app.rs
|
@ -13,6 +13,7 @@ use task_hookrs::uda::UDAValue;
|
|||
|
||||
use chrono::{Local, NaiveDateTime, TimeZone};
|
||||
|
||||
use crate::calendar::Calendar;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::{sync::mpsc, thread, time::Duration};
|
||||
use tui::{
|
||||
|
@ -111,14 +112,15 @@ fn centered_rect(percent_x: u16, percent_y: u16, r: Rect) -> Rect {
|
|||
}
|
||||
|
||||
pub enum AppMode {
|
||||
Report,
|
||||
Filter,
|
||||
AddTask,
|
||||
AnnotateTask,
|
||||
LogTask,
|
||||
ModifyTask,
|
||||
HelpPopup,
|
||||
TaskReport,
|
||||
TaskFilter,
|
||||
TaskAdd,
|
||||
TaskAnnotate,
|
||||
TaskLog,
|
||||
TaskModify,
|
||||
TaskHelpPopup,
|
||||
TaskError,
|
||||
Calendar,
|
||||
}
|
||||
|
||||
pub struct TTApp {
|
||||
|
@ -153,7 +155,7 @@ impl TTApp {
|
|||
command: "".to_string(),
|
||||
modify: "".to_string(),
|
||||
error: "".to_string(),
|
||||
mode: AppMode::Report,
|
||||
mode: AppMode::TaskReport,
|
||||
colors: TColorConfig::default(),
|
||||
};
|
||||
app.get_context();
|
||||
|
@ -186,6 +188,32 @@ impl TTApp {
|
|||
}
|
||||
|
||||
pub fn draw(&mut self, f: &mut Frame<impl Backend>) {
|
||||
match self.mode {
|
||||
AppMode::TaskReport
|
||||
| AppMode::TaskFilter
|
||||
| AppMode::TaskAdd
|
||||
| AppMode::TaskAnnotate
|
||||
| AppMode::TaskError
|
||||
| AppMode::TaskHelpPopup
|
||||
| AppMode::TaskLog
|
||||
| AppMode::TaskModify => self.draw_task(f),
|
||||
AppMode::Calendar => self.draw_calendar(f),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw_calendar(&mut self, f: &mut Frame<impl Backend>) {
|
||||
let rects = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints([Constraint::Min(0)].as_ref())
|
||||
.split(f.size());
|
||||
let c = &Calendar::new(2020).to_string()[..];
|
||||
let p = Paragraph::new(Text::from(c))
|
||||
.alignment(Alignment::Left)
|
||||
.block(Block::default().borders(Borders::ALL).title("Calendar"));
|
||||
f.render_widget(p, rects[0]);
|
||||
}
|
||||
|
||||
pub fn draw_task(&mut self, f: &mut Frame<impl Backend>) {
|
||||
let tasks_is_empty = self.tasks.lock().unwrap().is_empty();
|
||||
let tasks_len = self.tasks.lock().unwrap().len();
|
||||
while !tasks_is_empty && self.state.selected().unwrap_or_default() >= tasks_len {
|
||||
|
@ -210,13 +238,13 @@ impl TTApp {
|
|||
.unwrap_or_default()
|
||||
};
|
||||
match self.mode {
|
||||
AppMode::Report => self.draw_command(f, rects[1], &self.filter[..], "Filter Tasks"),
|
||||
AppMode::Filter => {
|
||||
AppMode::TaskReport => self.draw_command(f, rects[1], &self.filter[..], "Filter Tasks"),
|
||||
AppMode::TaskFilter => {
|
||||
f.render_widget(Clear, rects[1]);
|
||||
f.set_cursor(rects[1].x + self.cursor_location as u16 + 1, rects[1].y + 1);
|
||||
self.draw_command(f, rects[1], &self.filter[..], "Filter Tasks");
|
||||
}
|
||||
AppMode::ModifyTask => {
|
||||
AppMode::TaskModify => {
|
||||
f.set_cursor(rects[1].x + self.cursor_location as u16 + 1, rects[1].y + 1);
|
||||
f.render_widget(Clear, rects[1]);
|
||||
self.draw_command(
|
||||
|
@ -226,12 +254,12 @@ impl TTApp {
|
|||
format!("Modify Task {}", task_id).as_str(),
|
||||
);
|
||||
}
|
||||
AppMode::LogTask => {
|
||||
AppMode::TaskLog => {
|
||||
f.set_cursor(rects[1].x + self.cursor_location as u16 + 1, rects[1].y + 1);
|
||||
f.render_widget(Clear, rects[1]);
|
||||
self.draw_command(f, rects[1], &self.command[..], "Log Task");
|
||||
}
|
||||
AppMode::AnnotateTask => {
|
||||
AppMode::TaskAnnotate => {
|
||||
f.set_cursor(rects[1].x + self.cursor_location as u16 + 1, rects[1].y + 1);
|
||||
f.render_widget(Clear, rects[1]);
|
||||
self.draw_command(
|
||||
|
@ -241,7 +269,7 @@ impl TTApp {
|
|||
format!("Annotate Task {}", task_id).as_str(),
|
||||
);
|
||||
}
|
||||
AppMode::AddTask => {
|
||||
AppMode::TaskAdd => {
|
||||
f.set_cursor(rects[1].x + self.cursor_location as u16 + 1, rects[1].y + 1);
|
||||
f.render_widget(Clear, rects[1]);
|
||||
self.draw_command(f, rects[1], &self.command[..], "Add Task");
|
||||
|
@ -250,10 +278,13 @@ impl TTApp {
|
|||
f.render_widget(Clear, rects[1]);
|
||||
self.draw_command(f, rects[1], &self.error[..], "Error");
|
||||
}
|
||||
AppMode::HelpPopup => {
|
||||
AppMode::TaskHelpPopup => {
|
||||
self.draw_command(f, rects[1], &self.filter[..], "Filter Tasks");
|
||||
self.draw_help_popup(f, f.size());
|
||||
}
|
||||
_ => {
|
||||
panic!("Reached unreachable code. Something went wrong");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -546,12 +577,21 @@ impl TTApp {
|
|||
match attribute {
|
||||
"id" => task.id().unwrap_or_default().to_string(),
|
||||
"due" => match task.due() {
|
||||
Some(v) => vague_format_date_time(Local::now().naive_utc(), NaiveDateTime::new(v.date(), v.time())),
|
||||
Some(v) => vague_format_date_time(
|
||||
Local::now().naive_utc(),
|
||||
NaiveDateTime::new(v.date(), v.time()),
|
||||
),
|
||||
None => "".to_string(),
|
||||
},
|
||||
"entry" => vague_format_date_time(NaiveDateTime::new(task.entry().date(), task.entry().time()), Local::now().naive_utc()),
|
||||
"entry" => vague_format_date_time(
|
||||
NaiveDateTime::new(task.entry().date(), task.entry().time()),
|
||||
Local::now().naive_utc(),
|
||||
),
|
||||
"start" => match task.start() {
|
||||
Some(v) => vague_format_date_time(NaiveDateTime::new(v.date(), v.time()), Local::now().naive_utc()),
|
||||
Some(v) => vague_format_date_time(
|
||||
NaiveDateTime::new(v.date(), v.time()),
|
||||
Local::now().naive_utc(),
|
||||
),
|
||||
None => "".to_string(),
|
||||
},
|
||||
"description" => task.description().to_string(),
|
||||
|
|
206
src/calendar.rs
Normal file
206
src/calendar.rs
Normal file
|
@ -0,0 +1,206 @@
|
|||
// Based on https://play.rust-lang.org/?gist=1057364daeee4cff472a&version=nightly
|
||||
// See: https://old.reddit.com/r/rust/comments/37b6oo/the_calendar_example_challenge/crlmbsg/
|
||||
|
||||
use std::fmt;
|
||||
|
||||
const COL_WIDTH: usize = 21;
|
||||
|
||||
use Day::*;
|
||||
use Month::*;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(u8)]
|
||||
pub enum Day {
|
||||
Sun,
|
||||
Mon,
|
||||
Tue,
|
||||
Wed,
|
||||
Thu,
|
||||
Fri,
|
||||
Sat,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum Month {
|
||||
Jan,
|
||||
Feb,
|
||||
Mar,
|
||||
Apr,
|
||||
May,
|
||||
Jun,
|
||||
Jul,
|
||||
Aug,
|
||||
Sep,
|
||||
Oct,
|
||||
Nov,
|
||||
Dec,
|
||||
}
|
||||
impl Month {
|
||||
fn len(self) -> u8 {
|
||||
match self {
|
||||
Jan => 31,
|
||||
Feb => 28,
|
||||
Mar => 31,
|
||||
Apr => 30,
|
||||
May => 31,
|
||||
Jun => 30,
|
||||
Jul => 31,
|
||||
Aug => 31,
|
||||
Sep => 30,
|
||||
Oct => 31,
|
||||
Nov => 30,
|
||||
Dec => 31,
|
||||
}
|
||||
}
|
||||
fn leap_len(self, leap_year: bool) -> u8 {
|
||||
match self {
|
||||
Feb => {
|
||||
if leap_year {
|
||||
29
|
||||
} else {
|
||||
28
|
||||
}
|
||||
}
|
||||
mon => mon.len(),
|
||||
}
|
||||
}
|
||||
fn first_day(self, year: i64) -> Day {
|
||||
let y = year - 1;
|
||||
let jan_first = (1 + (5 * (y % 4)) + (4 * (y % 100)) + (6 * (y % 400))) % 7;
|
||||
let mut len = 0;
|
||||
for m in Jan {
|
||||
if m == self {
|
||||
break;
|
||||
}
|
||||
len += m.leap_len(is_leap_year(year)) as i64;
|
||||
}
|
||||
match (len + jan_first) % 7 {
|
||||
0 => Sun,
|
||||
1 => Mon,
|
||||
2 => Tue,
|
||||
3 => Wed,
|
||||
4 => Thu,
|
||||
5 => Fri,
|
||||
_ => Sat,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Iterator for Month {
|
||||
type Item = Month;
|
||||
fn next(&mut self) -> Option<Month> {
|
||||
let ret = Some(*self);
|
||||
*self = match *self {
|
||||
Jan => Feb,
|
||||
Feb => Mar,
|
||||
Mar => Apr,
|
||||
Apr => May,
|
||||
May => Jun,
|
||||
Jun => Jul,
|
||||
Jul => Aug,
|
||||
Aug => Sep,
|
||||
Sep => Oct,
|
||||
Oct => Nov,
|
||||
Nov => Dec,
|
||||
Dec => Jan,
|
||||
};
|
||||
ret
|
||||
}
|
||||
}
|
||||
impl fmt::Display for Month {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let name = match *self {
|
||||
Jan => "January",
|
||||
Feb => "February",
|
||||
Mar => "March",
|
||||
Apr => "April",
|
||||
May => "May",
|
||||
Jun => "June",
|
||||
Jul => "July",
|
||||
Aug => "August",
|
||||
Sep => "September",
|
||||
Oct => "October",
|
||||
Nov => "November",
|
||||
Dec => "December",
|
||||
};
|
||||
let padding = COL_WIDTH - name.len();
|
||||
write!(f, "{:1$}", "", padding / 2)?;
|
||||
if padding % 2 != 0 {
|
||||
f.write_str(" ")?;
|
||||
}
|
||||
f.write_str(name)?;
|
||||
write!(f, "{:1$}", "", padding / 2)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Calendar {
|
||||
pub year: i64,
|
||||
}
|
||||
|
||||
impl Calendar {
|
||||
pub fn new(year: i64) -> Self {
|
||||
Self { year }
|
||||
}
|
||||
}
|
||||
|
||||
fn is_leap_year(year: i64) -> bool {
|
||||
(year % 4 == 0 && year % 100 != 0) || year % 400 == 0
|
||||
}
|
||||
|
||||
impl fmt::Display for Calendar {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let cols = f.width().unwrap_or(3);
|
||||
let year = self.year;
|
||||
let leap_year = is_leap_year(year);
|
||||
let months = [Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec];
|
||||
let mut dates = [
|
||||
0..Jan.len(),
|
||||
0..Feb.leap_len(leap_year),
|
||||
0..Mar.len(),
|
||||
0..Apr.len(),
|
||||
0..May.len(),
|
||||
0..Jun.len(),
|
||||
0..Jul.len(),
|
||||
0..Aug.len(),
|
||||
0..Sep.len(),
|
||||
0..Oct.len(),
|
||||
0..Nov.len(),
|
||||
0..Dec.len(),
|
||||
];
|
||||
let chunks = dates.chunks_mut(cols).zip(months.chunks(cols));
|
||||
for (days_chunk, months) in chunks {
|
||||
for month in months {
|
||||
write!(f, "{:>1$} ", month, COL_WIDTH)?;
|
||||
}
|
||||
f.write_str("\n")?;
|
||||
for month in months {
|
||||
write!(f, "{:>1$} ", " S M T W T F S", COL_WIDTH)?;
|
||||
}
|
||||
f.write_str("\n")?;
|
||||
for (days, mon) in days_chunk.iter_mut().zip(months.iter()) {
|
||||
let first_day = mon.first_day(year) as u8;
|
||||
for _ in 0..(first_day) {
|
||||
f.write_str(" ")?;
|
||||
}
|
||||
for _ in 0..(7 - first_day) {
|
||||
write!(f, "{:>3}", days.next().unwrap() + 1)?;
|
||||
}
|
||||
f.write_str(" ")?;
|
||||
}
|
||||
f.write_str("\n")?;
|
||||
while !days_chunk.iter().all(|r| r.start == r.end) {
|
||||
for days in days_chunk.iter_mut() {
|
||||
for _ in 0..7 {
|
||||
match days.next() {
|
||||
Some(s) => write!(f, "{:>3}", s + 1)?,
|
||||
None => f.write_str(" ")?,
|
||||
}
|
||||
}
|
||||
f.write_str(" ")?;
|
||||
}
|
||||
f.write_str("\n")?;
|
||||
}
|
||||
f.write_str("\n")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
60
src/main.rs
60
src/main.rs
|
@ -3,6 +3,7 @@
|
|||
#![allow(unused_variables)]
|
||||
|
||||
mod app;
|
||||
mod calendar;
|
||||
mod color;
|
||||
mod util;
|
||||
|
||||
|
@ -57,8 +58,11 @@ fn tui_main(_config: &str) -> Result<(), Box<dyn Error>> {
|
|||
// Handle input
|
||||
match events.next()? {
|
||||
Event::Input(input) => match app.mode {
|
||||
AppMode::Report => match input {
|
||||
AppMode::TaskReport => match input {
|
||||
Key::Ctrl('c') | Key::Char('q') => app.should_quit = true,
|
||||
Key::Char(']') => {
|
||||
app.mode = AppMode::Calendar;
|
||||
}
|
||||
Key::Char('r') => app.update(),
|
||||
Key::Down | Key::Char('j') => app.next(),
|
||||
Key::Up | Key::Char('k') => app.previous(),
|
||||
|
@ -103,7 +107,7 @@ fn tui_main(_config: &str) -> Result<(), Box<dyn Error>> {
|
|||
}
|
||||
}
|
||||
Key::Char('m') => {
|
||||
app.mode = AppMode::ModifyTask;
|
||||
app.mode = AppMode::TaskModify;
|
||||
match app.task_current() {
|
||||
Some(t) => app.modify = t.description().to_string(),
|
||||
None => app.modify = "".to_string(),
|
||||
|
@ -111,35 +115,35 @@ fn tui_main(_config: &str) -> Result<(), Box<dyn Error>> {
|
|||
app.cursor_location = app.modify.chars().count();
|
||||
}
|
||||
Key::Char('l') => {
|
||||
app.mode = AppMode::LogTask;
|
||||
app.mode = AppMode::TaskLog;
|
||||
}
|
||||
Key::Char('a') => {
|
||||
app.mode = AppMode::AddTask;
|
||||
app.mode = AppMode::TaskAdd;
|
||||
app.cursor_location = app.command.chars().count();
|
||||
}
|
||||
Key::Char('A') => {
|
||||
app.mode = AppMode::AnnotateTask;
|
||||
app.mode = AppMode::TaskAnnotate;
|
||||
app.cursor_location = app.command.chars().count();
|
||||
}
|
||||
Key::Char('?') => {
|
||||
app.mode = AppMode::HelpPopup;
|
||||
app.mode = AppMode::TaskHelpPopup;
|
||||
}
|
||||
Key::Char('/') => {
|
||||
app.mode = AppMode::Filter;
|
||||
app.mode = AppMode::TaskFilter;
|
||||
app.cursor_location = app.filter.chars().count();
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
AppMode::HelpPopup => match input {
|
||||
AppMode::TaskHelpPopup => match input {
|
||||
Key::Esc => {
|
||||
app.mode = AppMode::Report;
|
||||
app.mode = AppMode::TaskReport;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
AppMode::ModifyTask => match input {
|
||||
AppMode::TaskModify => match input {
|
||||
Key::Char('\n') => match app.task_modify() {
|
||||
Ok(_) => {
|
||||
app.mode = AppMode::Report;
|
||||
app.mode = AppMode::TaskReport;
|
||||
app.update();
|
||||
}
|
||||
Err(e) => {
|
||||
|
@ -149,7 +153,7 @@ fn tui_main(_config: &str) -> Result<(), Box<dyn Error>> {
|
|||
},
|
||||
Key::Esc => {
|
||||
app.modify = "".to_string();
|
||||
app.mode = AppMode::Report;
|
||||
app.mode = AppMode::TaskReport;
|
||||
}
|
||||
Key::Right => {
|
||||
if app.cursor_location < app.modify.chars().count() {
|
||||
|
@ -179,10 +183,10 @@ fn tui_main(_config: &str) -> Result<(), Box<dyn Error>> {
|
|||
}
|
||||
_ => {}
|
||||
},
|
||||
AppMode::LogTask => match input {
|
||||
AppMode::TaskLog => match input {
|
||||
Key::Char('\n') => match app.task_log() {
|
||||
Ok(_) => {
|
||||
app.mode = AppMode::Report;
|
||||
app.mode = AppMode::TaskReport;
|
||||
app.update();
|
||||
}
|
||||
Err(e) => {
|
||||
|
@ -192,7 +196,7 @@ fn tui_main(_config: &str) -> Result<(), Box<dyn Error>> {
|
|||
},
|
||||
Key::Esc => {
|
||||
app.command = "".to_string();
|
||||
app.mode = AppMode::Report;
|
||||
app.mode = AppMode::TaskReport;
|
||||
}
|
||||
Key::Right => {
|
||||
if app.cursor_location < app.command.chars().count() {
|
||||
|
@ -222,10 +226,10 @@ fn tui_main(_config: &str) -> Result<(), Box<dyn Error>> {
|
|||
}
|
||||
_ => {}
|
||||
},
|
||||
AppMode::AnnotateTask => match input {
|
||||
AppMode::TaskAnnotate => match input {
|
||||
Key::Char('\n') => match app.task_annotate() {
|
||||
Ok(_) => {
|
||||
app.mode = AppMode::Report;
|
||||
app.mode = AppMode::TaskReport;
|
||||
app.update();
|
||||
}
|
||||
Err(e) => {
|
||||
|
@ -235,7 +239,7 @@ fn tui_main(_config: &str) -> Result<(), Box<dyn Error>> {
|
|||
},
|
||||
Key::Esc => {
|
||||
app.command = "".to_string();
|
||||
app.mode = AppMode::Report;
|
||||
app.mode = AppMode::TaskReport;
|
||||
}
|
||||
Key::Right => {
|
||||
if app.cursor_location < app.command.chars().count() {
|
||||
|
@ -265,10 +269,10 @@ fn tui_main(_config: &str) -> Result<(), Box<dyn Error>> {
|
|||
}
|
||||
_ => {}
|
||||
},
|
||||
AppMode::AddTask => match input {
|
||||
AppMode::TaskAdd => match input {
|
||||
Key::Char('\n') => match app.task_add() {
|
||||
Ok(_) => {
|
||||
app.mode = AppMode::Report;
|
||||
app.mode = AppMode::TaskReport;
|
||||
app.update();
|
||||
}
|
||||
Err(e) => {
|
||||
|
@ -278,7 +282,7 @@ fn tui_main(_config: &str) -> Result<(), Box<dyn Error>> {
|
|||
},
|
||||
Key::Esc => {
|
||||
app.command = "".to_string();
|
||||
app.mode = AppMode::Report;
|
||||
app.mode = AppMode::TaskReport;
|
||||
}
|
||||
Key::Right => {
|
||||
if app.cursor_location < app.command.chars().count() {
|
||||
|
@ -308,9 +312,9 @@ fn tui_main(_config: &str) -> Result<(), Box<dyn Error>> {
|
|||
}
|
||||
_ => {}
|
||||
},
|
||||
AppMode::Filter => match input {
|
||||
AppMode::TaskFilter => match input {
|
||||
Key::Char('\n') | Key::Esc => {
|
||||
app.mode = AppMode::Report;
|
||||
app.mode = AppMode::TaskReport;
|
||||
app.update();
|
||||
}
|
||||
Key::Right => {
|
||||
|
@ -343,9 +347,16 @@ fn tui_main(_config: &str) -> Result<(), Box<dyn Error>> {
|
|||
},
|
||||
AppMode::TaskError => match input {
|
||||
_ => {
|
||||
app.mode = AppMode::Report;
|
||||
app.mode = AppMode::TaskReport;
|
||||
}
|
||||
},
|
||||
AppMode::Calendar => match input {
|
||||
Key::Char('[') => {
|
||||
app.mode = AppMode::TaskReport;
|
||||
}
|
||||
Key::Ctrl('c') | Key::Char('q') => app.should_quit = true,
|
||||
_ => {}
|
||||
},
|
||||
},
|
||||
Event::Tick => app.update(),
|
||||
}
|
||||
|
@ -355,5 +366,6 @@ fn tui_main(_config: &str) -> Result<(), Box<dyn Error>> {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue