Add WIP timesheet

This commit is contained in:
kageru 2020-01-25 22:15:41 +01:00
parent e0d3bad477
commit 61c385404b
Signed by untrusted user: kageru
GPG Key ID: 8282A2BEA4ADA3D2
5 changed files with 170 additions and 53 deletions

View File

@ -9,3 +9,4 @@ tui = "0.8.0"
termion = "1.5"
serde_json = "1"
serde = { version = "1", features = ["derive"] }
time = { version = "0.2", features = ["serde"] }

View File

@ -5,6 +5,7 @@ use tui::backend::TermionBackend;
use tui::Terminal;
mod todolist;
mod tracc;
mod timesheet;
use tracc::Tracc;
fn main() -> Result<(), io::Error> {

104
src/timesheet.rs Normal file
View File

@ -0,0 +1,104 @@
use serde::{Deserialize, Serialize};
use serde_json::from_reader;
use std::fmt;
use std::fs::File;
use std::io::BufReader;
use time::Time;
pub struct TimeSheet {
pub times: Vec<TimePoint>,
pub selected: usize,
pub register: Option<TimePoint>,
}
#[derive(Serialize, Deserialize, Clone)]
pub struct TimePoint {
text: String,
time: Time,
}
impl TimePoint {
pub fn new(text: &str) -> Self {
Self {
text: String::from(text),
time: Time::now(),
}
}
}
impl fmt::Display for TimePoint {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[{}] {}", self.time.format("%H:%M"), self.text)
}
}
impl TimeSheet {
pub fn new() -> Self {
Self {
times: vec![
TimePoint::new("A test value"),
TimePoint::new("A second test value"),
],
selected: 0,
register: None,
}
}
pub fn printable(&self) -> Vec<String> {
self.times.iter().map(TimePoint::to_string).collect()
}
}
/*
impl TimeSheet {
pub fn selection_down(&mut self) {
self.selected = (self.selected + 1).min(self.todos.len().saturating_sub(1));
}
pub fn selection_up(&mut self) {
self.selected = self.selected.saturating_sub(1);
}
pub fn insert(&mut self, todo: Todo) {
if self.selected == self.todos.len().saturating_sub(1) {
self.todos.push(todo);
self.selected = self.todos.len() - 1;
} else {
self.todos.insert(self.selected + 1, todo);
self.selected += 1;
}
}
pub fn remove_current(&mut self) -> Option<Todo> {
if self.todos.is_empty() {
return None;
}
let index = self.selected;
self.selected = index.min(self.todos.len().saturating_sub(2));
return Some(self.todos.remove(index));
}
pub fn toggle_current(&mut self) {
self.todos[self.selected].done = !self.todos[self.selected].done;
}
fn current(&self) -> &Todo {
&self.todos[self.selected]
}
pub fn normal_mode(&mut self) {
if self.current().text.is_empty() {
self.remove_current();
self.selected = self.selected.saturating_sub(1);
}
}
pub fn append_to_current(&mut self, chr: char) {
self.todos[self.selected].text.push(chr);
}
pub fn current_pop(&mut self) {
self.todos[self.selected].text.pop();
}
}
*/

View File

