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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
use egui::{Context, Key};
use crate::TitleBar;
impl TitleBar {
/// Get the maximum fitted index (repeated calculation pattern)
fn get_max_fitted_index(&self) -> usize {
self.items_fitted.len().saturating_sub(1)
}
/// Handle main menu bar navigation (top-level menu items)
pub fn handle_main_menu_navigation(&mut self, ctx: &Context) -> bool {
let current_time = ctx.input(|i| i.time);
let has_overflow = self.items_fitted.len() < self.menu_order.len();
if self.items_fitted.is_empty() {
// No visible menus (minimal mode) - open overlay with Down/Right/Enter/Space
if ctx.input(|i| i.key_pressed(Key::ArrowDown))
|| ctx.input(|i| i.key_pressed(Key::ArrowRight))
|| ctx.input(|i| i.key_pressed(Key::Enter))
|| ctx.input(|i| i.key_pressed(Key::Space))
{
if !self.hamburger_menu_open {
self.hamburger_menu_open = true;
self.hamburger_open_time = Some(current_time);
self.reset_all_menu_states();
// Select first item in overlay
self.overlay_selected_index = Some(0);
}
return true;
}
} else if has_overflow && self.selected_menu_index.is_none() {
// Mixed mode (items + overflow) - dots selected (or no selection yet).
// When dots are selected we must NOT wrap around to index 0 with ArrowRight.
if ctx.input(|i| i.key_pressed(Key::ArrowRight)) && !self.dots_selected {
// No selection yet (coming from activation): select first fitted item
self.selected_menu_index = Some(0);
return true;
} else if ctx.input(|i| i.key_pressed(Key::ArrowLeft)) && self.dots_selected {
// We're on dots, go to the last fitted item and close overlay
let max_fitted_index = self.get_max_fitted_index();
self.selected_menu_index = Some(max_fitted_index);
self.dots_selected = false;
self.hamburger_menu_open = false; // Close overlay
return true;
} else if (ctx.input(|i| i.key_pressed(Key::Enter))
|| ctx.input(|i| i.key_pressed(Key::Space)))
&& self.dots_selected
{
// Enter/Space on dots: toggle overflow overlay
if self.hamburger_menu_open {
self.hamburger_menu_open = false;
self.overlay_selected_index = None;
} else {
self.hamburger_menu_open = true;
self.hamburger_open_time = Some(current_time);
let first_overlay_index = self
.items_fitted
.last()
.map(|i| i.saturating_add(1))
.unwrap_or(0);
self.overlay_selected_index = Some(first_overlay_index);
}
return true;
}
} else {
// Check if any submenu is open before allowing main menu navigation
let has_open_submenu = self.render_state.is_any_menu_open();
if !has_open_submenu {
// Normal mode - handle main menu navigation only when no submenu is open
if ctx.input(|i| i.key_pressed(Key::ArrowRight)) {
// Allow main menu navigation only when no submenu is open
if !self.dots_selected {
if let Some(current_index) = self.selected_menu_index {
// Navigation limit: don't go beyond fitted items
let max_fitted_index = self.get_max_fitted_index();
if current_index < max_fitted_index {
self.selected_menu_index = Some(current_index + 1);
} else if current_index == max_fitted_index && has_overflow {
// We're at the last fitted item, select dots only
self.dots_selected = true;
self.selected_menu_index = None; // Clear menu selection when on dots
}
}
// Close hamburger overlay when navigating to normal menu
self.hamburger_menu_open = false;
self.overlay_selected_index = None;
}
return true;
}
if ctx.input(|i| i.key_pressed(Key::ArrowLeft)) {
// Allow main menu left navigation only when no submenu is open
if !self.dots_selected {
if let Some(current_index) = self.selected_menu_index {
if current_index > 0 {
self.selected_menu_index = Some(current_index - 1);
}
// On first item (index 0): do nothing, do not wrap to dots
}
// Close hamburger overlay when navigating to normal menu
self.hamburger_menu_open = false;
self.overlay_selected_index = None;
}
return true;
}
}
// Handle Enter/Space when no submenu is open
let open_submenu_index = self.render_state.get_open_menu_at_depth(0);
if open_submenu_index.is_none() {
if ctx.input(|i| i.key_pressed(Key::Enter))
|| ctx.input(|i| i.key_pressed(Key::Space))
{
if let Some(menu_index) = self.selected_menu_index {
// Get the actual menu item from menu_order
if let Some(&(is_submenu, actual_index)) = self.menu_order.get(menu_index) {
if is_submenu {
// Menu with submenu
if let Some(menu_item) =
self.menu_items_with_submenus.get(actual_index)
{
if !menu_item.subitems.is_empty() {
self.submenu_from_hamburger = false; // Open from bar, not overlay
self.open_menu_recursive(0, actual_index, 0);
self.submenu_just_opened_frame = true;
}
}
} else {
// Simple menu item - trigger callback
if let Some((_, callback)) = self.menu_items.get(actual_index) {
if let Some(callback) = callback {
callback();
}
}
}
}
}
return true;
}
}
}
false
}
}