1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
/// Recursive menu state for rendering - can handle N levels of nesting
#[derive(Debug, Clone)]
pub struct RenderState {
/// open_menus[i] = Some(index) if menu at depth i is open with item at index selected
/// open_menus[0] = Some(0) means main menu item 0 is open (File menu)
/// open_menus[1] = Some(2) means submenu item 2 is open (Recent Files)
pub open_menus: Vec<Option<usize>>,
/// selections[i] = selected item index at depth i
/// selections[0] = 0 means File menu is selected
/// selections[1] = 2 means Recent Files is selected
pub selections: Vec<usize>,
}
impl RenderState {
pub fn new() -> Self {
Self {
open_menus: Vec::new(),
selections: Vec::new(),
}
}
/// Reset all state
pub fn reset(&mut self) {
self.open_menus.clear();
self.selections.clear();
}
/// Get current depth (how many menus are open)
pub fn get_current_depth(&self) -> usize {
self.open_menus.len()
}
/// Check if any menu is open
pub fn is_any_menu_open(&self) -> bool {
!self.open_menus.is_empty()
}
/// Get the index of the open menu at specific depth
pub fn get_open_menu_at_depth(&self, depth: usize) -> Option<usize> {
self.open_menus.get(depth).copied().flatten()
}
/// Get the selection at specific depth
pub fn get_selection_at_depth(&self, depth: usize) -> Option<usize> {
self.selections.get(depth).copied()
}
/// Open a menu at specific depth with specific selection
pub fn open_menu_at_depth(&mut self, depth: usize, menu_index: usize, selection: usize) {
// Ensure vectors are long enough
while self.open_menus.len() <= depth {
self.open_menus.push(None);
self.selections.push(0);
}
self.open_menus[depth] = Some(menu_index);
self.selections[depth] = selection;
// Close all deeper menus
self.open_menus.truncate(depth + 1);
self.selections.truncate(depth + 1);
}
/// Close menu at specific depth (and all deeper menus)
pub fn close_menus_from_depth(&mut self, depth: usize) {
self.open_menus.truncate(depth);
self.selections.truncate(depth);
}
/// Update selection at specific depth
pub fn update_selection_at_depth(&mut self, depth: usize, selection: usize) {
if depth < self.selections.len() {
self.selections[depth] = selection;
}
}
/// Go deeper (open submenu of current item)
/// Updates the current level's selection to the clicked item so the parent menu highlights the correct row (e.g. "Recent Files" not "New").
pub fn go_deeper(&mut self, item_index: usize) {
if !self.selections.is_empty() {
let last = self.selections.len() - 1;
self.selections[last] = item_index;
}
self.open_menus.push(Some(item_index));
self.selections.push(0);
}
/// Go up (close current level)
pub fn go_up(&mut self) -> bool {
if self.open_menus.is_empty() {
return false;
}
self.open_menus.pop();
self.selections.pop();
true
}
}
impl Default for RenderState {
fn default() -> Self {
Self::new()
}
}