mod active_pane;
mod command_bar;
mod help_popup;
mod macros;
mod pane;
mod side_effect;
mod widgets;
use std::io;
pub use active_pane::ActivePane;
use anyhow::Context;
use command_bar::CommandBar;
use help_popup::HelpPopup;
pub use pane::{Pane, init_panes};
use ratatui::layout::{Constraint, Flex, Layout, Rect};
use ratatui::widgets::Clear;
use ratatui::{DefaultTerminal, Frame};
pub use side_effect::SideEffect;
use super::store::{AppState, Store};
pub trait View<S: Store> {
fn on_update(&mut self, store: &S) -> anyhow::Result<()>;
}
pub struct App {
terminal: DefaultTerminal,
}
impl App {
pub fn new() -> Self {
App::default()
}
fn render(&mut self, store: &AppState) -> anyhow::Result<()> {
self.terminal
.try_draw(|frame| render(frame, store).map_err(io::Error::other))
.context("failed to redraw the frame")?;
Ok(())
}
}
impl Default for App {
fn default() -> Self {
let terminal = ratatui::init();
App { terminal }
}
}
impl Drop for App {
fn drop(&mut self) {
ratatui::restore();
}
}
impl View<AppState> for App {
fn on_update(&mut self, store: &AppState) -> anyhow::Result<()> {
self.render(store)
}
}
fn render(frame: &mut Frame, state: &AppState) -> anyhow::Result<()> {
for (pane, pane_area) in state.panes.iter() {
frame.render_widget(
pane.as_ref()
.context("bug: pane wasn't returned back after an operation")?
.render(state, pane_area.height, pane_area.width)
.context("failed to render a frame")?,
*pane_area,
);
}
frame.render_widget(
CommandBar::render(state)
.context("failed to redner the command bar")?,
state.command_bar_area,
);
if state.show_help_popup {
let popup_area = popup_area(
frame.area(),
Some(Constraint::Length(23)),
Some(Constraint::Length(75)),
);
clear_area(frame, popup_area);
frame.render_widget(
HelpPopup::render(state)
.context("failed to render the help popup")?,
popup_area,
);
}
Ok(())
}
fn popup_area(
area: Rect,
vertical_constraints: impl IntoIterator<Item = Constraint>,
horizontal_constraint: impl IntoIterator<Item = Constraint>,
) -> Rect {
let vertical = Layout::vertical(vertical_constraints).flex(Flex::Center);
let [area] = vertical.areas(area);
let horizontal =
Layout::horizontal(horizontal_constraint).flex(Flex::Center);
let [area] = horizontal.areas(area);
area
}
fn clear_area(frame: &mut Frame, area: Rect) {
frame.render_widget(Clear, area);
}