1use crate::graphics::display;
2use crate::graphics::display::{Display, Renderer};
3use crate::input::Input;
4
5pub struct Menu<'a> {
7 top: usize,
8 selected: usize,
9 rows: usize,
10
11 items: Vec<MenuItem<'a>>,
12}
13
14struct MenuItem<'a> {
16 name: String,
17 callback: &'a dyn Fn(),
18}
19
20impl<'a> Menu<'a> {
21 pub fn new(rows: usize) -> Self {
23 Self {
24 top: 0,
25 selected: 0,
26 rows,
27
28 items: vec![],
29 }
30 }
31
32 pub fn push<F: Fn() + 'a>(&mut self, name: &str, code: &'a F) {
34 self.items.push(MenuItem {
35 name: name.to_owned(),
36 callback: code,
37 });
38 }
39
40 fn update_bounds(&mut self) {
42 self.selected %= self.items.len();
44
45 let offset = self.selected as isize - self.top as isize;
47 if offset < 0 {
48 self.top = self.selected;
49 } else if offset >= self.rows as isize {
50 self.top = self.selected - self.rows + 1;
51 }
52 }
53}
54
55impl Menu<'_> {
56 pub fn render<D: Display>(&self, renderer: &mut Renderer<D>) {
58 let start = self.top;
59 let end = usize::min(start + self.rows, self.items.len());
60
61 let row_height = renderer.height() / self.rows as u32;
62
63 renderer.clear();
64
65 for idx in start..end {
66 let name = &self.items[idx].name;
67
68 if idx == self.selected {
69 renderer.draw_rectangle_solid(
70 0,
71 row_height as i32 * (idx as i32 - start as i32),
72 renderer.width(),
73 row_height,
74 display::BLACK,
75 );
76 renderer.draw_text(
77 name,
78 10,
79 row_height as i32 * (idx as i32 - start as i32),
80 row_height as f32,
81 display::WHITE,
82 );
83 } else {
84 renderer.draw_text(
85 name,
86 10,
87 row_height as i32 * (idx as i32 - start as i32),
88 row_height as f32,
89 display::BLACK,
90 );
91 }
92 }
93
94 renderer.update();
95 }
96
97 pub fn notify_input(&mut self, input: &Input) -> bool {
101 if input.is_down() {
103 self.selected += 1;
104 }
105 if input.is_up() {
106 self.selected += self.items.len().max(1) - 1;
107 }
108
109 self.update_bounds();
110
111 if input.is_enter() {
112 if let Some(item) = self.items.get_mut(self.selected) {
113 (item.callback)();
115 } else {
116 eprintln!("Menu cursor in invalid position");
117 }
118 }
119
120 !input.is_left()
122 }
123}