Make active panel in bold

This commit is contained in:
Dheepak Krishnamurthy 2020-10-24 23:52:04 -06:00
parent fe81fc4fa7
commit 5e2eabfba9
4 changed files with 166 additions and 98 deletions

View file

@ -4,9 +4,9 @@ use crate::table::{Row, Table, TableState};
use std::cmp::Ordering; use std::cmp::Ordering;
use std::convert::TryInto; use std::convert::TryInto;
use std::error::Error;
use std::process::Command; use std::process::Command;
use std::result::Result; use std::result::Result;
use std::error::Error;
use task_hookrs::date::Date; use task_hookrs::date::Date;
use task_hookrs::import::import; use task_hookrs::import::import;
@ -25,7 +25,7 @@ use tui::{
style::{Color, Modifier, Style}, style::{Color, Modifier, Style},
terminal::Frame, terminal::Frame,
text::{Span, Spans, Text}, text::{Span, Spans, Text},
widgets::{Block, Borders, Clear, Paragraph}, widgets::{Block, BorderType, Borders, Clear, Paragraph},
}; };
use rustyline::error::ReadlineError; use rustyline::error::ReadlineError;
@ -205,10 +205,7 @@ impl TaskReportTable {
self.columns = vec![]; self.columns = vec![];
self.labels = vec![]; self.labels = vec![];
let output = Command::new("task") let output = Command::new("task").arg("show").arg("report.next.columns").output()?;
.arg("show")
.arg("report.next.columns")
.output()?;
let data = String::from_utf8(output.stdout)?; let data = String::from_utf8(output.stdout)?;
for line in data.split('\n') { for line in data.split('\n') {
@ -220,10 +217,7 @@ impl TaskReportTable {
} }
} }
let output = Command::new("task") let output = Command::new("task").arg("show").arg("report.next.labels").output()?;
.arg("show")
.arg("report.next.labels")
.output()?;
let data = String::from_utf8(output.stdout)?; let data = String::from_utf8(output.stdout)?;
for line in data.split('\n') { for line in data.split('\n') {
@ -395,10 +389,7 @@ impl TTApp {
} }
pub fn get_context(&mut self) -> Result<(), Box<dyn Error>> { pub fn get_context(&mut self) -> Result<(), Box<dyn Error>> {
let output = Command::new("task") let output = Command::new("task").arg("_get").arg("rc.context").output()?;
.arg("_get")
.arg("rc.context")
.output()?;
self.context_name = String::from_utf8(output.stdout)?; self.context_name = String::from_utf8(output.stdout)?;
self.context_name = self.context_name.strip_suffix('\n').unwrap_or("").to_string(); self.context_name = self.context_name.strip_suffix('\n').unwrap_or("").to_string();
@ -434,21 +425,16 @@ impl TTApp {
.split(f.size()); .split(f.size());
let today = Local::today(); let today = Local::today();
let c = Calendar::default() let c = Calendar::default()
.block(Block::default() .block(
.title(Spans::from(vec![ Block::default()
Span::styled( .title(Spans::from(vec![
"Task", Span::styled("Task", Style::default().add_modifier(Modifier::DIM)),
Style::default().add_modifier(Modifier::DIM), Span::from("|"),
), Span::styled("Calendar", Style::default().add_modifier(Modifier::BOLD)),
Span::from("|"), ]))
Span::styled( .borders(Borders::ALL)
"Calendar", .border_type(BorderType::Rounded),
Style::default().add_modifier(Modifier::BOLD), )
),
]),
)
.borders(Borders::ALL)
)
.year(self.calendar_year) .year(self.calendar_year)
.date_style(dates_with_styles) .date_style(dates_with_styles)
.months_per_row(self.config.uda_calendar_months_per_row); .months_per_row(self.config.uda_calendar_months_per_row);
@ -498,7 +484,12 @@ impl TTApp {
AppMode::TaskFilter => { AppMode::TaskFilter => {
f.set_cursor(rects[1].x + self.filter.pos() as u16 + 1, rects[1].y + 1); f.set_cursor(rects[1].x + self.filter.pos() as u16 + 1, rects[1].y + 1);
f.render_widget(Clear, rects[1]); f.render_widget(Clear, rects[1]);
self.draw_command(f, rects[1], self.filter.as_str(), "Filter Tasks"); self.draw_command(
f,
rects[1],
self.filter.as_str(),
Span::styled("Filter Tasks", Style::default().add_modifier(Modifier::BOLD)),
);
} }
AppMode::TaskModify => { AppMode::TaskModify => {
f.set_cursor(rects[1].x + self.modify.pos() as u16 + 1, rects[1].y + 1); f.set_cursor(rects[1].x + self.modify.pos() as u16 + 1, rects[1].y + 1);
@ -507,18 +498,31 @@ impl TTApp {
f, f,
rects[1], rects[1],
self.modify.as_str(), self.modify.as_str(),
format!("Modify Task {}", task_id).as_str(), Span::styled(
format!("Modify Task {}", task_id).as_str(),
Style::default().add_modifier(Modifier::BOLD),
),
); );
} }
AppMode::TaskLog => { AppMode::TaskLog => {
f.set_cursor(rects[1].x + self.command.pos() as u16 + 1, rects[1].y + 1); f.set_cursor(rects[1].x + self.command.pos() as u16 + 1, rects[1].y + 1);
f.render_widget(Clear, rects[1]); f.render_widget(Clear, rects[1]);
self.draw_command(f, rects[1], self.command.as_str(), "Log Task"); self.draw_command(
f,
rects[1],
self.command.as_str(),
Span::styled("Log Tasks", Style::default().add_modifier(Modifier::BOLD)),
);
} }
AppMode::TaskSubprocess => { AppMode::TaskSubprocess => {
f.set_cursor(rects[1].x + self.command.pos() as u16 + 1, rects[1].y + 1); f.set_cursor(rects[1].x + self.command.pos() as u16 + 1, rects[1].y + 1);
f.render_widget(Clear, rects[1]); f.render_widget(Clear, rects[1]);
self.draw_command(f, rects[1], self.command.as_str(), "Shell Command"); self.draw_command(
f,
rects[1],
self.command.as_str(),
Span::styled("Shell Command", Style::default().add_modifier(Modifier::BOLD)),
);
} }
AppMode::TaskAnnotate => { AppMode::TaskAnnotate => {
f.set_cursor(rects[1].x + self.command.pos() as u16 + 1, rects[1].y + 1); f.set_cursor(rects[1].x + self.command.pos() as u16 + 1, rects[1].y + 1);
@ -527,17 +531,30 @@ impl TTApp {
f, f,
rects[1], rects[1],
self.command.as_str(), self.command.as_str(),
format!("Annotate Task {}", task_id).as_str(), Span::styled(
format!("Annotate Task {}", task_id).as_str(),
Style::default().add_modifier(Modifier::BOLD),
),
); );
} }
AppMode::TaskAdd => { AppMode::TaskAdd => {
f.set_cursor(rects[1].x + self.command.pos() as u16 + 1, rects[1].y + 1); f.set_cursor(rects[1].x + self.command.pos() as u16 + 1, rects[1].y + 1);
f.render_widget(Clear, rects[1]); f.render_widget(Clear, rects[1]);
self.draw_command(f, rects[1], self.command.as_str(), "Add Task"); self.draw_command(
f,
rects[1],
self.command.as_str(),
Span::styled("Add Task", Style::default().add_modifier(Modifier::BOLD)),
);
} }
AppMode::TaskError => { AppMode::TaskError => {
f.render_widget(Clear, rects[1]); f.render_widget(Clear, rects[1]);
self.draw_command(f, rects[1], self.error.as_str(), "Error"); self.draw_command(
f,
rects[1],
self.error.as_str(),
Span::styled("Error", Style::default().add_modifier(Modifier::BOLD)),
);
} }
AppMode::TaskHelpPopup => { AppMode::TaskHelpPopup => {
self.draw_command(f, rects[1], self.filter.as_str(), "Filter Tasks"); self.draw_command(f, rects[1], self.filter.as_str(), "Filter Tasks");
@ -719,21 +736,40 @@ impl TTApp {
Spans::from(""), Spans::from(""),
]; ];
let paragraph = Paragraph::new(text) let paragraph = Paragraph::new(text)
.block(Block::default().title("Help").borders(Borders::ALL)) .block(
Block::default()
.title(Span::styled("Help", Style::default().add_modifier(Modifier::BOLD)))
.borders(Borders::ALL)
.border_type(BorderType::Rounded),
)
.alignment(Alignment::Left); .alignment(Alignment::Left);
let area = centered_rect(80, 90, rect); let area = centered_rect(80, 90, rect);
f.render_widget(Clear, area); f.render_widget(Clear, area);
f.render_widget(paragraph, area); f.render_widget(paragraph, area);
} }
fn draw_command(&self, f: &mut Frame<impl Backend>, rect: Rect, text: &str, title: &str) { fn draw_command<'a, T>(&self, f: &mut Frame<impl Backend>, rect: Rect, text: &str, title: T)
let p = Paragraph::new(Text::from(text)).block(Block::default().borders(Borders::ALL).title(title)); where
T: Into<Spans<'a>>,
{
let p = Paragraph::new(Text::from(text)).block(
Block::default()
.borders(Borders::ALL)
.border_type(BorderType::Rounded)
.title(title.into()),
);
f.render_widget(p, rect); f.render_widget(p, rect);
} }
fn draw_task_details(&mut self, f: &mut Frame<impl Backend>, rect: Rect) { fn draw_task_details(&mut self, f: &mut Frame<impl Backend>, rect: Rect) {
if self.tasks.lock().unwrap().is_empty() { if self.tasks.lock().unwrap().is_empty() {
f.render_widget(Block::default().borders(Borders::ALL).title("Task not found"), rect); f.render_widget(
Block::default()
.borders(Borders::ALL)
.border_type(BorderType::Rounded)
.title("Task not found"),
rect,
);
return; return;
} }
let selected = self.state.selected().unwrap_or_default(); let selected = self.state.selected().unwrap_or_default();
@ -747,6 +783,7 @@ impl TTApp {
let p = Paragraph::new(Text::from(&data[..])).block( let p = Paragraph::new(Text::from(&data[..])).block(
Block::default() Block::default()
.borders(Borders::ALL) .borders(Borders::ALL)
.border_type(BorderType::Rounded)
.title(format!("Task {}", task_id)), .title(format!("Task {}", task_id)),
); );
f.render_widget(p, rect); f.render_widget(p, rect);
@ -798,20 +835,22 @@ impl TTApp {
fn draw_task_report(&mut self, f: &mut Frame<impl Backend>, rect: Rect) { fn draw_task_report(&mut self, f: &mut Frame<impl Backend>, rect: Rect) {
let (tasks, headers) = self.task_report(); let (tasks, headers) = self.task_report();
if tasks.is_empty() { if tasks.is_empty() {
f.render_widget(Block::default() let mut style = Style::default();
.borders(Borders::ALL) match self.mode {
.title(Spans::from(vec![ AppMode::TaskReport => style = style.add_modifier(Modifier::BOLD),
Span::styled( _ => style = style.add_modifier(Modifier::DIM),
"task next", }
Style::default().add_modifier(Modifier::BOLD), f.render_widget(
), Block::default()
Span::from("|"), .borders(Borders::ALL)
Span::styled( .border_type(BorderType::Rounded)
"Calendar", .title(Spans::from(vec![
Style::default().add_modifier(Modifier::DIM), Span::styled("Task", style),
), Span::from("|"),
]), Span::styled("Calendar", Style::default().add_modifier(Modifier::DIM)),
), rect); ])),
rect,
);
return; return;
} }
@ -885,23 +924,22 @@ impl TTApp {
.map(|i| Constraint::Min((*i).try_into().unwrap_or(10))) .map(|i| Constraint::Min((*i).try_into().unwrap_or(10)))
.collect(); .collect();
let mut style = Style::default();
match self.mode {
AppMode::TaskReport => style = style.add_modifier(Modifier::BOLD),
_ => style = style.add_modifier(Modifier::DIM),
}
let t = Table::new(header, rows.into_iter()) let t = Table::new(header, rows.into_iter())
.block( .block(
Block::default() Block::default()
.borders(Borders::ALL) .borders(Borders::ALL)
.title(Spans::from(vec![ .border_type(BorderType::Rounded)
Span::styled( .title(Spans::from(vec![
"Task", Span::styled("Task", style),
Style::default().add_modifier(Modifier::BOLD), Span::from("|"),
), Span::styled("Calendar", Style::default().add_modifier(Modifier::DIM)),
Span::from("|"), ])),
Span::styled( )
"Calendar",
Style::default().add_modifier(Modifier::DIM),
),
]),
)
)
.highlight_style(highlight_style) .highlight_style(highlight_style)
.highlight_symbol(&self.config.uda_selection_indicator) .highlight_symbol(&self.config.uda_selection_indicator)
.widths(&constraints); .widths(&constraints);
@ -1014,10 +1052,7 @@ impl TTApp {
self.command.update("", 0); self.command.update("", 0);
Ok(()) Ok(())
} }
Err(_) => Err(format!( Err(_) => Err(format!("Shell command `{}` exited with non-zero output", shell,)),
"Shell command `{}` exited with non-zero output",
shell,
)),
} }
} }
None => Err(format!("Cannot run subprocess. Unable to shlex split `{}`", shell)), None => Err(format!("Cannot run subprocess. Unable to shlex split `{}`", shell)),
@ -1046,10 +1081,16 @@ impl TTApp {
self.command.update("", 0); self.command.update("", 0);
Ok(()) Ok(())
} }
Err(_) => Err(format!("Cannot run `task log {}`. Check documentation for more information", shell)) Err(_) => Err(format!(
"Cannot run `task log {}`. Check documentation for more information",
shell
)),
} }
} }
None => Err(format!("Unable to run `task log`. Cannot shlex split `{}`", shell.as_str())), None => Err(format!(
"Unable to run `task log`. Cannot shlex split `{}`",
shell.as_str()
)),
} }
} }
@ -1083,8 +1124,7 @@ impl TTApp {
} }
None => Err(format!( None => Err(format!(
"Unable to run `task {} modify`. Cannot shlex split `{}`", "Unable to run `task {} modify`. Cannot shlex split `{}`",
task_id, task_id, shell,
shell,
)), )),
} }
} }
@ -1111,10 +1151,16 @@ impl TTApp {
self.command.update("", 0); self.command.update("", 0);
Ok(()) Ok(())
} }
Err(_) => Err(format!("Cannot run `task {} annotate {}`. Check documentation for more information", task_id, shell)), Err(_) => Err(format!(
"Cannot run `task {} annotate {}`. Check documentation for more information",
task_id, shell
)),
} }
} }
None => Err(format!("Unable to run `task {} annotate`. Cannot shlex split `{}`", task_id, shell)), None => Err(format!(
"Unable to run `task {} annotate`. Cannot shlex split `{}`",
task_id, shell
)),
} }
} }
@ -1135,7 +1181,10 @@ impl TTApp {
self.command.update("", 0); self.command.update("", 0);
Ok(()) Ok(())
} }
Err(_) => Err(format!("Cannot run `task add {}`. Check documentation for more information", shell)), Err(_) => Err(format!(
"Cannot run `task add {}`. Check documentation for more information",
shell
)),
} }
} }
None => Err(format!("Unable to run `task add`. Cannot shlex split `{}`", shell)), None => Err(format!("Unable to run `task add`. Cannot shlex split `{}`", shell)),
@ -1387,7 +1436,12 @@ impl TTApp {
} }
} }
pub fn handle_input(&mut self, input: Key, terminal: &mut Terminal<CrosstermBackend<io::Stdout>>, events: &Events) -> Result<(), Box<dyn Error>> { pub fn handle_input(
&mut self,
input: Key,
terminal: &mut Terminal<CrosstermBackend<io::Stdout>>,
events: &Events,
) -> Result<(), Box<dyn Error>> {
match self.mode { match self.mode {
AppMode::TaskReport => match input { AppMode::TaskReport => match input {
Key::Ctrl('c') | Key::Char('q') => self.should_quit = true, Key::Ctrl('c') | Key::Char('q') => self.should_quit = true,
@ -1570,9 +1624,11 @@ impl TTApp {
Key::Char('[') => { Key::Char('[') => {
self.mode = AppMode::TaskReport; self.mode = AppMode::TaskReport;
} }
Key::Up | Key::Char('k') => if self.calendar_year > 0 { Key::Up | Key::Char('k') => {
self.calendar_year -= 1 if self.calendar_year > 0 {
}, self.calendar_year -= 1
}
}
Key::Down | Key::Char('j') => self.calendar_year += 1, Key::Down | Key::Char('j') => self.calendar_year += 1,
Key::Ctrl('c') | Key::Char('q') => self.should_quit = true, Key::Ctrl('c') | Key::Char('q') => self.should_quit = true,
_ => {} _ => {}

View file

@ -1,8 +1,8 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::error::Error;
use std::process::Command; use std::process::Command;
use std::str; use std::str;
use tui::style::{Color, Modifier}; use tui::style::{Color, Modifier};
use std::error::Error;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct TColor { pub struct TColor {
@ -94,10 +94,7 @@ impl TConfig {
fn get_color_collection() -> Result<HashMap<String, TColor>, Box<dyn Error>> { fn get_color_collection() -> Result<HashMap<String, TColor>, Box<dyn Error>> {
let mut color_collection = HashMap::new(); let mut color_collection = HashMap::new();
let output = Command::new("task") let output = Command::new("task").arg("rc.color=off").arg("show").output()?;
.arg("rc.color=off")
.arg("show")
.output()?;
let data = String::from_utf8(output.stdout).expect("Unable to convert stdout to string"); let data = String::from_utf8(output.stdout).expect("Unable to convert stdout to string");
for line in data.split('\n') { for line in data.split('\n') {

View file

@ -12,8 +12,8 @@ use crate::util::{destruct_terminal, setup_terminal, Event, EventConfig, Events}
use clap::{App, Arg}; use clap::{App, Arg};
use std::env; use std::env;
use std::error::Error; use std::error::Error;
use std::time::Duration;
use std::io::Write; use std::io::Write;
use std::time::Duration;
use crate::util::Key; use crate::util::Key;
use app::{AppMode, TTApp}; use app::{AppMode, TTApp};
@ -41,7 +41,10 @@ fn main() -> Result<(), Box<dyn Error>> {
match r { match r {
Ok(_) => std::process::exit(0), Ok(_) => std::process::exit(0),
Err(error) => { Err(error) => {
eprintln!("{}: {}. Please report as a github issue on https://github.com/kdheepak/taskwarrior-tui", "[taskwarrior-tui error]", error); eprintln!(
"{}: {}. Please report as a github issue on https://github.com/kdheepak/taskwarrior-tui",
"[taskwarrior-tui error]", error
);
std::process::exit(1); std::process::exit(1);
} }
} }
@ -71,16 +74,16 @@ fn tui_main(_config: &str) -> Result<(), Box<dyn Error>> {
let r = app.handle_input(input, &mut terminal, &events); let r = app.handle_input(input, &mut terminal, &events);
if r.is_err() { if r.is_err() {
destruct_terminal(terminal); destruct_terminal(terminal);
return r return r;
} }
}, }
Event::Tick => { Event::Tick => {
let r = app.update(); let r = app.update();
if r.is_err() { if r.is_err() {
destruct_terminal(terminal); destruct_terminal(terminal);
return r return r;
} }
}, }
} }
if app.should_quit { if app.should_quit {
@ -90,10 +93,10 @@ fn tui_main(_config: &str) -> Result<(), Box<dyn Error>> {
} }
Ok(()) Ok(())
}, }
Err(e) => { Err(e) => {
destruct_terminal(terminal); destruct_terminal(terminal);
Err(e) Err(e)
}, }
} }
} }

View file

@ -316,10 +316,22 @@ where
x = table_area.left(); x = table_area.left();
for (c, (w, elt)) in solved_widths.iter().zip(data).enumerate() { for (c, (w, elt)) in solved_widths.iter().zip(data).enumerate() {
let s = if c == 0 { let s = if c == 0 {
buf.set_stringn(x, y + i as u16, format!("{symbol:^width$}", symbol="", width=area.width as usize), *w as usize, style); buf.set_stringn(
x,
y + i as u16,
format!("{symbol:^width$}", symbol = "", width = area.width as usize),
*w as usize,
style,
);
format!("{}{}", symbol, elt) format!("{}{}", symbol, elt)
} else { } else {
buf.set_stringn(x - 1, y + i as u16, format!("{symbol:^width$}", symbol="", width=area.width as usize), *w as usize + 1, style); buf.set_stringn(
x - 1,
y + i as u16,
format!("{symbol:^width$}", symbol = "", width = area.width as usize),
*w as usize + 1,
style,
);
format!("{}", elt) format!("{}", elt)
}; };
buf.set_stringn(x, y + i as u16, s, *w as usize, style); buf.set_stringn(x, y + i as u16, s, *w as usize, style);