Skip to main content

zenthra_widgets/controls/
menu.rs

1// crates/zenthra-widgets/src/controls/menu.rs
2
3use crate::ui::{DrawCommand, RectDraw, TextDraw, Ui};
4use zenthra_core::{Color, Id, Rect, Response};
5use zenthra_render::RectInstance;
6
7fn get_theme_color(ui: &Ui, base_id: u64, default: Color) -> Color {
8    let r = ui.interaction_state.get(&Id::from_u64(base_id)).copied();
9    let g = ui.interaction_state.get(&Id::from_u64(base_id + 1)).copied();
10    let b = ui.interaction_state.get(&Id::from_u64(base_id + 2)).copied();
11    let a = ui.interaction_state.get(&Id::from_u64(base_id + 3)).copied();
12    
13    if let (Some(r), Some(g), Some(b), Some(a)) = (r, g, b, a) {
14        Color::rgba(r, g, b, a)
15    } else {
16        default
17    }
18}
19
20pub struct MenuBarBuilder<'u, 'a> {
21    ui: &'u mut Ui<'a>,
22    bg: Option<Color>,
23}
24
25impl<'u, 'a> MenuBarBuilder<'u, 'a> {
26    pub fn new(ui: &'u mut Ui<'a>) -> Self {
27        Self { ui, bg: None }
28    }
29
30    pub fn bg(mut self, color: Color) -> Self {
31        self.bg = Some(color);
32        self
33    }
34
35    pub fn show<F>(self, f: F)
36    where F: FnOnce(&mut Ui) {
37        let active_menu_key = Id::from_u64(999999900);
38        let active_submenu_key = Id::from_u64(999999901);
39        let hover_flag_key = Id::from_u64(999999902);
40
41        // Reset hover flag for this frame
42        self.ui.interaction_state.insert(hover_flag_key, 0.0);
43
44        // Render the horizontal menu bar container
45        let menu_bar_bg = self.bg.unwrap_or_else(|| {
46            let is_light_theme = self.ui.interaction_state.get(&zenthra_core::Id::from_u64(999999999)).copied().unwrap_or(0.0) > 0.5;
47            if is_light_theme {
48                Color::rgb(0.90, 0.90, 0.92)
49            } else {
50                Color::rgb(2.0 / 255.0, 2.0 / 255.0, 2.0 / 255.0) // 2 – menu bar
51            }
52        });
53
54        self.ui.container()
55            .full_width()
56            .height(30.0)
57            .bg(menu_bar_bg)
58            .row()
59            .padding_left(8.0)
60            .padding_right(8.0)
61            .show(|ui| {
62                f(ui);
63            });
64
65        // Click outside (light dismiss) handling
66        let clicked = self.ui.clicked;
67        let active_menu_id = self.ui.interaction_state.get(&active_menu_key).copied().map(|v| v as u64).unwrap_or(0);
68        let hover_flag = self.ui.interaction_state.get(&hover_flag_key).copied().unwrap_or(0.0) > 0.5;
69
70        if active_menu_id != 0 && clicked && !hover_flag {
71            self.ui.interaction_state.insert(active_menu_key, 0.0);
72            self.ui.interaction_state.insert(active_submenu_key, 0.0);
73            self.ui.needs_redraw = true;
74        }
75    }
76}
77
78pub struct MenuBuilder<'u, 'a> {
79    ui: &'u mut Ui<'a>,
80    label: String,
81    id: Id,
82}
83
84impl<'u, 'a> MenuBuilder<'u, 'a> {
85    pub fn new(ui: &'u mut Ui<'a>, label: &str) -> Self {
86        let mut hasher = std::collections::hash_map::DefaultHasher::new();
87        use std::hash::{Hash, Hasher};
88        label.hash(&mut hasher);
89        if let Some(parent) = ui.semantic_stack.last() {
90            parent.hash(&mut hasher);
91        }
92        let id = Id::from_u64((hasher.finish() & 0x7FFFFF) + 1);
93        Self {
94            ui,
95            label: label.to_string(),
96            id,
97        }
98    }
99
100    pub fn show<F>(self, f: F)
101    where F: FnOnce(&mut Ui) {
102        let active_menu_key = Id::from_u64(999999900);
103        let active_submenu_key = Id::from_u64(999999901);
104        let hover_flag_key = Id::from_u64(999999902);
105
106        let active_menu_id = self.ui.interaction_state.get(&active_menu_key).copied().map(|v| v as u64).unwrap_or(0);
107        let is_currently_active = active_menu_id == self.id.raw();
108
109        let (x, y) = (self.ui.cursor_x, self.ui.cursor_y);
110        let mut text_w = self.label.len() as f32 * 7.0;
111        if let Some(fs) = self.ui.font_system.as_ref() {
112            let mut adapter = zenthra_text::prelude::CosmicFontProvider::new_with_system(fs.clone());
113            let options = zenthra_text::prelude::TextOptions::new().font_size(13.0);
114            adapter.set_layout_size(1000.0, 100.0);
115            let buffer = adapter.shape(&self.label, &options);
116            text_w = buffer.size().0;
117        }
118        let padding_x = 10.0;
119        let w = text_w + padding_x * 2.0;
120        let h = 26.0;
121
122        let (actual_ox, actual_oy, actual_w, actual_h) = if let Some((rect, _)) = self.ui.get_recorded_layout(self.id) {
123            (
124                rect.origin.x + self.ui.offset_x,
125                rect.origin.y + self.ui.offset_y,
126                rect.size.width.max(w),
127                rect.size.height.max(h)
128            )
129        } else {
130            (x + self.ui.offset_x, y + self.ui.offset_y, w, h)
131        };
132
133        let is_hovered = self.ui.is_hovered(self.id, actual_ox, actual_oy, actual_w, actual_h);
134
135        if is_hovered {
136            self.ui.interaction_state.insert(hover_flag_key, 1.0);
137        }
138
139        // Auto-expand on hover if menu bar is active
140        if is_hovered && active_menu_id != 0 && !is_currently_active {
141            self.ui.interaction_state.insert(active_menu_key, self.id.raw() as f32);
142            self.ui.interaction_state.remove(&active_submenu_key);
143            self.ui.needs_redraw = true;
144        }
145
146        // Toggle on click
147        if self.ui.clicked && is_hovered {
148            if is_currently_active {
149                self.ui.interaction_state.insert(active_menu_key, 0.0);
150            } else {
151                self.ui.interaction_state.insert(active_menu_key, self.id.raw() as f32);
152            }
153            self.ui.interaction_state.remove(&active_submenu_key);
154            self.ui.needs_redraw = true;
155        }
156
157        let is_light_theme = self.ui.interaction_state.get(&Id::from_u64(999999999)).copied().unwrap_or(0.0) > 0.5;
158        let is_glassmorphism = self.ui.interaction_state.get(&Id::from_u64(999999998)).copied().unwrap_or(0.0) > 0.5;
159
160        let theme_accent = get_theme_color(self.ui, 999999980, Color::rgb(255.0 / 255.0, 214.0 / 255.0, 0.0 / 255.0));
161        let theme_highlight = get_theme_color(self.ui, 999999970, Color::rgba(255.0 / 255.0, 255.0 / 255.0, 255.0 / 255.0, 0.08));
162        let theme_text_primary = get_theme_color(self.ui, 999999960, Color::rgb(224.0 / 255.0, 224.0 / 255.0, 224.0 / 255.0));
163        let theme_bg_panel = get_theme_color(self.ui, 999999940, Color::rgb(1.0 / 255.0, 1.0 / 255.0, 1.0 / 255.0));
164        let theme_border = get_theme_color(self.ui, 999999930, Color::rgb(3.0 / 255.0, 3.0 / 255.0, 3.0 / 255.0));
165
166        let bg_color = if is_light_theme {
167            if is_currently_active {
168                Color::rgb(0.80, 0.80, 0.85)
169            } else if is_hovered {
170                Color::rgb(0.88, 0.88, 0.90)
171            } else {
172                Color::TRANSPARENT
173            }
174        } else {
175            if is_currently_active || is_hovered {
176                theme_highlight
177            } else {
178                Color::TRANSPARENT
179            }
180        };
181
182        let text_color = if is_light_theme {
183            Color::rgb(0.1, 0.1, 0.15)
184        } else {
185            if is_currently_active || is_hovered {
186                theme_accent
187            } else {
188                theme_text_primary
189            }
190        };
191
192        let start_draw = self.ui.draws.len();
193        self.ui.draws.push(DrawCommand::Rect(RectDraw {
194            instance: RectInstance {
195                pos: [x, y + 2.0],
196                size: [w, h],
197                color: bg_color.to_array(),
198                radius: [3.0; 4],
199                ..Default::default()
200            }
201        }));
202
203        self.ui.draws.push(DrawCommand::Text(TextDraw {
204            text: self.label.clone(),
205            pos: [x + (w - text_w) / 2.0, y + 6.0],
206            options: zenthra_text::prelude::TextOptions::new().font_size(13.0).color(text_color),
207            clip: [x, y, w, h + 4.0],
208        }));
209
210        self.ui.record_layout(self.id, Rect::new(x, y, w, h + 4.0));
211        self.ui.advance(w, h + 4.0, start_draw);
212
213        if is_currently_active {
214            let popup_id = Id::from_u64((self.id.raw() << 8) | 10);
215
216            // Check if popup is hovered
217            if let Some(rect) = self.ui.screen_layout_cache.get(&popup_id) {
218                if self.ui.mouse_in_rect(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height) {
219                    self.ui.interaction_state.insert(hover_flag_key, 1.0);
220                }
221            }
222
223            // Isolate layout state variables
224            let prev_child_ranges = std::mem::take(&mut self.ui.child_draw_ranges);
225            let prev_id_ranges = std::mem::take(&mut self.ui.id_ranges);
226            let prev_id_log = std::mem::take(&mut self.ui.id_log);
227            let prev_child_sizes = std::mem::take(&mut self.ui.child_sizes);
228            let prev_child_origins = std::mem::take(&mut self.ui.child_origins);
229
230            let popup_bg = if is_light_theme {
231                Color::WHITE
232            } else {
233                theme_bg_panel
234            };
235
236            let popup_border = if is_light_theme {
237                Color::rgb(0.8, 0.8, 0.85)
238            } else {
239                theme_border
240            };
241
242            let popup_x = if let Some((rect, _)) = self.ui.get_recorded_layout(self.id) {
243                rect.origin.x
244            } else {
245                x
246            };
247            let popup_y = if let Some((rect, _)) = self.ui.get_recorded_layout(self.id) {
248                rect.origin.y
249            } else {
250                y
251            };
252
253            self.ui.overlay(|ui| {
254                let mut container = ui.container()
255                    .id(popup_id)
256                    .absolute(popup_x, popup_y + h + 2.0)
257                    .overlay()
258                    .width(270.0)
259                    .radius_all(4.0)
260                    .padding(4.0, 4.0, 4.0, 4.0)
261                    .column();
262
263                if is_glassmorphism {
264                    container = container
265                        .bg(popup_bg.with_alpha(0.65))
266                        .border(popup_border.with_alpha(0.08), 1.0)
267                        .backdrop_filter(zenthra_core::BackdropFilter::new().blur(15.0, zenthra_core::style::blur::Type::Glassmorphism));
268                } else {
269                    container = container
270                        .bg(popup_bg)
271                        .border(popup_border, 1.0);
272                }
273
274                container.show(|ui| {
275                    f(ui);
276                });
277            });
278
279            // Discard child variables and restore parent's
280            let _ = std::mem::take(&mut self.ui.child_draw_ranges);
281            let _ = std::mem::take(&mut self.ui.id_ranges);
282            let _ = std::mem::take(&mut self.ui.id_log);
283            let _ = std::mem::take(&mut self.ui.child_sizes);
284            let _ = std::mem::take(&mut self.ui.child_origins);
285
286            self.ui.child_draw_ranges = prev_child_ranges;
287            self.ui.id_ranges = prev_id_ranges;
288            self.ui.id_log = prev_id_log;
289            self.ui.child_sizes = prev_child_sizes;
290            self.ui.child_origins = prev_child_origins;
291        }
292    }
293}
294
295pub struct SubMenuBuilder<'u, 'a> {
296    ui: &'u mut Ui<'a>,
297    label: String,
298    id: Id,
299}
300
301impl<'u, 'a> SubMenuBuilder<'u, 'a> {
302    pub fn new(ui: &'u mut Ui<'a>, label: &str) -> Self {
303        let mut hasher = std::collections::hash_map::DefaultHasher::new();
304        use std::hash::{Hash, Hasher};
305        label.hash(&mut hasher);
306        if let Some(parent) = ui.semantic_stack.last() {
307            parent.hash(&mut hasher);
308        }
309        let id = Id::from_u64((hasher.finish() & 0x7FFFFF) + 1);
310        Self {
311            ui,
312            label: label.to_string(),
313            id,
314        }
315    }
316
317    pub fn show<F>(self, f: F)
318    where F: FnOnce(&mut Ui) {
319        let active_submenu_key = Id::from_u64(999999901);
320        let hover_flag_key = Id::from_u64(999999902);
321
322        let active_submenu_id = self.ui.interaction_state.get(&active_submenu_key).copied().map(|v| v as u64).unwrap_or(0);
323        let is_currently_active = active_submenu_id == self.id.raw();
324
325        let (x, y) = (self.ui.cursor_x, self.ui.cursor_y);
326        let w = 262.0;
327        let h = 26.0;
328
329        let (actual_ox, actual_oy, actual_w, actual_h) = if let Some((rect, _)) = self.ui.get_recorded_layout(self.id) {
330            (
331                rect.origin.x + self.ui.offset_x,
332                rect.origin.y + self.ui.offset_y,
333                rect.size.width.max(w),
334                rect.size.height.max(h)
335            )
336        } else {
337            (x + self.ui.offset_x, y + self.ui.offset_y, w, h)
338        };
339
340        let is_hovered = self.ui.is_hovered(self.id, actual_ox, actual_oy, actual_w, actual_h);
341
342        if is_hovered {
343            self.ui.interaction_state.insert(hover_flag_key, 1.0);
344        }
345
346        // Toggle on click
347        if self.ui.clicked && is_hovered {
348            if is_currently_active {
349                self.ui.interaction_state.insert(active_submenu_key, 0.0);
350            } else {
351                self.ui.interaction_state.insert(active_submenu_key, self.id.raw() as f32);
352            }
353            self.ui.needs_redraw = true;
354        }
355
356        let is_light_theme = self.ui.interaction_state.get(&Id::from_u64(999999999)).copied().unwrap_or(0.0) > 0.5;
357        let is_glassmorphism = self.ui.interaction_state.get(&Id::from_u64(999999998)).copied().unwrap_or(0.0) > 0.5;
358
359        let theme_accent = get_theme_color(self.ui, 999999980, Color::rgb(255.0 / 255.0, 214.0 / 255.0, 0.0 / 255.0));
360        let theme_highlight = get_theme_color(self.ui, 999999970, Color::rgba(255.0 / 255.0, 255.0 / 255.0, 255.0 / 255.0, 0.08));
361        let theme_text_primary = get_theme_color(self.ui, 999999960, Color::rgb(224.0 / 255.0, 224.0 / 255.0, 224.0 / 255.0));
362        let theme_text_muted = get_theme_color(self.ui, 999999950, Color::rgb(136.0 / 255.0, 136.0 / 255.0, 136.0 / 255.0));
363        let theme_bg_panel = get_theme_color(self.ui, 999999940, Color::rgb(1.0 / 255.0, 1.0 / 255.0, 1.0 / 255.0));
364        let theme_border = get_theme_color(self.ui, 999999930, Color::rgb(3.0 / 255.0, 3.0 / 255.0, 3.0 / 255.0));
365
366        let bg_color = if is_light_theme {
367            if is_currently_active || is_hovered {
368                Color::rgb(0.90, 0.90, 0.95)
369            } else {
370                Color::TRANSPARENT
371            }
372        } else {
373            if is_currently_active || is_hovered {
374                theme_highlight
375            } else {
376                Color::TRANSPARENT
377            }
378        };
379
380        let text_color = if is_light_theme {
381            Color::rgb(0.1, 0.1, 0.15)
382        } else {
383            if is_currently_active || is_hovered {
384                theme_accent
385            } else {
386                theme_text_primary
387            }
388        };
389
390        let chevron_color = if is_light_theme {
391            Color::rgb(0.5, 0.5, 0.5)
392        } else {
393            if is_currently_active || is_hovered {
394                theme_accent
395            } else {
396                theme_text_muted
397            }
398        };
399
400        let start_draw = self.ui.draws.len();
401
402        self.ui.draws.push(DrawCommand::Rect(RectDraw {
403            instance: RectInstance {
404                pos: [x, y],
405                size: [w, h],
406                color: bg_color.to_array(),
407                radius: [3.0; 4],
408                ..Default::default()
409            }
410        }));
411
412        self.ui.draws.push(DrawCommand::Text(TextDraw {
413            text: self.label.clone(),
414            pos: [x + 8.0, y + 6.0],
415            options: zenthra_text::prelude::TextOptions::new().font_size(13.0).color(text_color),
416            clip: [x, y, w, h],
417        }));
418
419        // Right arrow indicator
420        self.ui.draws.push(DrawCommand::Text(TextDraw {
421            text: crate::icons::NF_FA_CHEVRON_RIGHT.to_string(),
422            pos: [x + w - 15.0, y + 6.0],
423            options: zenthra_text::prelude::TextOptions::new().font_size(12.0).color(chevron_color),
424            clip: [x, y, w, h],
425        }));
426
427        self.ui.record_layout(self.id, Rect::new(x, y, w, h));
428        self.ui.advance(w, h, start_draw);
429
430        if is_currently_active {
431            let sub_popup_id = Id::from_u64((self.id.raw() << 8) | 11);
432
433            // Check if sub-popup is hovered
434            if let Some(rect) = self.ui.screen_layout_cache.get(&sub_popup_id) {
435                if self.ui.mouse_in_rect(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height) {
436                    self.ui.interaction_state.insert(hover_flag_key, 1.0);
437                }
438            }
439
440            // Isolate layout state variables
441            let prev_child_ranges = std::mem::take(&mut self.ui.child_draw_ranges);
442            let prev_id_ranges = std::mem::take(&mut self.ui.id_ranges);
443            let prev_id_log = std::mem::take(&mut self.ui.id_log);
444            let prev_child_sizes = std::mem::take(&mut self.ui.child_sizes);
445            let prev_child_origins = std::mem::take(&mut self.ui.child_origins);
446
447            let popup_bg = if is_light_theme {
448                Color::WHITE
449            } else {
450                theme_bg_panel
451            };
452
453            let popup_border = if is_light_theme {
454                Color::rgb(0.8, 0.8, 0.85)
455            } else {
456                theme_border
457            };
458
459            self.ui.overlay(|ui| {
460                let mut container = ui.container()
461                    .id(sub_popup_id)
462                    .absolute(x + w - 2.0, y)
463                    .overlay()
464                    .width(270.0)
465                    .radius_all(4.0)
466                    .padding(4.0, 4.0, 4.0, 4.0)
467                    .column();
468
469                if is_glassmorphism {
470                    container = container
471                        .bg(popup_bg.with_alpha(0.65))
472                        .border(popup_border.with_alpha(0.08), 1.0)
473                        .backdrop_filter(zenthra_core::BackdropFilter::new().blur(15.0, zenthra_core::style::blur::Type::Glassmorphism));
474                } else {
475                    container = container
476                        .bg(popup_bg)
477                        .border(popup_border, 1.0);
478                }
479
480                container.show(|ui| {
481                    f(ui);
482                });
483            });
484
485            // Discard child variables and restore parent's
486            let _ = std::mem::take(&mut self.ui.child_draw_ranges);
487            let _ = std::mem::take(&mut self.ui.id_ranges);
488            let _ = std::mem::take(&mut self.ui.id_log);
489            let _ = std::mem::take(&mut self.ui.child_sizes);
490            let _ = std::mem::take(&mut self.ui.child_origins);
491
492            self.ui.child_draw_ranges = prev_child_ranges;
493            self.ui.id_ranges = prev_id_ranges;
494            self.ui.id_log = prev_id_log;
495            self.ui.child_sizes = prev_child_sizes;
496            self.ui.child_origins = prev_child_origins;
497        }
498    }
499}
500
501pub struct MenuItemBuilder<'u, 'a> {
502    ui: &'u mut Ui<'a>,
503    label: String,
504    shortcut: Option<String>,
505    id: Id,
506}
507
508impl<'u, 'a> MenuItemBuilder<'u, 'a> {
509    pub fn new(ui: &'u mut Ui<'a>, label: &str) -> Self {
510        let mut hasher = std::collections::hash_map::DefaultHasher::new();
511        use std::hash::{Hash, Hasher};
512        label.hash(&mut hasher);
513        if let Some(parent) = ui.semantic_stack.last() {
514            parent.hash(&mut hasher);
515        }
516        let id = Id::from_u64((hasher.finish() & 0x7FFFFF) + 1);
517        Self {
518            ui,
519            label: label.to_string(),
520            shortcut: None,
521            id,
522        }
523    }
524
525    pub fn shortcut(mut self, shortcut: &str) -> Self {
526        self.shortcut = Some(shortcut.to_string());
527        self
528    }
529
530    pub fn show(self) -> Response {
531        let hover_flag_key = Id::from_u64(999999902);
532
533        let (x, y) = (self.ui.cursor_x, self.ui.cursor_y);
534        let w = 262.0;
535        let h = 26.0;
536
537        let (actual_ox, actual_oy, actual_w, actual_h) = if let Some((rect, _)) = self.ui.get_recorded_layout(self.id) {
538            (
539                rect.origin.x + self.ui.offset_x,
540                rect.origin.y + self.ui.offset_y,
541                rect.size.width.max(w),
542                rect.size.height.max(h)
543            )
544        } else {
545            (x + self.ui.offset_x, y + self.ui.offset_y, w, h)
546        };
547
548        let is_hovered = self.ui.is_hovered(self.id, actual_ox, actual_oy, actual_w, actual_h);
549
550        if is_hovered {
551            self.ui.interaction_state.insert(hover_flag_key, 1.0);
552        }
553
554        let is_light_theme = self.ui.interaction_state.get(&Id::from_u64(999999999)).copied().unwrap_or(0.0) > 0.5;
555
556        let theme_accent = get_theme_color(self.ui, 999999980, Color::rgb(255.0 / 255.0, 214.0 / 255.0, 0.0 / 255.0));
557        let theme_highlight = get_theme_color(self.ui, 999999970, Color::rgba(255.0 / 255.0, 255.0 / 255.0, 255.0 / 255.0, 0.08));
558        let theme_text_primary = get_theme_color(self.ui, 999999960, Color::rgb(224.0 / 255.0, 224.0 / 255.0, 224.0 / 255.0));
559        let theme_text_muted = get_theme_color(self.ui, 999999950, Color::rgb(136.0 / 255.0, 136.0 / 255.0, 136.0 / 255.0));
560
561        let bg_color = if is_light_theme {
562            if is_hovered {
563                Color::rgb(0.9, 0.9, 0.95)
564            } else {
565                Color::TRANSPARENT
566            }
567        } else {
568            if is_hovered {
569                theme_highlight
570            } else {
571                Color::TRANSPARENT
572            }
573        };
574
575        let text_color = if is_light_theme {
576            Color::rgb(0.1, 0.1, 0.15)
577        } else {
578            if is_hovered {
579                theme_accent
580            } else {
581                theme_text_primary
582            }
583        };
584
585        let shortcut_color = if is_light_theme {
586            Color::rgb(0.5, 0.5, 0.5)
587        } else {
588            if is_hovered {
589                theme_accent
590            } else {
591                theme_text_muted
592            }
593        };
594
595        let start_draw = self.ui.draws.len();
596
597        self.ui.draws.push(DrawCommand::Rect(RectDraw {
598            instance: RectInstance {
599                pos: [x, y],
600                size: [w, h],
601                color: bg_color.to_array(),
602                radius: [3.0; 4],
603                ..Default::default()
604            }
605        }));
606
607        self.ui.draws.push(DrawCommand::Text(TextDraw {
608            text: self.label.clone(),
609            pos: [x + 8.0, y + 6.0],
610            options: zenthra_text::prelude::TextOptions::new().font_size(13.0).color(text_color),
611            clip: [x, y, w, h],
612        }));
613
614        if let Some(sh) = self.shortcut {
615            self.ui.draws.push(DrawCommand::Text(TextDraw {
616                text: sh,
617                pos: [x + w - 80.0, y + 6.0],
618                options: zenthra_text::prelude::TextOptions::new().font_size(12.0).color(shortcut_color),
619                clip: [x, y, w, h],
620            }));
621        }
622
623        self.ui.record_layout(self.id, Rect::new(x, y, w, h));
624        self.ui.advance(w, h, start_draw);
625
626        let clicked = self.ui.clicked && is_hovered;
627        if clicked {
628            // Close all menus upon item selection
629            let active_menu_key = Id::from_u64(999999900);
630            let active_submenu_key = Id::from_u64(999999901);
631            self.ui.interaction_state.insert(active_menu_key, 0.0);
632            self.ui.interaction_state.insert(active_submenu_key, 0.0);
633            self.ui.needs_redraw = true;
634        }
635
636        Response {
637            clicked,
638            hovered: is_hovered,
639            pressed: is_hovered && self.ui.mouse_down,
640        }
641    }
642}