use ratatui::buffer::Buffer as Surface;
use ratatui::layout::Rect;
use ratatui::text::{Line, Span};
use ratatui::widgets::{Block, BorderType, Borders, Clear, Paragraph, Widget, Wrap};
use crate::theme::Theme;
pub struct Popup<'a> {
pub title: &'a str,
pub body: &'a str,
pub hint: &'a str,
pub theme: &'a Theme,
}
impl Popup<'_> {
fn centred(area: Rect, width: u16, height: u16) -> Rect {
let width = width.min(area.width.saturating_sub(2)).max(1);
let height = height.min(area.height.saturating_sub(2)).max(1);
Rect {
x: area.x + (area.width.saturating_sub(width)) / 2,
y: area.y + (area.height.saturating_sub(height)) / 2,
width,
height,
}
}
}
impl Widget for Popup<'_> {
fn render(self, area: Rect, surface: &mut Surface) {
if area.is_empty() {
return;
}
let lines = u16::try_from(self.body.lines().count()).unwrap_or(u16::MAX);
let popup = Self::centred(area, area.width * 3 / 4, lines + 4);
Clear.render(popup, surface);
let block = Block::default()
.borders(Borders::ALL)
.border_type(BorderType::Rounded)
.border_style(self.theme.popup_border)
.style(self.theme.popup)
.title(Span::styled(
format!(" {} ", self.title),
self.theme.popup_border,
));
let inner = block.inner(popup);
block.render(popup, surface);
let mut text: Vec<Line<'_>> = self
.body
.lines()
.map(|line| Line::from(Span::styled(line, self.theme.popup)))
.collect();
if !self.hint.is_empty() {
text.push(Line::from(""));
text.push(Line::from(Span::styled(self.hint, self.theme.gutter)).right_aligned());
}
Paragraph::new(text)
.wrap(Wrap { trim: false })
.render(inner, surface);
}
}