rsnaker/graphics/menus/utils_layout.rs
1use crate::graphics::menus::retro_parameter_table::generic_logic::RowData;
2use crate::graphics::menus::retro_parameter_table::generic_style::DEFAULT_ITEM_HEIGHT;
3use ratatui::layout::{Constraint, Direction, Layout, Rect};
4use ratatui::prelude::{Color, Style};
5use ratatui::widgets::{Block, Paragraph};
6use ratatui::Frame;
7use unicode_segmentation::UnicodeSegmentation;
8
9/// Creates a vertically centered rectangle within the given area with the specified number of lines.
10#[must_use]
11pub fn frame_vertically_centered_rect(area: Rect, lines: usize) -> Rect {
12 // Auto center vertically, elsewhere could be manually calculated with y offset
13 // by taking height from frame.area
14 Layout::default()
15 .direction(Direction::Vertical)
16 .constraints([
17 Constraint::Fill(1), // top space
18 Constraint::Length(u16::try_from(lines).unwrap_or(u16::MAX)), // content
19 Constraint::Fill(1), // bottom space
20 ])
21 // take second rect to render content
22 .split(area)[1]
23 // if we want to do manual center horizontally also
24 /*Layout::default()
25 .direction(Direction::Horizontal)
26 .constraints([
27 Constraint::Fill(1), // top space
28 Constraint::Length(lines.lines().last().unwrap().len() as u16), // content
29 Constraint::Fill(1), // bottom space
30 ])
31 // take second rect to render content
32 .split(vertical_layout)[1]*/
33}
34/// Render a horizontally and vertically centered paragraph with text and color style
35/// With the game external border set to the same color as the text
36pub fn render_full_centered_paragraph(frame: &mut Frame, text: &'static str, color: Option<Color>) {
37 let area = frame_vertically_centered_rect(frame.area(), text.lines().count());
38 // ensure that all cells under the popup are cleared to avoid leaking content: no useful, and ugly
39 //frame.render_widget(Clear, area);
40 frame.render_widget(
41 Paragraph::new(text)
42 //.style(Style::default().fg(color))
43 .alignment(ratatui::layout::Alignment::Center),
44 area,
45 );
46 // Set all screen items in the same color as the menu.
47 // Applying style break position of emojis
48 // even by previously setting a full style to item to avoid them to inherit the overall one
49 // so keep global style to the final situations without the snake moving after
50 if let Some(color) = color {
51 frame.render_widget(
52 Block::default().style(Style::default().fg(color)),
53 frame.area(),
54 );
55 }
56}
57#[must_use]
58pub fn constraint_length_from_widths(column_widths: &[u16]) -> Vec<Constraint> {
59 let mut constraints = vec![];
60 // A little fun with iterator, and peekable to foresee the future
61 //peekable show the next element without advancing the iterator
62 let mut iter = column_widths.iter().peekable();
63 //for each element, add a constraint
64 // to have some fun with a destructuring pattern and while let
65 while let Some(&size) = iter.next() {
66 // if there is still more elements after
67 if iter.peek().is_some() {
68 constraints.push(Constraint::Length(size));
69 } else {
70 //for the last element, add all the remaining space
71 constraints.push(Constraint::Length(size + 1));
72 }
73 }
74 constraints
75}
76#[allow(clippy::cast_possible_truncation)]
77#[must_use]
78pub fn calculate_max_column_widths(rows: &[RowData], headers: &[String]) -> Vec<u16> {
79 // Initialize with header widths, +1 for header separator
80 let mut column_widths: Vec<u16> = headers
81 .iter()
82 .map(|h| h.as_str().graphemes(true).count() as u16 + 1)
83 .collect();
84 let column_len = column_widths.len();
85 // Update with row data widths
86 for row in rows {
87 let cell_widths = row.get_cell_widths();
88 for (i, &width) in cell_widths.iter().enumerate() {
89 if width as u16 > column_widths[i] {
90 column_widths[i % column_len] = width as u16;
91 }
92 }
93 }
94
95 column_widths
96}
97#[must_use]
98pub fn calculate_sum_inner_row_heights(rows: &[RowData]) -> usize {
99 rows.iter()
100 .map(|r| {
101 // Get the maximum height of the cells in the current row.
102 // max() returns an Option<&usize> because it operates on an iterator of references.
103 // *The result must be dereferenced to get the usize value before falling back
104 // to a default or summing.*
105 r.get_cell_heights()
106 .iter()
107 .max()
108 .copied() // Dereference the &usize to usize
109 .unwrap_or(DEFAULT_ITEM_HEIGHT) // Fall back to DEFAULT_ITEM_HEIGHT if the row is empty
110 })
111 .sum::<usize>()
112}