rsnaker/graphics/menus/layout_utils.rs
1use ratatui::layout::{Constraint, Direction, Layout, Rect};
2use ratatui::prelude::{Color, Style};
3use ratatui::widgets::{Block, Paragraph};
4use ratatui::Frame;
5
6/// Creates a vertically centered rectangle within the given area with the specified number of lines.
7#[must_use]
8pub fn frame_vertically_centered_rect(area: Rect, lines: usize) -> Rect {
9 // Auto center vertically, elsewhere could be manually calculated with y offset
10 // by taking height from frame.area
11 Layout::default()
12 .direction(Direction::Vertical)
13 .constraints([
14 Constraint::Fill(1), // top space
15 Constraint::Length(u16::try_from(lines).unwrap_or(u16::MAX)), // content
16 Constraint::Fill(1), // bottom space
17 ])
18 // take second rect to render content
19 .split(area)[1]
20 // if we want to do manual center horizontally also
21 /*Layout::default()
22 .direction(Direction::Horizontal)
23 .constraints([
24 Constraint::Fill(1), // top space
25 Constraint::Length(lines.lines().last().unwrap().len() as u16), // content
26 Constraint::Fill(1), // bottom space
27 ])
28 // take second rect to render content
29 .split(vertical_layout)[1]*/
30}
31/// Render a horizontally and vertically centered paragraph with text and color style
32/// With the game external border set to the same color as the text
33pub fn render_full_centered_paragraph(frame: &mut Frame, text: &'static str, color: Option<Color>) {
34 let area = frame_vertically_centered_rect(frame.area(), text.lines().count());
35 // ensure that all cells under the popup are cleared to avoid leaking content: no useful, and ugly
36 //frame.render_widget(Clear, area);
37 frame.render_widget(
38 Paragraph::new(text)
39 //.style(Style::default().fg(color))
40 .alignment(ratatui::layout::Alignment::Center),
41 area,
42 );
43 // Set all screen items in the same color as the menu.
44 // Applying style break position of emojis
45 // even by previously setting a full style to item to avoid them to inherit the overall one
46 // so keep global style to the final situations without the snake moving after
47 if let Some(color) = color {
48 frame.render_widget(
49 Block::default().style(Style::default().fg(color)),
50 frame.area(),
51 );
52 }
53}