mirror of
https://github.com/kdheepak/taskwarrior-tui.git
synced 2025-08-26 03:07:18 +02:00
Add context switcher
This commit is contained in:
parent
5a8e4dd184
commit
2250c20cfa
3 changed files with 233 additions and 59 deletions
262
src/app.rs
262
src/app.rs
|
@ -1,5 +1,6 @@
|
||||||
use crate::calendar::Calendar;
|
use crate::calendar::Calendar;
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
|
use crate::context::Context;
|
||||||
use crate::help::Help;
|
use crate::help::Help;
|
||||||
use crate::table::{Row, Table, TableState};
|
use crate::table::{Row, Table, TableState};
|
||||||
use crate::task_report::TaskReportTable;
|
use crate::task_report::TaskReportTable;
|
||||||
|
@ -118,13 +119,15 @@ pub enum AppMode {
|
||||||
TaskHelpPopup,
|
TaskHelpPopup,
|
||||||
TaskError,
|
TaskError,
|
||||||
Calendar,
|
Calendar,
|
||||||
|
ContextMenu,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TTApp {
|
pub struct TTApp {
|
||||||
pub should_quit: bool,
|
pub should_quit: bool,
|
||||||
pub state: TableState,
|
pub task_table_state: TableState,
|
||||||
pub context_filter: String,
|
pub context_table_state: TableState,
|
||||||
pub context_name: String,
|
pub current_context_filter: String,
|
||||||
|
pub current_context: String,
|
||||||
pub command: LineBuffer,
|
pub command: LineBuffer,
|
||||||
pub filter: LineBuffer,
|
pub filter: LineBuffer,
|
||||||
pub modify: LineBuffer,
|
pub modify: LineBuffer,
|
||||||
|
@ -137,6 +140,7 @@ pub struct TTApp {
|
||||||
pub task_report_show_info: bool,
|
pub task_report_show_info: bool,
|
||||||
pub task_report_height: u16,
|
pub task_report_height: u16,
|
||||||
pub help_popup: Help,
|
pub help_popup: Help,
|
||||||
|
pub contexts: Vec<Context>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TTApp {
|
impl TTApp {
|
||||||
|
@ -144,10 +148,11 @@ impl TTApp {
|
||||||
let c = Config::default()?;
|
let c = Config::default()?;
|
||||||
let mut app = Self {
|
let mut app = Self {
|
||||||
should_quit: false,
|
should_quit: false,
|
||||||
state: TableState::default(),
|
task_table_state: TableState::default(),
|
||||||
|
context_table_state: TableState::default(),
|
||||||
tasks: Arc::new(Mutex::new(vec![])),
|
tasks: Arc::new(Mutex::new(vec![])),
|
||||||
context_filter: "".to_string(),
|
current_context_filter: "".to_string(),
|
||||||
context_name: "".to_string(),
|
current_context: "".to_string(),
|
||||||
command: LineBuffer::with_capacity(MAX_LINE),
|
command: LineBuffer::with_capacity(MAX_LINE),
|
||||||
filter: LineBuffer::with_capacity(MAX_LINE),
|
filter: LineBuffer::with_capacity(MAX_LINE),
|
||||||
modify: LineBuffer::with_capacity(MAX_LINE),
|
modify: LineBuffer::with_capacity(MAX_LINE),
|
||||||
|
@ -159,6 +164,7 @@ impl TTApp {
|
||||||
task_report_table: TaskReportTable::new()?,
|
task_report_table: TaskReportTable::new()?,
|
||||||
calendar_year: Local::today().year(),
|
calendar_year: Local::today().year(),
|
||||||
help_popup: Help::new(),
|
help_popup: Help::new(),
|
||||||
|
contexts: vec![],
|
||||||
};
|
};
|
||||||
for c in "status:pending ".chars() {
|
for c in "status:pending ".chars() {
|
||||||
app.filter.insert(c, 1);
|
app.filter.insert(c, 1);
|
||||||
|
@ -170,15 +176,15 @@ 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").arg("_get").arg("rc.context").output()?;
|
let output = Command::new("task").arg("_get").arg("rc.context").output()?;
|
||||||
self.context_name = String::from_utf8_lossy(&output.stdout).to_string();
|
self.current_context = String::from_utf8_lossy(&output.stdout).to_string();
|
||||||
self.context_name = self.context_name.strip_suffix('\n').unwrap_or("").to_string();
|
self.current_context = self.current_context.strip_suffix('\n').unwrap_or("").to_string();
|
||||||
|
|
||||||
let output = Command::new("task")
|
let output = Command::new("task")
|
||||||
.arg("_get")
|
.arg("_get")
|
||||||
.arg(format!("rc.context.{}", self.context_name))
|
.arg(format!("rc.context.{}", self.current_context))
|
||||||
.output()?;
|
.output()?;
|
||||||
self.context_filter = String::from_utf8_lossy(&output.stdout).to_string();
|
self.current_context_filter = String::from_utf8_lossy(&output.stdout).to_string();
|
||||||
self.context_filter = self.context_filter.strip_suffix('\n').unwrap_or("").to_string();
|
self.current_context_filter = self.current_context_filter.strip_suffix('\n').unwrap_or("").to_string();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,6 +199,7 @@ impl TTApp {
|
||||||
| AppMode::TaskSubprocess
|
| AppMode::TaskSubprocess
|
||||||
| AppMode::TaskLog
|
| AppMode::TaskLog
|
||||||
| AppMode::TaskModify => self.draw_task(f),
|
| AppMode::TaskModify => self.draw_task(f),
|
||||||
|
AppMode::ContextMenu => self.draw_context_menu(f),
|
||||||
AppMode::Calendar => self.draw_calendar(f),
|
AppMode::Calendar => self.draw_calendar(f),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -210,6 +217,8 @@ impl TTApp {
|
||||||
.title(Spans::from(vec![
|
.title(Spans::from(vec![
|
||||||
Span::styled("Task", Style::default().add_modifier(Modifier::DIM)),
|
Span::styled("Task", Style::default().add_modifier(Modifier::DIM)),
|
||||||
Span::from("|"),
|
Span::from("|"),
|
||||||
|
Span::styled("Context", Style::default().add_modifier(Modifier::DIM)),
|
||||||
|
Span::from("|"),
|
||||||
Span::styled("Calendar", Style::default().add_modifier(Modifier::BOLD)),
|
Span::styled("Calendar", Style::default().add_modifier(Modifier::BOLD)),
|
||||||
]))
|
]))
|
||||||
.borders(Borders::ALL)
|
.borders(Borders::ALL)
|
||||||
|
@ -240,8 +249,8 @@ impl TTApp {
|
||||||
pub fn draw_task(&mut self, f: &mut Frame<impl Backend>) {
|
pub fn draw_task(&mut self, f: &mut Frame<impl Backend>) {
|
||||||
let tasks_is_empty = self.tasks.lock().unwrap().is_empty();
|
let tasks_is_empty = self.tasks.lock().unwrap().is_empty();
|
||||||
let tasks_len = self.tasks.lock().unwrap().len();
|
let tasks_len = self.tasks.lock().unwrap().len();
|
||||||
while !tasks_is_empty && self.state.selected().unwrap_or_default() >= tasks_len {
|
while !tasks_is_empty && self.task_table_state.selected().unwrap_or_default() >= tasks_len {
|
||||||
self.previous();
|
self.task_report_previous();
|
||||||
}
|
}
|
||||||
let rects = Layout::default()
|
let rects = Layout::default()
|
||||||
.direction(Direction::Vertical)
|
.direction(Direction::Vertical)
|
||||||
|
@ -266,7 +275,7 @@ impl TTApp {
|
||||||
self.draw_task_report(f, split_task_layout[0]);
|
self.draw_task_report(f, split_task_layout[0]);
|
||||||
self.draw_task_details(f, split_task_layout[1]);
|
self.draw_task_details(f, split_task_layout[1]);
|
||||||
}
|
}
|
||||||
let selected = self.state.selected().unwrap_or_default();
|
let selected = self.task_table_state.selected().unwrap_or_default();
|
||||||
let task_id = if tasks_len == 0 {
|
let task_id = if tasks_len == 0 {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
|
@ -353,7 +362,7 @@ impl TTApp {
|
||||||
self.draw_command(f, rects[1], self.filter.as_str(), "Filter Tasks");
|
self.draw_command(f, rects[1], self.filter.as_str(), "Filter Tasks");
|
||||||
self.draw_help_popup(f, f.size());
|
self.draw_help_popup(f, f.size());
|
||||||
}
|
}
|
||||||
AppMode::Calendar => {
|
_ => {
|
||||||
panic!("Reached unreachable code. Something went wrong");
|
panic!("Reached unreachable code. Something went wrong");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -365,6 +374,54 @@ impl TTApp {
|
||||||
f.render_widget(&self.help_popup, area);
|
f.render_widget(&self.help_popup, area);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn draw_context_menu(&mut self, f: &mut Frame<impl Backend>) {
|
||||||
|
let rects = Layout::default()
|
||||||
|
.direction(Direction::Vertical)
|
||||||
|
.constraints([Constraint::Min(0)].as_ref())
|
||||||
|
.split(f.size());
|
||||||
|
let area = rects[0];
|
||||||
|
|
||||||
|
f.render_widget(Clear, area);
|
||||||
|
|
||||||
|
let maximum_column_width = area.width;
|
||||||
|
|
||||||
|
let (contexts, headers) = self.get_all_contexts();
|
||||||
|
let widths = self.calculate_widths(&contexts, &headers, maximum_column_width);
|
||||||
|
|
||||||
|
let selected = self.context_table_state.selected().unwrap_or_default();
|
||||||
|
let header = headers.iter();
|
||||||
|
let mut rows = vec![];
|
||||||
|
let style = Style::default();
|
||||||
|
for (i, context) in contexts.iter().enumerate() {
|
||||||
|
rows.push(Row::StyledData(context.iter(), style));
|
||||||
|
}
|
||||||
|
|
||||||
|
let constraints: Vec<Constraint> = widths
|
||||||
|
.iter()
|
||||||
|
.map(|i| Constraint::Length((*i).try_into().unwrap_or(maximum_column_width as u16)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let highlight_style = Style::default().add_modifier(Modifier::BOLD);
|
||||||
|
let t = Table::new(header, rows.into_iter())
|
||||||
|
.block(
|
||||||
|
Block::default()
|
||||||
|
.borders(Borders::ALL)
|
||||||
|
.border_type(BorderType::Rounded)
|
||||||
|
.title(Spans::from(vec![
|
||||||
|
Span::styled("Task", Style::default().add_modifier(Modifier::DIM)),
|
||||||
|
Span::from("|"),
|
||||||
|
Span::styled("Context", Style::default().add_modifier(Modifier::BOLD)),
|
||||||
|
Span::from("|"),
|
||||||
|
Span::styled("Calendar", Style::default().add_modifier(Modifier::DIM)),
|
||||||
|
])),
|
||||||
|
)
|
||||||
|
.highlight_style(highlight_style)
|
||||||
|
.highlight_symbol(&self.config.uda_selection_indicator)
|
||||||
|
.widths(&constraints);
|
||||||
|
|
||||||
|
f.render_stateful_widget(t, area, &mut self.context_table_state);
|
||||||
|
}
|
||||||
|
|
||||||
fn draw_command<'a, T>(&self, f: &mut Frame<impl Backend>, rect: Rect, text: &str, title: T)
|
fn draw_command<'a, T>(&self, f: &mut Frame<impl Backend>, rect: Rect, text: &str, title: T)
|
||||||
where
|
where
|
||||||
T: Into<Spans<'a>>,
|
T: Into<Spans<'a>>,
|
||||||
|
@ -389,7 +446,7 @@ impl TTApp {
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let selected = self.state.selected().unwrap_or_default();
|
let selected = self.task_table_state.selected().unwrap_or_default();
|
||||||
let task_id = self.tasks.lock().unwrap()[selected].id().unwrap_or_default();
|
let task_id = self.tasks.lock().unwrap()[selected].id().unwrap_or_default();
|
||||||
let output = Command::new("task").arg(format!("{}", task_id)).output();
|
let output = Command::new("task").arg(format!("{}", task_id)).output();
|
||||||
if let Ok(output) = output {
|
if let Ok(output) = output {
|
||||||
|
@ -458,8 +515,8 @@ impl TTApp {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i, header) in headers.iter().enumerate() {
|
for (i, header) in headers.iter().enumerate() {
|
||||||
if header == "Description" {
|
if header == "Description" || header == "Definition" {
|
||||||
// always give description the most room to breath
|
// always give description or definition the most room to breath
|
||||||
widths[i] = maximum_column_width as usize;
|
widths[i] = maximum_column_width as usize;
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -475,7 +532,7 @@ 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.get_task_report();
|
||||||
if tasks.is_empty() {
|
if tasks.is_empty() {
|
||||||
let mut style = Style::default();
|
let mut style = Style::default();
|
||||||
match self.mode {
|
match self.mode {
|
||||||
|
@ -489,6 +546,8 @@ impl TTApp {
|
||||||
.title(Spans::from(vec![
|
.title(Spans::from(vec![
|
||||||
Span::styled("Task", style),
|
Span::styled("Task", style),
|
||||||
Span::from("|"),
|
Span::from("|"),
|
||||||
|
Span::styled("Context", Style::default().add_modifier(Modifier::DIM)),
|
||||||
|
Span::from("|"),
|
||||||
Span::styled("Calendar", Style::default().add_modifier(Modifier::DIM)),
|
Span::styled("Calendar", Style::default().add_modifier(Modifier::DIM)),
|
||||||
])),
|
])),
|
||||||
rect,
|
rect,
|
||||||
|
@ -499,7 +558,7 @@ impl TTApp {
|
||||||
let maximum_column_width = rect.width;
|
let maximum_column_width = rect.width;
|
||||||
let widths = self.calculate_widths(&tasks, &headers, maximum_column_width);
|
let widths = self.calculate_widths(&tasks, &headers, maximum_column_width);
|
||||||
|
|
||||||
let selected = self.state.selected().unwrap_or_default();
|
let selected = self.task_table_state.selected().unwrap_or_default();
|
||||||
let header = headers.iter();
|
let header = headers.iter();
|
||||||
let mut rows = vec![];
|
let mut rows = vec![];
|
||||||
let mut highlight_style = Style::default();
|
let mut highlight_style = Style::default();
|
||||||
|
@ -541,6 +600,8 @@ impl TTApp {
|
||||||
.title(Spans::from(vec![
|
.title(Spans::from(vec![
|
||||||
Span::styled("Task", style),
|
Span::styled("Task", style),
|
||||||
Span::from("|"),
|
Span::from("|"),
|
||||||
|
Span::styled("Context", Style::default().add_modifier(Modifier::DIM)),
|
||||||
|
Span::from("|"),
|
||||||
Span::styled("Calendar", Style::default().add_modifier(Modifier::DIM)),
|
Span::styled("Calendar", Style::default().add_modifier(Modifier::DIM)),
|
||||||
])),
|
])),
|
||||||
)
|
)
|
||||||
|
@ -548,10 +609,16 @@ impl TTApp {
|
||||||
.highlight_symbol(&self.config.uda_selection_indicator)
|
.highlight_symbol(&self.config.uda_selection_indicator)
|
||||||
.widths(&constraints);
|
.widths(&constraints);
|
||||||
|
|
||||||
f.render_stateful_widget(t, rect, &mut self.state);
|
f.render_stateful_widget(t, rect, &mut self.task_table_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn task_report(&mut self) -> (Vec<Vec<String>>, Vec<String>) {
|
pub fn get_all_contexts(&self) -> (Vec<Vec<String>>, Vec<String>) {
|
||||||
|
let contexts = self.contexts.iter().map(|c| vec![c.name.clone(), c.description.clone(), c.active.clone()]).collect();
|
||||||
|
let headers = vec!["Name".to_string(), "Description".to_string(), "Active".to_string()];
|
||||||
|
(contexts, headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_task_report(&mut self) -> (Vec<Vec<String>>, Vec<String>) {
|
||||||
let alltasks = &*(self.tasks.lock().unwrap());
|
let alltasks = &*(self.tasks.lock().unwrap());
|
||||||
|
|
||||||
self.task_report_table.generate_table(alltasks);
|
self.task_report_table.generate_table(alltasks);
|
||||||
|
@ -564,15 +631,54 @@ impl TTApp {
|
||||||
pub fn update(&mut self) -> Result<(), Box<dyn Error>> {
|
pub fn update(&mut self) -> Result<(), Box<dyn Error>> {
|
||||||
self.task_report_table.export_headers()?;
|
self.task_report_table.export_headers()?;
|
||||||
self.export_tasks()?;
|
self.export_tasks()?;
|
||||||
|
self.export_contexts()?;
|
||||||
self.update_tags();
|
self.update_tags();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn next(&mut self) {
|
pub fn context_next(&mut self) {
|
||||||
|
let i = match self.context_table_state.selected() {
|
||||||
|
Some(i) => {
|
||||||
|
if i >= self.contexts.len() - 1 {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
i + 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => 0,
|
||||||
|
};
|
||||||
|
self.context_table_state.select(Some(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn context_previous(&mut self) {
|
||||||
|
let i = match self.context_table_state.selected() {
|
||||||
|
Some(i) => {
|
||||||
|
if i == 0 {
|
||||||
|
self.contexts.len() - 1
|
||||||
|
} else {
|
||||||
|
i - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => 0,
|
||||||
|
};
|
||||||
|
self.context_table_state.select(Some(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn context_select(&mut self) {
|
||||||
|
let i = self.context_table_state.selected().unwrap();
|
||||||
|
|
||||||
|
let output = Command::new("task")
|
||||||
|
.arg("context")
|
||||||
|
.arg(&self.contexts[i].name)
|
||||||
|
.output();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn task_report_next(&mut self) {
|
||||||
if self.tasks.lock().unwrap().is_empty() {
|
if self.tasks.lock().unwrap().is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let i = match self.state.selected() {
|
let i = match self.task_table_state.selected() {
|
||||||
Some(i) => {
|
Some(i) => {
|
||||||
if i >= self.tasks.lock().unwrap().len() - 1 {
|
if i >= self.tasks.lock().unwrap().len() - 1 {
|
||||||
0
|
0
|
||||||
|
@ -582,14 +688,14 @@ impl TTApp {
|
||||||
}
|
}
|
||||||
None => 0,
|
None => 0,
|
||||||
};
|
};
|
||||||
self.state.select(Some(i));
|
self.task_table_state.select(Some(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn previous(&mut self) {
|
pub fn task_report_previous(&mut self) {
|
||||||
if self.tasks.lock().unwrap().is_empty() {
|
if self.tasks.lock().unwrap().is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let i = match self.state.selected() {
|
let i = match self.task_table_state.selected() {
|
||||||
Some(i) => {
|
Some(i) => {
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
self.tasks.lock().unwrap().len() - 1
|
self.tasks.lock().unwrap().len() - 1
|
||||||
|
@ -599,14 +705,14 @@ impl TTApp {
|
||||||
}
|
}
|
||||||
None => 0,
|
None => 0,
|
||||||
};
|
};
|
||||||
self.state.select(Some(i));
|
self.task_table_state.select(Some(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn next_page(&mut self) {
|
pub fn task_report_next_page(&mut self) {
|
||||||
if self.tasks.lock().unwrap().is_empty() {
|
if self.tasks.lock().unwrap().is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let i = match self.state.selected() {
|
let i = match self.task_table_state.selected() {
|
||||||
Some(i) => {
|
Some(i) => {
|
||||||
if i >= self.tasks.lock().unwrap().len() - 1 {
|
if i >= self.tasks.lock().unwrap().len() - 1 {
|
||||||
0
|
0
|
||||||
|
@ -617,14 +723,14 @@ impl TTApp {
|
||||||
}
|
}
|
||||||
None => 0,
|
None => 0,
|
||||||
};
|
};
|
||||||
self.state.select(Some(i));
|
self.task_table_state.select(Some(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn previous_page(&mut self) {
|
pub fn task_report_previous_page(&mut self) {
|
||||||
if self.tasks.lock().unwrap().is_empty() {
|
if self.tasks.lock().unwrap().is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let i = match self.state.selected() {
|
let i = match self.task_table_state.selected() {
|
||||||
Some(i) => {
|
Some(i) => {
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
self.tasks.lock().unwrap().len() - 1
|
self.tasks.lock().unwrap().len() - 1
|
||||||
|
@ -634,7 +740,36 @@ impl TTApp {
|
||||||
}
|
}
|
||||||
None => 0,
|
None => 0,
|
||||||
};
|
};
|
||||||
self.state.select(Some(i));
|
self.task_table_state.select(Some(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn export_contexts(&mut self) -> Result<(), Box<dyn Error>> {
|
||||||
|
let output = Command::new("task").arg("context").output()?;
|
||||||
|
let data = String::from_utf8_lossy(&output.stdout);
|
||||||
|
|
||||||
|
self.contexts = vec![];
|
||||||
|
|
||||||
|
for (i, line) in data.trim().split('\n').enumerate() {
|
||||||
|
let line = line.trim();
|
||||||
|
let mut s = line.split(" ");
|
||||||
|
let name = s.next().unwrap_or_default();
|
||||||
|
let active = s.last().unwrap_or_default();
|
||||||
|
let definition = line.replacen(name, "", 1);
|
||||||
|
let definition = definition.strip_suffix(active).unwrap();
|
||||||
|
if i == 0 || i == 1 {
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
let context = Context::new(name.to_string(), definition.trim().to_string(), active.to_string());
|
||||||
|
self.contexts.push(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.contexts.iter().any(|r| r.active != "no") {
|
||||||
|
self.contexts.insert(0, Context::new("none".to_string(), "".to_string(), "no".to_string()))
|
||||||
|
} else {
|
||||||
|
self.contexts.insert(0, Context::new("none".to_string(), "".to_string(), "yes".to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn export_tasks(&mut self) -> Result<(), Box<dyn Error>> {
|
pub fn export_tasks(&mut self) -> Result<(), Box<dyn Error>> {
|
||||||
|
@ -644,8 +779,8 @@ impl TTApp {
|
||||||
task.arg("rc.confirmation=off");
|
task.arg("rc.confirmation=off");
|
||||||
task.arg("export");
|
task.arg("export");
|
||||||
|
|
||||||
let filter = if self.context_filter != *"" {
|
let filter = if self.current_context_filter != "" {
|
||||||
let t = format!("{} {}", self.filter.as_str(), self.context_filter);
|
let t = format!("{} {}", self.filter.as_str(), self.current_context_filter);
|
||||||
t
|
t
|
||||||
} else {
|
} else {
|
||||||
self.filter.as_str().into()
|
self.filter.as_str().into()
|
||||||
|
@ -743,7 +878,7 @@ impl TTApp {
|
||||||
if self.tasks.lock().unwrap().is_empty() {
|
if self.tasks.lock().unwrap().is_empty() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let selected = self.state.selected().unwrap_or_default();
|
let selected = self.task_table_state.selected().unwrap_or_default();
|
||||||
let task_id = self.tasks.lock().unwrap()[selected].id().unwrap_or_default();
|
let task_id = self.tasks.lock().unwrap()[selected].id().unwrap_or_default();
|
||||||
let mut command = Command::new("task");
|
let mut command = Command::new("task");
|
||||||
command.arg(format!("{}", task_id)).arg("modify");
|
command.arg(format!("{}", task_id)).arg("modify");
|
||||||
|
@ -778,7 +913,7 @@ impl TTApp {
|
||||||
if self.tasks.lock().unwrap().is_empty() {
|
if self.tasks.lock().unwrap().is_empty() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let selected = self.state.selected().unwrap_or_default();
|
let selected = self.task_table_state.selected().unwrap_or_default();
|
||||||
let task_id = self.tasks.lock().unwrap()[selected].id().unwrap_or_default();
|
let task_id = self.tasks.lock().unwrap()[selected].id().unwrap_or_default();
|
||||||
let mut command = Command::new("task");
|
let mut command = Command::new("task");
|
||||||
command.arg(format!("{}", task_id)).arg("annotate");
|
command.arg(format!("{}", task_id)).arg("annotate");
|
||||||
|
@ -865,7 +1000,7 @@ impl TTApp {
|
||||||
if self.tasks.lock().unwrap().is_empty() {
|
if self.tasks.lock().unwrap().is_empty() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let selected = self.state.selected().unwrap_or_default();
|
let selected = self.task_table_state.selected().unwrap_or_default();
|
||||||
let task_id = self.tasks.lock().unwrap()[selected].id().unwrap_or_default();
|
let task_id = self.tasks.lock().unwrap()[selected].id().unwrap_or_default();
|
||||||
let mut command = "start";
|
let mut command = "start";
|
||||||
for tag in TTApp::task_virtual_tags(task_id)?.split(' ') {
|
for tag in TTApp::task_virtual_tags(task_id)?.split(' ') {
|
||||||
|
@ -888,7 +1023,7 @@ impl TTApp {
|
||||||
if self.tasks.lock().unwrap().is_empty() {
|
if self.tasks.lock().unwrap().is_empty() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let selected = self.state.selected().unwrap_or_default();
|
let selected = self.task_table_state.selected().unwrap_or_default();
|
||||||
let task_id = self.tasks.lock().unwrap()[selected].id().unwrap_or_default();
|
let task_id = self.tasks.lock().unwrap()[selected].id().unwrap_or_default();
|
||||||
|
|
||||||
let output = Command::new("task")
|
let output = Command::new("task")
|
||||||
|
@ -909,7 +1044,7 @@ impl TTApp {
|
||||||
if self.tasks.lock().unwrap().is_empty() {
|
if self.tasks.lock().unwrap().is_empty() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let selected = self.state.selected().unwrap_or_default();
|
let selected = self.task_table_state.selected().unwrap_or_default();
|
||||||
let task_id = self.tasks.lock().unwrap()[selected].id().unwrap_or_default();
|
let task_id = self.tasks.lock().unwrap()[selected].id().unwrap_or_default();
|
||||||
let output = Command::new("task").arg(format!("{}", task_id)).arg("done").output();
|
let output = Command::new("task").arg(format!("{}", task_id)).arg("done").output();
|
||||||
match output {
|
match output {
|
||||||
|
@ -937,7 +1072,7 @@ impl TTApp {
|
||||||
if self.tasks.lock().unwrap().is_empty() {
|
if self.tasks.lock().unwrap().is_empty() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let selected = self.state.selected().unwrap_or_default();
|
let selected = self.task_table_state.selected().unwrap_or_default();
|
||||||
let task_id = self.tasks.lock().unwrap()[selected].id().unwrap_or_default();
|
let task_id = self.tasks.lock().unwrap()[selected].id().unwrap_or_default();
|
||||||
let r = Command::new("task").arg(format!("{}", task_id)).arg("edit").spawn();
|
let r = Command::new("task").arg(format!("{}", task_id)).arg("edit").spawn();
|
||||||
|
|
||||||
|
@ -971,7 +1106,7 @@ impl TTApp {
|
||||||
if self.tasks.lock().unwrap().is_empty() {
|
if self.tasks.lock().unwrap().is_empty() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let selected = self.state.selected().unwrap_or_default();
|
let selected = self.task_table_state.selected().unwrap_or_default();
|
||||||
Some(self.tasks.lock().unwrap()[selected].clone())
|
Some(self.tasks.lock().unwrap()[selected].clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1090,14 +1225,11 @@ impl TTApp {
|
||||||
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,
|
||||||
Key::Char(']') => {
|
|
||||||
self.mode = AppMode::Calendar;
|
|
||||||
}
|
|
||||||
Key::Char('r') => self.update()?,
|
Key::Char('r') => self.update()?,
|
||||||
Key::Down | Key::Char('j') => self.next(),
|
Key::Down | Key::Char('j') => self.task_report_next(),
|
||||||
Key::Up | Key::Char('k') => self.previous(),
|
Key::Up | Key::Char('k') => self.task_report_previous(),
|
||||||
Key::PageDown | Key::Char('J') => self.next_page(),
|
Key::PageDown | Key::Char('J') => self.task_report_next_page(),
|
||||||
Key::PageUp | Key::Char('K') => self.previous_page(),
|
Key::PageUp | Key::Char('K') => self.task_report_previous_page(),
|
||||||
Key::Char('d') => match self.task_done() {
|
Key::Char('d') => match self.task_done() {
|
||||||
Ok(_) => self.update()?,
|
Ok(_) => self.update()?,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
@ -1169,6 +1301,22 @@ impl TTApp {
|
||||||
Key::Char('z') => {
|
Key::Char('z') => {
|
||||||
self.task_report_show_info = !self.task_report_show_info;
|
self.task_report_show_info = !self.task_report_show_info;
|
||||||
}
|
}
|
||||||
|
Key::Char(']') => {
|
||||||
|
self.mode = AppMode::ContextMenu;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
AppMode::ContextMenu => match input {
|
||||||
|
Key::Ctrl('c') | Key::Char('q') => self.should_quit = true,
|
||||||
|
Key::Char('[') => {
|
||||||
|
self.mode = AppMode::TaskReport;
|
||||||
|
},
|
||||||
|
Key::Char(']') => {
|
||||||
|
self.mode = AppMode::Calendar;
|
||||||
|
}
|
||||||
|
Key::Down | Key::Char('j') => self.context_next(),
|
||||||
|
Key::Up | Key::Char('k') => self.context_previous(),
|
||||||
|
Key::Char('\n') => self.context_select(),
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
AppMode::TaskHelpPopup => match input {
|
AppMode::TaskHelpPopup => match input {
|
||||||
|
@ -1281,8 +1429,9 @@ impl TTApp {
|
||||||
},
|
},
|
||||||
AppMode::TaskError => self.mode = AppMode::TaskReport,
|
AppMode::TaskError => self.mode = AppMode::TaskReport,
|
||||||
AppMode::Calendar => match input {
|
AppMode::Calendar => match input {
|
||||||
|
Key::Ctrl('c') | Key::Char('q') => self.should_quit = true,
|
||||||
Key::Char('[') => {
|
Key::Char('[') => {
|
||||||
self.mode = AppMode::TaskReport;
|
self.mode = AppMode::ContextMenu;
|
||||||
}
|
}
|
||||||
Key::Up | Key::Char('k') => {
|
Key::Up | Key::Char('k') => {
|
||||||
if self.calendar_year > 0 {
|
if self.calendar_year > 0 {
|
||||||
|
@ -1296,7 +1445,6 @@ impl TTApp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Key::PageDown | Key::Char('J') => self.calendar_year += 10,
|
Key::PageDown | Key::Char('J') => self.calendar_year += 10,
|
||||||
Key::Ctrl('c') | Key::Char('q') => self.should_quit = true,
|
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -1373,16 +1521,14 @@ mod tests {
|
||||||
fn test_app() {
|
fn test_app() {
|
||||||
let mut app = TTApp::new().unwrap();
|
let mut app = TTApp::new().unwrap();
|
||||||
|
|
||||||
let (tasks, headers) = app.task_report();
|
let (contexts, headers) = app.get_all_contexts();
|
||||||
let maximum_column_width = 120;
|
dbg!(contexts);
|
||||||
let widths = app.calculate_widths(&tasks, &headers, maximum_column_width);
|
dbg!(headers);
|
||||||
|
|
||||||
dbg!(widths);
|
|
||||||
|
|
||||||
//println!("{:?}", app.task_report_columns);
|
//println!("{:?}", app.task_report_columns);
|
||||||
//println!("{:?}", app.task_report_labels);
|
//println!("{:?}", app.task_report_labels);
|
||||||
|
|
||||||
// let (t, h, c) = app.task_report();
|
// let (t, h, c) = app.get_task_report();
|
||||||
// app.next();
|
// app.next();
|
||||||
// app.next();
|
// app.next();
|
||||||
// app.modify = "Cannot add this string ' because it has a single quote".to_string();
|
// app.modify = "Cannot add this string ' because it has a single quote".to_string();
|
||||||
|
|
26
src/context.rs
Normal file
26
src/context.rs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
use std::cmp;
|
||||||
|
|
||||||
|
use tui::{
|
||||||
|
buffer::Buffer,
|
||||||
|
layout::{Alignment, Rect},
|
||||||
|
style::{Modifier, Style},
|
||||||
|
text::{Span, Spans, Text},
|
||||||
|
widgets::{Block, BorderType, Borders, Clear, Paragraph, StatefulWidget, Widget},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct Context {
|
||||||
|
pub name: String,
|
||||||
|
pub description: String,
|
||||||
|
pub active: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Context {
|
||||||
|
pub fn new(name: String, description: String, active: String) -> Self {
|
||||||
|
Self {
|
||||||
|
name,
|
||||||
|
description,
|
||||||
|
active,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,7 @@
|
||||||
mod app;
|
mod app;
|
||||||
mod calendar;
|
mod calendar;
|
||||||
mod config;
|
mod config;
|
||||||
|
mod context;
|
||||||
mod help;
|
mod help;
|
||||||
mod table;
|
mod table;
|
||||||
mod task_report;
|
mod task_report;
|
||||||
|
@ -72,7 +73,8 @@ fn tui_main(_config: &str) -> Result<(), Box<dyn Error>> {
|
||||||
let maybeapp = TTApp::new();
|
let maybeapp = TTApp::new();
|
||||||
match maybeapp {
|
match maybeapp {
|
||||||
Ok(mut app) => {
|
Ok(mut app) => {
|
||||||
app.next();
|
app.task_report_next();
|
||||||
|
app.context_next();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
terminal.draw(|mut frame| app.draw(&mut frame)).unwrap();
|
terminal.draw(|mut frame| app.draw(&mut frame)).unwrap();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue