minifb_ui/ui/
contextmenu.rs1pub struct ContextMenu {
2 items: Vec<String>,
3 pub open: bool,
4 menu_x: usize,
5 menu_y: usize,
6
7 pub item_height: usize,
8 pub width: usize,
9 pub padding: usize,
10
11 pub bg_color: crate::color::Color,
12 pub hover_color: crate::color::Color,
13 pub text_color: crate::color::Color,
14 pub border_color: crate::color::Color,
15 pub border_size: usize,
16 pub radius: usize,
17
18 pub font: Option<crate::ttf::Font>,
19 pub font_size: f32,
20
21 clicked_item: Option<usize>,
22 lmb_was_down: bool,
23}
24
25impl Default for ContextMenu {
26 fn default() -> Self {
27 Self {
28 items: Vec::new(),
29 open: false,
30 menu_x: 0,
31 menu_y: 0,
32 item_height: 28,
33 width: 160,
34 padding: 4,
35 bg_color: crate::color::Color::new(25, 25, 35),
36 hover_color: crate::color::Color::rgba(108, 92, 231, 40),
37 text_color: crate::color::Color::new(220, 220, 230),
38 border_color: crate::color::Color::new(70, 70, 90),
39 border_size: 1,
40 radius: 6,
41 font: None,
42 font_size: 13.0,
43 clicked_item: None,
44 lmb_was_down: false,
45 }
46 }
47}
48
49impl ContextMenu {
50 pub fn items(mut self, items: &[&str]) -> Self {
51 self.items = items.iter().map(|s| s.to_string()).collect();
52 self
53 }
54
55 pub fn width(mut self, w: usize) -> Self {
56 self.width = w;
57 self
58 }
59
60 pub fn item_height(mut self, h: usize) -> Self {
61 self.item_height = h;
62 self
63 }
64
65 pub fn font(mut self, font: crate::ttf::Font, size: f32) -> Self {
66 self.font = Some(font);
67 self.font_size = size;
68 self
69 }
70
71 pub fn bg_color(mut self, color: crate::color::Color) -> Self {
72 self.bg_color = color;
73 self
74 }
75
76 pub fn hover_color(mut self, color: crate::color::Color) -> Self {
77 self.hover_color = color;
78 self
79 }
80
81 pub fn text_color(mut self, color: crate::color::Color) -> Self {
82 self.text_color = color;
83 self
84 }
85
86 pub fn border_color(mut self, color: crate::color::Color) -> Self {
87 self.border_color = color;
88 self
89 }
90
91 pub fn border(mut self, size: usize) -> Self {
92 self.border_size = size;
93 self
94 }
95
96 pub fn radius(mut self, radius: usize) -> Self {
97 self.radius = radius;
98 self
99 }
100
101 pub fn open(&mut self, x: usize, y: usize) {
103 self.menu_x = x;
104 self.menu_y = y;
105 self.open = true;
106 self.clicked_item = None;
107 }
108
109 pub fn close(&mut self) {
111 self.open = false;
112 self.clicked_item = None;
113 }
114
115 pub fn is_open(&self) -> bool {
116 self.open
117 }
118
119 pub fn clicked_item(&self) -> Option<usize> {
121 self.clicked_item
122 }
123
124 pub fn draw(&mut self, window: &mut crate::window::Window) {
125 self.clicked_item = None;
126
127 if !self.open {
128 self.lmb_was_down = window.get_mouse_state().lmb_clicked;
129 return;
130 }
131
132 let font = match &self.font {
133 Some(f) => f.clone(),
134 None => return,
135 };
136
137 let mouse = window.get_mouse_state();
138 let mx = mouse.pos_x;
139 let my = mouse.pos_y;
140 let lmb = mouse.lmb_clicked;
141 let lmb_just = lmb && !self.lmb_was_down;
142
143 let total_h = self.padding * 2 + self.items.len() * self.item_height;
144
145 window.draw_rect_f(
147 self.menu_x, self.menu_y, self.width, total_h,
148 self.radius, &self.bg_color, 0,
149 );
150 for i in 0..self.border_size {
151 window.draw_rect(
152 self.menu_x + i, self.menu_y + i,
153 self.width - i * 2, total_h - i * 2,
154 self.radius.saturating_sub(i), &self.border_color,
155 );
156 }
157
158 for (i, item) in self.items.iter().enumerate() {
160 let iy = self.menu_y + self.padding + i * self.item_height;
161 let hovered = mx >= self.menu_x as f32
162 && mx < (self.menu_x + self.width) as f32
163 && my >= iy as f32
164 && my < (iy + self.item_height) as f32;
165
166 if hovered {
167 window.draw_rect_f(
168 self.menu_x + self.border_size, iy,
169 self.width - self.border_size * 2, self.item_height,
170 0, &self.hover_color, 0,
171 );
172
173 if lmb_just {
174 self.clicked_item = Some(i);
175 self.open = false;
176 }
177 }
178
179 let text = crate::ui::text::Text::new(item, font.clone());
180 let ty = iy as f32 + (self.item_height as f32 - self.font_size) / 2.0;
181 window.draw_text(self.menu_x + 12, ty as usize, &text, self.font_size, &self.text_color);
182 }
183
184 if lmb_just {
186 let in_menu = mx >= self.menu_x as f32
187 && mx < (self.menu_x + self.width) as f32
188 && my >= self.menu_y as f32
189 && my < (self.menu_y + total_h) as f32;
190 if !in_menu {
191 self.open = false;
192 }
193 }
194
195 self.lmb_was_down = lmb;
196 }
197}