use super::*;
pub fn render(term: &mut Term, vault: &Vault, state: &State) -> Result<()> {
match state {
State::SearchIdeas(query) => render_search_screen(term, vault, &query, 0, false),
State::SearchJournals(query) => render_search_screen(term, vault, &query, 1, false),
State::SearchNotes(query) => render_search_screen(term, vault, &query, 2, false),
State::SearchTasks(query) => render_search_screen(term, vault, &query, 3, true),
State::HelpScreen => render_help_screen(term, vault),
}
}
fn draw_tabs<B>(mut f: &mut Frame<B>, index: usize, vault: &Vault)
where
B: Backend,
{
let size = f.size();
Tabs::default()
.block(Block::default().borders(Borders::ALL).border_style(Style::default().fg(Color::White)))
.titles(&["Ideas", "Journals", "Notes", "Tasks", "Help"])
.style(Style::default().fg(Color::White))
.highlight_style(Style::default().fg(Color::LightYellow).modifier(Modifier::BOLD))
.divider(tui::symbols::DOT)
.select(index)
.render(&mut f, size);
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Length(1), Constraint::Length(1), Constraint::Percentage(100)].as_ref())
.split(f.size());
let chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints([Constraint::Length(43), Constraint::Min(40), Constraint::Length(2)].as_ref())
.split(chunks[1]);
let root = vault.root.as_path().to_path_buf()
.into_os_string().into_string().unwrap_or(String::from(""));
Paragraph::new([Text::raw(root)].iter())
.style(Style::default().fg(Color::DarkGray))
.alignment(Alignment::Left)
.wrap(true)
.render(&mut f, chunks[1]);
}
fn draw_search_prompt<B>(mut f: &mut Frame<B>, query: &Query)
where
B: Backend,
{
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Length(3), Constraint::Length(1), Constraint::Percentage(100)].as_ref())
.split(f.size());
let chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints([Constraint::Length(9), Constraint::Min(40), Constraint::Length(2)].as_ref())
.split(chunks[1]);
Paragraph::new([Text::raw("Input: ")].iter())
.style(Style::default().fg(Color::Red))
.alignment(Alignment::Right)
.render(&mut f, chunks[0]);
let input = format!("{}_", query.input);
Paragraph::new([Text::raw(input.as_str())].iter())
.style(Style::default().fg(Color::Cyan).bg(Color::Black))
.alignment(Alignment::Left)
.wrap(true)
.render(&mut f, chunks[1]);
}
fn format_item(config: &Config, category: &String, name: &String, max_length: Option<usize>, show_tasks: bool) -> String {
if name == "" {
category.to_owned()
} else {
let (delimiter, name) =
if !show_tasks {
(&config.view_delimiter, name.to_owned())
} else {
if name.as_str().starts_with(config.task_not_done_prefix.as_str()) {
let value = name.as_str()
.trim_start_matches(config.task_not_done_prefix.as_str())
.trim_start()
.to_owned();
(&config.task_not_done_display_prefix, value)
} else if name.as_str().starts_with(config.task_in_progress_prefix.as_str()) {
let value = name.as_str()
.trim_start_matches(config.task_in_progress_prefix.as_str())
.trim_start()
.to_owned();
(&config.task_in_progress_display_prefix, value)
} else if name.as_str().starts_with(config.task_done_prefix.as_str()) {
let value = name.as_str()
.trim_start_matches(config.task_done_prefix.as_str())
.trim_start()
.to_owned();
(&config.task_done_display_prefix, value)
} else {
(&config.view_delimiter, name.to_owned())
}
};
if let Some(width) = max_length {
format!("{:>width$} {} {}", category, delimiter, name, width = width)
} else {
format!("{} {} {}", category, delimiter, name)
}
}
}
fn draw_search_results<B>(mut f: &mut Frame<B>, query: &Query, vault: &Vault, show_tasks: bool)
where
B: Backend,
{
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Length(4), Constraint::Min(10), Constraint::Length(0)].as_ref())
.split(f.size());
let chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints([Constraint::Length(1), Constraint::Min(40), Constraint::Length(1)].as_ref())
.split(chunks[1]);
let mut block = Block::default()
.borders(Borders::ALL)
.border_style(Style::default().fg(Color::DarkGray));
let results = query.flatten_results();
let max_length = results.iter().map(|(a, _, _, _)| a.len()).max();
let results = results.iter()
.map(|(a, b, _, _)| format_item(&vault.config, a, b, max_length, show_tasks))
.collect::<Vec<String>>();
let hi_color =
match query.deletion_stage {
DeletionStage::NotFlagged => Color::LightYellow,
DeletionStage::Flagged => Color::Red,
};
if !results.is_empty() {
SelectableList::default()
.block(block)
.items(results.as_slice())
.select(query.selected_result_index)
.style(Style::default().fg(Color::Gray))
.highlight_style(Style::default().fg(hi_color))
.highlight_symbol(">>")
.render(&mut f, chunks[1]);
} else {
block.render(&mut f, chunks[1]);
}
}
fn render_search_screen(term: &mut Term, vault: &Vault, query: &Query, index: usize, show_tasks: bool) -> Result<()> {
term.draw(|mut f| {
draw_tabs(&mut f, index, vault);
draw_search_prompt(&mut f, query);
draw_search_results(&mut f, query, vault, show_tasks);
})?;
Ok(())
}
fn render_help_screen(term: &mut Term, vault: &Vault) -> Result<()> {
term.draw(|mut f| {
draw_tabs(&mut f, 4, vault);
let chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints([Constraint::Length(4), Constraint::Percentage(100)].as_ref())
.split(f.size());
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Length(4), Constraint::Length(6), Constraint::Percentage(100)].as_ref())
.split(chunks[1]);
let hi_style = Style::default().fg(Color::Red);
let text = [
Text::styled("My Journal ", hi_style),
Text::raw("is a productivity tool that will help you manage your daily ideas,\n"),
Text::raw("journals, notes and tasks. "),
Text::styled("My Journal ", hi_style),
Text::raw("is nothing else but a thin layer atop your\n"),
Text::raw("default editor that it will use to organize your textual notes in a clean and\n"),
Text::raw("yet open structure of directories.\n"),
];
Paragraph::new(text.iter())
.alignment(Alignment::Left)
.wrap(true)
.render(&mut f, chunks[1]);
Block::default()
.title(" Key bindings ")
.title_style(Style::default().fg(Color::Yellow))
.borders(Borders::NONE)
.render(&mut f, chunks[2]);
let chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints([Constraint::Length(2), Constraint::Percentage(100)].as_ref())
.split(chunks[2]);
let row_style = Style::default().fg(Color::White);
let hr_style = Style::default().fg(Color::DarkGray);
Table::new(
["", ""].into_iter(),
vec![
Row::StyledData(["--------", "---------------------------"].into_iter(), hr_style),
Row::StyledData([" alt-i ", " Switch to [Ideas] tab"].into_iter(), row_style),
Row::StyledData([" alt-j ", " Switch to [Journals] tab"].into_iter(), row_style),
Row::StyledData([" alt-n ", " Switch to [Notes] tab"].into_iter(), row_style),
Row::StyledData([" alt-t ", " Switch to [Tasks] tab"].into_iter(), row_style),
Row::StyledData(["--------", "---------------------------"].into_iter(), hr_style),
Row::StyledData([" alt-h ", " Show help"].into_iter(), row_style),
Row::StyledData(["--------", "---------------------------"].into_iter(), hr_style),
Row::StyledData([" ctrl-w ", " Delete last word in input"].into_iter(), row_style),
Row::StyledData([" esc ", " Clear input"].into_iter(), row_style),
Row::StyledData(["--------", "---------------------------"].into_iter(), hr_style),
Row::StyledData([" enter ", " Edit/create entry"].into_iter(), row_style),
Row::StyledData([" alt-s ", " Rotate task status"].into_iter(), row_style),
Row::StyledData([" ctrl-d ", " Delete entry under cursor"].into_iter(), row_style),
Row::StyledData(["--------", "---------------------------"].into_iter(), hr_style),
Row::StyledData([" ctrl-q ", " Quit"].into_iter(), row_style),
]
.into_iter(),
)
.widths(&[10, 30])
.style(Style::default().fg(Color::White))
.column_spacing(2)
.render(&mut f, chunks[1]);
})?;
Ok(())
}