pub mod nav_panel;
mod preview_panel;
mod status_bar;
mod terminal_panel;
mod theme;
use ratatui::backend::Backend;
use ratatui::layout::{Constraint, Direction, Layout, Rect};
use ratatui::Terminal;
use std::io;
use crate::app::state::{AppState, PreviewSlot};
use crate::preview::provider::PreviewContent;
#[derive(Debug, Clone, Copy)]
struct Panes {
nav: Rect,
preview: Rect,
terminal: Option<Rect>,
status: Rect,
files_shown: bool,
}
impl Panes {
fn new(area: Rect, state: &AppState) -> Panes {
let outer = Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Min(1), Constraint::Length(1), ])
.split(area);
let (main, terminal) = if state.terminal.is_visible() {
let rows = crate::terminal::panel_rows(
outer[0].height,
state.config.terminal.height,
state.terminal.is_maximized(),
);
let split = Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Min(0), Constraint::Length(rows)])
.split(outer[0]);
(split[0], Some(split[1]))
} else {
(outer[0], None)
};
let inner = Layout::default()
.direction(Direction::Horizontal)
.constraints([
Constraint::Percentage(40), Constraint::Percentage(60), ])
.split(main);
Panes {
nav: inner[0],
preview: inner[1],
terminal,
status: outer[1],
files_shown: main.height > 0,
}
}
}
fn image_needs_clear(slot: &mut PreviewSlot, pane: Option<Rect>) -> bool {
let PreviewContent::Image(image) = &mut slot.content else {
return false;
};
let moved = pane.is_some() && image.placed_at != pane;
image.placed_at = pane;
moved && image.draws_over_cells
}
pub fn render<B: Backend>(terminal: &mut Terminal<B>, state: &mut AppState) -> io::Result<()> {
let size = terminal.size()?;
let panes = Panes::new(Rect::new(0, 0, size.width, size.height), state);
let drawing_image =
panes.files_shown && matches!(state.preview.content, PreviewContent::Image(_));
let image_moved = image_needs_clear(
&mut state.preview,
panes.files_shown.then_some(panes.preview),
);
if image_moved || (state.preview.drew_image && !drawing_image) {
terminal.clear()?;
}
state.preview.drew_image = drawing_image;
terminal.draw(|frame| {
status_bar::draw(frame, panes.status, state);
if let Some(panel) = panes.terminal {
terminal_panel::draw(frame, panel, state);
}
if !panes.files_shown {
return;
}
let listed = state.filtered_count();
let nav_offset = crate::app::scroll::NavScroll::update(
&mut state.nav_scroll,
&state.cwd,
state.selected,
listed,
usize::from(panes.nav.height.saturating_sub(2)),
state.config.navigation.scroll_margin,
);
nav_panel::draw(frame, panes.nav, state, nav_offset);
preview_panel::draw(frame, panes.preview, state);
})?;
Ok(())
}