rsnaker/graphics/menus/
main_menu.rs1use crate::graphics::menus::messages::{CONTROLS_TABLE, SNAKE_LOGO};
2use crate::graphics::menus::utils_layout::frame_vertically_centered_rect;
3use clap::ValueEnum;
4use ratatui::style::Stylize;
5use ratatui::style::{Color, Modifier, Style};
6use ratatui::text::{Line, Span, Text};
7use ratatui::widgets::{Block, BorderType, Paragraph};
8use ratatui::{DefaultTerminal, Frame};
9use std::cmp::PartialEq;
10use std::thread::sleep;
11use std::time::Duration;
12use unicode_segmentation::UnicodeSegmentation;
13
14pub fn display_main_menu(
20 terminal: &mut DefaultTerminal,
21 selected_switch_menu_for_display_bracket: &SwitchMenu,
22) {
23 terminal
25 .draw(|frame| {
26 let area = frame.area();
27 big_snake_menu(frame, selected_switch_menu_for_display_bracket);
28 frame.render_widget(Block::bordered().border_type(BorderType::Double), area);
31 })
32 .expect("Unusable terminal render");
33 sleep(Duration::from_millis(100));
34}
35
36fn big_snake_menu(frame: &mut Frame, to_display_switch: &SwitchMenu) {
37 let mut lines = vec![];
38 for logo_line in SNAKE_LOGO.lines() {
40 lines.push(Line::from(logo_line));
41 }
42
43 lines.push(Line::from(get_button_span(to_display_switch)));
45
46 for table_line in CONTROLS_TABLE.lines() {
48 lines.push(Line::from(table_line));
49 }
50
51 lines.push(Line::from("Have a good 🐍 game ! 🎮".green()));
53 let nb_lines = lines.len();
54 frame.render_widget(
55 Paragraph::new(Text::from(lines)).centered(),
57 frame_vertically_centered_rect(frame.area(), nb_lines),
58 );
59}
60pub struct Button {
62 pub name: &'static str,
63 pub selected: bool,
64}
65
66impl Button {
67 #[must_use]
69 pub const fn new(name: &'static str) -> Self {
70 Self {
71 name,
72 selected: false,
73 }
74 }
75
76 pub fn selected(&mut self, selected: bool) {
78 self.selected = selected;
79 }
80
81 #[must_use]
83 pub fn to_spans(&self) -> Vec<Span<'static>> {
84 if self.selected {
85 vec![
86 Span::styled(" [ ", Style::default().fg(Color::Red)).add_modifier(Modifier::BOLD),
87 Span::raw(self.name),
88 Span::styled(
89 " ] ",
90 Style::default().fg(Color::Red).add_modifier(Modifier::BOLD),
91 ),
92 ]
93 } else {
94 let (first, rest) = match self.name.graphemes(true).next() {
95 Some(grapheme) => {
96 let rest = &self.name[grapheme.len()..];
98 (grapheme, rest) }
100 None => ("", ""), };
102 vec![
103 Span::raw("["),
104 Span::styled(first, Style::default().fg(Color::Yellow)),
106 Span::raw(format!("{rest}]")),
107 ]
108 }
109 }
110}
111
112#[derive(PartialEq, ValueEnum, Clone)]
114pub enum SwitchMenu {
115 Highs,
116 Fruits,
117 Speed,
118 Run,
119 Parameters,
120 Doc,
121 Main,
122}
123const BUTTONS: [(SwitchMenu, Button); 6] = [
124 (SwitchMenu::Highs, Button::new("Highs💯")),
125 (SwitchMenu::Fruits, Button::new("Fruit")),
126 (SwitchMenu::Speed, Button::new("Speed")),
127 (SwitchMenu::Run, Button::new("Run")),
128 (SwitchMenu::Parameters, Button::new("Edit⚙️")),
129 (SwitchMenu::Doc, Button::new("Docℹ️")),
130];
131fn get_button_span(selected: &SwitchMenu) -> Vec<Span<'static>> {
133 let mut vec_line_button = vec![Span::raw("↔")];
134 for (menu, mut button) in BUTTONS {
136 button.selected(selected == &menu);
137 vec_line_button.extend(button.to_spans());
138 }
139 vec_line_button
140}