yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
menu_builder.cc
Go to the documentation of this file.
2
3#include "absl/strings/str_cat.h"
4
5namespace yaze {
6namespace editor {
7
8MenuBuilder& MenuBuilder::BeginMenu(const char* label, const char* icon) {
9 Menu menu;
10 menu.label = label;
11 if (icon) {
12 menu.icon = icon;
13 }
14 menus_.push_back(menu);
15 current_menu_ = &menus_.back();
16 return *this;
17}
18
19MenuBuilder& MenuBuilder::BeginSubMenu(const char* label, const char* icon,
20 EnabledCheck enabled) {
21 if (!current_menu_)
22 return *this;
23
24 MenuItem item;
26 item.label = label;
27 if (icon) {
28 item.icon = icon;
29 }
30 item.enabled = enabled;
31 current_menu_->items.push_back(item);
32 return *this;
33}
34
36 if (!current_menu_)
37 return *this;
38
39 // Check if we're ending a submenu or top-level menu
40 // We need to track nesting depth to handle nested submenus correctly
41 bool is_submenu = false;
42 int depth = 0;
43
44 for (auto it = current_menu_->items.rbegin();
45 it != current_menu_->items.rend(); ++it) {
46 if (it->type == MenuItem::Type::kSubMenuEnd) {
47 depth++; // Found an end, so we need to skip its matching begin
48 } else if (it->type == MenuItem::Type::kSubMenuBegin) {
49 if (depth == 0) {
50 // Found an unmatched begin - this is what we're closing
51 is_submenu = true;
52 break;
53 }
54 depth--; // This begin matches a previous end
55 }
56 }
57
58 if (is_submenu) {
59 MenuItem item;
61 current_menu_->items.push_back(item);
62 } else {
63 current_menu_ = nullptr;
64 }
65
66 return *this;
67}
68
69MenuBuilder& MenuBuilder::Item(const char* label, const char* icon,
70 Callback callback, const char* shortcut,
71 EnabledCheck enabled, EnabledCheck checked) {
72 if (!current_menu_)
73 return *this;
74
75 MenuItem item;
77 item.label = label;
78 if (icon) {
79 item.icon = icon;
80 }
81 if (shortcut) {
82 item.shortcut = shortcut;
83 }
84 item.callback = callback;
85 item.enabled = enabled;
86 item.checked = checked;
87 current_menu_->items.push_back(item);
88 return *this;
89}
90
91MenuBuilder& MenuBuilder::Item(const char* label, Callback callback,
92 const char* shortcut, EnabledCheck enabled) {
93 return Item(label, nullptr, callback, shortcut, enabled, nullptr);
94}
95
97 if (!current_menu_)
98 return *this;
99
100 MenuItem item;
102 current_menu_->items.push_back(item);
103 return *this;
104}
105
106MenuBuilder& MenuBuilder::DisabledItem(const char* label, const char* icon) {
107 if (!current_menu_)
108 return *this;
109
110 MenuItem item;
112 item.label = label;
113 if (icon) {
114 item.icon = icon;
115 }
116 current_menu_->items.push_back(item);
117 return *this;
118}
119
120MenuBuilder& MenuBuilder::CustomMenu(const char* label, Callback draw_callback) {
121 Menu menu;
122 menu.label = label;
123 menu.custom_draw = draw_callback;
124 menu.is_custom = true;
125 menus_.push_back(menu);
126 current_menu_ = nullptr; // Custom menus don't use the builder pattern
127 return *this;
128}
129
131 for (const auto& menu : menus_) {
132 // Don't add icons to top-level menus as they get cut off
133 std::string menu_label = menu.label;
134
135 if (menu.is_custom) {
136 // Custom menu with callback for dynamic content
137 if (ImGui::BeginMenu(menu_label.c_str())) {
138 if (menu.custom_draw) {
139 menu.custom_draw();
140 }
141 ImGui::EndMenu();
142 }
143 } else {
144 // Standard menu with predefined items
145 if (ImGui::BeginMenu(menu_label.c_str())) {
146 submenu_stack_.clear(); // Reset submenu stack for each top-level menu
147 skip_depth_ = 0; // Reset skip depth
148 for (const auto& item : menu.items) {
149 DrawMenuItem(item);
150 }
151 ImGui::EndMenu();
152 }
153 }
154 }
155}
156
158 switch (item.type) {
160 if (skip_depth_ == 0) {
161 ImGui::Separator();
162 }
163 break;
164
166 if (skip_depth_ == 0) {
167 std::string label = item.icon.empty()
168 ? item.label
169 : absl::StrCat(item.icon, " ", item.label);
170 ImGui::BeginDisabled();
171 ImGui::MenuItem(label.c_str(), nullptr, false, false);
172 ImGui::EndDisabled();
173 }
174 break;
175 }
176
178 // If we're already skipping, increment skip depth and continue
179 if (skip_depth_ > 0) {
180 skip_depth_++;
181 submenu_stack_.push_back(false);
182 break;
183 }
184
185 std::string label = item.icon.empty()
186 ? item.label
187 : absl::StrCat(item.icon, " ", item.label);
188
189 bool enabled = !item.enabled || item.enabled();
190 bool opened = false;
191
192 if (!enabled) {
193 // Disabled submenu - show as disabled item but don't open
194 ImGui::BeginDisabled();
195 ImGui::MenuItem(label.c_str(), nullptr, false, false);
196 ImGui::EndDisabled();
197 submenu_stack_.push_back(false);
198 skip_depth_++; // Skip contents of disabled submenu
199 } else {
200 // BeginMenu returns true if submenu is currently open/visible
201 opened = ImGui::BeginMenu(label.c_str());
202 submenu_stack_.push_back(opened);
203 if (!opened) {
204 skip_depth_++; // Skip contents of closed submenu
205 }
206 }
207 break;
208 }
209
211 // Decrement skip depth if we were skipping
212 if (skip_depth_ > 0) {
213 skip_depth_--;
214 }
215
216 // Pop the stack and call EndMenu only if submenu was opened
217 if (!submenu_stack_.empty()) {
218 bool was_opened = submenu_stack_.back();
219 submenu_stack_.pop_back();
220 if (was_opened && skip_depth_ == 0) {
221 ImGui::EndMenu();
222 }
223 }
224 break;
225
227 if (skip_depth_ > 0) {
228 break; // Skip items in closed submenus
229 }
230
231 std::string label = item.icon.empty()
232 ? item.label
233 : absl::StrCat(item.icon, " ", item.label);
234
235 bool enabled = !item.enabled || item.enabled();
236 bool checked = item.checked && item.checked();
237
238 const char* shortcut_str =
239 item.shortcut.empty() ? nullptr : item.shortcut.c_str();
240
241 if (ImGui::MenuItem(label.c_str(), shortcut_str, checked, enabled)) {
242 if (item.callback) {
243 item.callback();
244 }
245 }
246 break;
247 }
248 }
249}
250
252 menus_.clear();
253 current_menu_ = nullptr;
254}
255
256} // namespace editor
257} // namespace yaze
Fluent interface for building ImGui menus with icons.
MenuBuilder & Item(const char *label, const char *icon, Callback callback, const char *shortcut=nullptr, EnabledCheck enabled=nullptr, EnabledCheck checked=nullptr)
Add a menu item.
MenuBuilder & CustomMenu(const char *label, Callback draw_callback)
Add a custom menu with a callback for drawing dynamic content.
void Draw()
Draw the menu bar (call in main menu bar)
void Clear()
Clear all menus.
std::function< bool()> EnabledCheck
MenuBuilder & BeginMenu(const char *label, const char *icon=nullptr)
Begin a top-level menu.
MenuBuilder & Separator()
Add a separator.
MenuBuilder & EndMenu()
End the current menu/submenu.
MenuBuilder & BeginSubMenu(const char *label, const char *icon=nullptr, EnabledCheck enabled=nullptr)
Begin a submenu.
std::function< void()> Callback
void DrawMenuItem(const MenuItem &item)
std::vector< Menu > menus_
MenuBuilder & DisabledItem(const char *label, const char *icon=nullptr)
Add a disabled item (grayed out)
std::vector< bool > submenu_stack_
std::vector< MenuItem > items