@ -5,7 +5,6 @@ use std::fmt;
use std::io::BufReader;
pub struct TodoList {
// We use owned strings here because they’re easier to manipulate when editing.
pub todos: Vec<Todo>,
pub selected: usize,
pub register: Option<Todo>,
@ -13,6 +12,7 @@ pub struct TodoList {
#[derive(Serialize, Deserialize, Default, Clone)]
pub struct Todo {
// We use owned strings here because they’re easier to manipulate when editing.
text: String,
done: bool,
}

View File

@ -1,4 +1,5 @@
use super::todolist::TodoList;
use super::timesheet::TimeSheet;
use std::default::Default;
use std::io::{self, Write};
use termion::event::Key;
@ -16,27 +17,35 @@ pub enum Mode {
Normal,
}
#[derive(PartialEq)]
enum Focus {
Top,
Bottom,
}
pub struct Tracc {
todos: TodoList,
times: TimeSheet,
terminal: Terminal,
input_mode: Mode,
top_panel_selected: bool,
focus: Focus,
}
impl Tracc {
pub fn new(terminal: Terminal) -> Self {
Self {
todos: TodoList::open_or_create(JSON_PATH),
times: TimeSheet::new(),
terminal,
input_mode: Mode::Normal,
top_panel_selected: true,
focus: Focus::Top,
}
}
pub fn run(&mut self) -> Result<(), io::Error> {
let mut inputs = io::stdin().keys();
loop {
refresh(&mut self.terminal, &self.todos, self.top_panel_selected)?;
self.refresh()?;
// I need to find a better way to handle inputs. This is awful.
let input = inputs.next().unwrap()?;
match self.input_mode {
@ -61,7 +70,12 @@ impl Tracc {
self.todos.insert(self.todos.register.clone().unwrap());
}
}
Key::Char('\t') => self.top_panel_selected = !self.top_panel_selected,
Key::Char('\t') => {
self.focus = match self.focus {
Focus::Top => Focus::Bottom,
Focus::Bottom => Focus::Top,
}
}
_ => (),
},
Mode::Insert => match input {
@ -88,56 +102,53 @@ impl Tracc {
self.input_mode = mode;
Ok(())
}
}
fn refresh(terminal: &mut Terminal, todos: &TodoList, top_selected: bool) -> Result<(), io::Error> {
fn selectable_list<'a, C: AsRef<str>>(
title: &'a str,
content: &'a [C],
selected: Option<usize>,
) -> SelectableList<'a> {
SelectableList::default()
.block(
Block::default()
.title(title)
.borders(Borders::TOP | Borders::RIGHT | Borders::LEFT),
)
.items(content)
.select(selected.into())
.highlight_style(Style::default().fg(Color::LightGreen))
.highlight_symbol(">")
fn refresh(&mut self) -> Result<(), io::Error> {
fn selectable_list<'a, C: AsRef<str>>(
title: &'a str,
content: &'a [C],
selected: Option<usize>,
) -> SelectableList<'a> {
SelectableList::default()
.block(
Block::default()
.title(title)
.borders(Borders::TOP | Borders::RIGHT | Borders::LEFT),
)
.items(content)
.select(selected.into())
.highlight_style(Style::default().fg(Color::LightGreen))
.highlight_symbol(">")
}
let printable_todos = self.todos.printable();
let top_focus = Some(self.todos.selected).filter(|_| self.focus == Focus::Top);
let printable_times = self.times.printable();
let bottom_focus = Some(self.times.selected).filter(|_| self.focus == Focus::Bottom);
self.terminal.draw(|mut frame| {
let size = frame.size();
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints(
[
Constraint::Percentage(42),
Constraint::Percentage(42),
Constraint::Percentage(16),
]
.as_ref(),
)
.split(size);
selectable_list(" t r a c c ", &printable_todos, top_focus)
.render(&mut frame, chunks[0]);
selectable_list(" 🕑 ", &printable_times, bottom_focus)
.render(&mut frame, chunks[1]);
Paragraph::new([Text::raw("Sum for today: 1:12")].iter())
.block(Block::default().borders(Borders::ALL))
.render(&mut frame, chunks[2]);
})?;
Ok(())
}
terminal.draw(|mut frame| {
let size = frame.size();
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints(
[
Constraint::Percentage(42),
Constraint::Percentage(42),
Constraint::Percentage(16),
]
.as_ref(),
)
.split(size);
selectable_list(
" t r a c c ",
&todos.printable(),
Some(todos.selected).filter(|_| top_selected),
)
.render(&mut frame, chunks[0]);
selectable_list(
" 🕑 ",
&["[08:23] start", "[09:35] end"],
Some(0).filter(|_| !top_selected),
)
.render(&mut frame, chunks[1]);
Paragraph::new([Text::raw("Sum for today: 1:12")].iter())
.block(Block::default().borders(Borders::ALL))
.render(&mut frame, chunks[2]);
})?;
Ok(())
}
fn persist_todos(todos: &TodoList, path: &str) {