Skip to main content

ratatui_command_palette/render/
modal.rs

1use ratatui::buffer::Buffer;
2use ratatui::layout::{Constraint, Direction, Layout, Rect};
3use ratatui::style::{Color, Style};
4use ratatui::widgets::{Block, BorderType, Borders, Clear, Widget};
5
6use super::PaletteRenderer;
7use super::parts::{render_footer, render_query, render_rows};
8use crate::view::PaletteView;
9
10/// Modal command palette renderer.
11///
12/// The modal renderer clears its area, draws a rounded border, then renders the
13/// prompt, rows, and footer. It is useful when the palette should interrupt the
14/// current surface. See the [`render`](super) module for the renderer contract
15/// shared by all built-in renderers.
16#[derive(Clone, Debug)]
17pub struct ModalRenderer {
18    title: String,
19}
20
21impl Default for ModalRenderer {
22    fn default() -> Self {
23        Self::new()
24    }
25}
26
27impl ModalRenderer {
28    /// Creates a [`ModalRenderer`] with the default title.
29    pub fn new() -> Self {
30        Self {
31            title: "Command Palette".into(),
32        }
33    }
34
35    /// Sets the modal title.
36    ///
37    /// # Examples
38    ///
39    /// ```
40    /// use ratatui_command_palette::render::ModalRenderer;
41    ///
42    /// let renderer = ModalRenderer::new().title("Actions");
43    ///
44    /// assert_eq!(
45    ///     format!("{renderer:?}"),
46    ///     "ModalRenderer { title: \"Actions\" }"
47    /// );
48    /// ```
49    pub fn title(mut self, title: impl Into<String>) -> Self {
50        self.title = title.into();
51        self
52    }
53}
54
55impl PaletteRenderer for ModalRenderer {
56    fn render(&self, area: Rect, buf: &mut Buffer, view: &PaletteView) {
57        Clear.render(area, buf);
58
59        let block = Block::new()
60            .title(self.title.as_str())
61            .borders(Borders::ALL)
62            .border_type(BorderType::Rounded)
63            .border_style(Style::new().fg(Color::Cyan));
64        let inner = block.inner(area);
65        block.render(area, buf);
66
67        if inner.height == 0 || inner.width == 0 {
68            return;
69        }
70
71        let sections = Layout::default()
72            .direction(Direction::Vertical)
73            .constraints([
74                Constraint::Length(1),
75                Constraint::Min(0),
76                Constraint::Length(1),
77            ])
78            .split(inner);
79
80        render_query(sections[0], buf, view);
81        render_rows(sections[1], buf, view);
82        render_footer(sections[2], buf, view);
83    }
84}