pub mod config_popup;
pub mod copy_menu;
pub mod doc_search_bar;
pub mod editor;
pub mod file_tree;
pub mod goto_line_bar;
pub mod help;
pub mod link_picker;
pub mod markdown_view;
pub mod search_modal;
pub mod status_bar;
pub mod tab_bar;
pub mod tab_picker;
pub mod table_modal;
pub mod table_render;
pub mod tabs;
use crate::app::{App, Focus};
use crate::config::TreePosition;
use ratatui::{
Frame,
layout::{Constraint, Direction, Layout},
style::Style,
widgets::Block,
};
#[allow(clippy::too_many_lines)]
pub fn draw(f: &mut Frame, app: &mut App) {
let area = f.area();
f.render_widget(
Block::default().style(Style::default().bg(app.palette.background)),
area,
);
app.tree_area_rect = None;
app.viewer_area_rect = None;
app.tab_bar_rects.clear();
app.tab_close_rects.clear();
app.tab_picker_rects.clear();
app.search_result_rects.clear();
let outer_chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Min(1), Constraint::Length(1)])
.split(area);
let has_tabs = !app.tabs.is_empty();
let tab_bar_height: u16 = u16::from(has_tabs);
let viewer_area;
if app.tree_hidden {
let content_chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Length(tab_bar_height), Constraint::Min(1)])
.split(outer_chunks[0]);
if has_tabs {
tab_bar::draw(f, app, content_chunks[0]);
}
viewer_area = content_chunks[1];
app.viewer_area_rect = Some(viewer_area);
if app.tabs.active_tab().is_some_and(|t| t.editor.is_some()) {
editor::draw(f, app, viewer_area);
} else {
markdown_view::draw(f, app, viewer_area, is_viewer_focused(app.focus));
}
} else {
let (first_pct, second_pct) = match app.tree_position {
TreePosition::Left => (app.tree_width_pct, 100 - app.tree_width_pct),
TreePosition::Right => (100 - app.tree_width_pct, app.tree_width_pct),
};
let main_chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints([
Constraint::Percentage(first_pct),
Constraint::Percentage(second_pct),
])
.split(outer_chunks[0]);
let (tree_idx, viewer_idx) = match app.tree_position {
TreePosition::Left => (0, 1),
TreePosition::Right => (1, 0),
};
let tree_area = main_chunks[tree_idx];
let viewer_col = main_chunks[viewer_idx];
file_tree::draw(f, app, tree_area, app.focus == Focus::Tree);
app.tree_area_rect = Some(tree_area);
let viewer_col_chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Length(tab_bar_height), Constraint::Min(1)])
.split(viewer_col);
if has_tabs {
tab_bar::draw(f, app, viewer_col_chunks[0]);
}
viewer_area = viewer_col_chunks[1];
app.viewer_area_rect = Some(viewer_area);
if app.tabs.active_tab().is_some_and(|t| t.editor.is_some()) {
editor::draw(f, app, viewer_area);
} else {
markdown_view::draw(f, app, viewer_area, is_viewer_focused(app.focus));
}
}
status_bar::draw(f, app, outer_chunks[1]);
let doc_search_active = app.doc_search().is_some_and(|ds| ds.active);
if doc_search_active {
doc_search_bar::draw(f, app, viewer_area);
}
if app.goto_line.active {
goto_line_bar::draw(f, app, viewer_area);
}
if app.show_help {
help::draw(f, app);
}
if app.tab_picker.is_some() {
tab_picker::draw(f, app);
}
if app.link_picker.is_some() {
link_picker::draw(f, app);
}
if app.table_modal.is_some() {
table_modal::draw(f, app);
}
if let Some(popup_state) = &app.config_popup {
let params = config_popup::ConfigPopupParams {
state: popup_state,
theme: app.theme,
show_line_numbers: app.show_line_numbers,
tree_position: app.tree_position,
search_preview: app.search_preview,
mermaid_mode: app.mermaid_mode,
palette: &app.palette,
};
config_popup::render_config_popup(f, ¶ms);
}
if let Some(state) = &app.copy_menu {
let state = state.clone();
copy_menu::draw(f, &state, &app.palette);
}
if app.search.active {
search_modal::draw(f, app);
}
}
fn is_viewer_focused(focus: Focus) -> bool {
matches!(
focus,
Focus::Viewer
| Focus::DocSearch
| Focus::Config
| Focus::GotoLine
| Focus::TabPicker
| Focus::TableModal
| Focus::CopyMenu
| Focus::LinkPicker
| Focus::Editor
)
}