use std::io::{self, Stdout};
use crossterm::{cursor::SetCursorStyle, execute};
use ratatui::{
Terminal,
backend::CrosstermBackend,
layout::{Constraint, Direction, Layout, Rect},
text::Line,
widgets::Paragraph,
};
use crate::{
components::{EditorView, MinibufferLine, StatusLine},
render::{CursorStyle, RenderedFrame, Renderer, StateSnapshot},
};
pub struct RatatuiRenderer {
term: Terminal<CrosstermBackend<Stdout>>,
}
impl RatatuiRenderer {
pub fn new() -> io::Result<Self> {
let backend = CrosstermBackend::new(io::stdout());
Ok(Self {
term: Terminal::new(backend)?,
})
}
}
impl Renderer for RatatuiRenderer {
fn render(&mut self, snap: StateSnapshot<'_>, frame_data: &RenderedFrame) -> io::Result<()> {
let style = match snap.cursor_style {
CursorStyle::Bar => SetCursorStyle::SteadyBar,
CursorStyle::Block => SetCursorStyle::SteadyBlock,
};
execute!(io::stdout(), style)?;
self.term.draw(|f| {
let mut constraints = vec![Constraint::Min(1), Constraint::Length(1)];
for b in &frame_data.bottom_extra {
constraints.push(Constraint::Length(b.lines.len() as u16));
}
constraints.push(Constraint::Length(1));
let rects = Layout::default()
.direction(Direction::Vertical)
.constraints(constraints)
.split(f.area());
let editor_area = rects[0];
let status_area = rects[1];
let minibuffer_area = *rects.last().unwrap();
let mut cursor: Option<(u16, u16)> = None;
let focused_path = snap.windows.focused_path();
for leaf in snap.windows.layout(editor_area) {
let Some(buf) = snap.bufs.get(leaf.bufno) else { continue };
let buf_frame = frame_data.per_buf.get(leaf.bufno);
EditorView::render(buf, leaf.area, buf_frame, f);
if !snap.focus_minibuffer && &leaf.path == focused_path {
cursor = Some(EditorView::cursor(buf, leaf.area, buf_frame));
}
}
StatusLine::render(
status_area,
&frame_data.status_left,
&frame_data.status_right,
f,
);
for (b, area) in frame_data
.bottom_extra
.iter()
.zip(rects.iter().skip(2).take(frame_data.bottom_extra.len()))
{
draw_extra_bottom(b, *area, f);
}
MinibufferLine::render(minibuffer_area, &snap, f);
if let Some(pos) = MinibufferLine::cursor(minibuffer_area, &snap) {
cursor = Some(pos);
}
if let Some((x, y)) = cursor {
f.set_cursor_position((x, y));
}
})?;
Ok(())
}
}
fn draw_extra_bottom(b: &crate::render::RenderedBottom, area: Rect, f: &mut ratatui::Frame) {
let rows: Vec<Line<'static>> = b
.lines
.iter()
.take(area.height as usize)
.map(|spans| Line::from(spans.clone()))
.collect();
f.render_widget(Paragraph::new(rows), area);
}