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_) return *this;
22
23 MenuItem item;
25 item.label = label;
26 if (icon) {
27 item.icon = icon;
28 }
29 item.enabled = enabled;
30 current_menu_->items.push_back(item);
31 return *this;
32}
33
35 if (!current_menu_) return *this;
36
37 // Check if we're ending a submenu or top-level menu
38 // We need to track nesting depth to handle nested submenus correctly
39 bool is_submenu = false;
40 int depth = 0;
41
42 for (auto it = current_menu_->items.rbegin();
43 it != current_menu_->items.rend(); ++it) {
44 if (it->type == MenuItem::Type::kSubMenuEnd) {
45 depth++; // Found an end, so we need to skip its matching begin
46 } else if (it->type == MenuItem::Type::kSubMenuBegin) {
47 if (depth == 0) {
48 // Found an unmatched begin - this is what we're closing
49 is_submenu = true;
50 break;
51 }
52 depth--; // This begin matches a previous end
53 }
54 }
55
56 if (is_submenu) {
57 MenuItem item;
59 current_menu_->items.push_back(item);
60 } else {
61 current_menu_ = nullptr;
62 }
63
64 return *this;
65}
66
67MenuBuilder& MenuBuilder::Item(const char* label, const char* icon,
68 Callback callback, const char* shortcut,
69 EnabledCheck enabled, EnabledCheck checked) {
70 if (!current_menu_) return *this;
71
72 MenuItem item;
74 item.label = label;
75 if (icon) {
76 item.icon = icon;
77 }
78 if (shortcut) {
79 item.shortcut = shortcut;
80 }
81 item.callback = callback;
82 item.enabled = enabled;
83 item.checked = checked;
84 current_menu_->items.push_back(item);
85 return *this;
86}
87
88MenuBuilder& MenuBuilder::Item(const char* label, Callback callback,
89 const char* shortcut, EnabledCheck enabled) {
90 return Item(label, nullptr, callback, shortcut, enabled, nullptr);
91}
92
94 if (!current_menu_) return *this;
95
96 MenuItem item;
98 current_menu_->items.push_back(item);
99 return *this;
100}
101
102MenuBuilder& MenuBuilder::DisabledItem(const char* label, const char* icon) {
103 if (!current_menu_) return *this;
104
105 MenuItem item;
107 item.label = label;
108 if (icon) {
109 item.icon = icon;
110 }
111 current_menu_->items.push_back(item);
112 return *this;
113}
114
116 for (const auto& menu : menus_) {
117 // Don't add icons to top-level menus as they get cut off
118 std::string menu_label = menu.label;
119
120 if (ImGui::BeginMenu(menu_label.c_str())) {
121 submenu_stack_.clear(); // Reset submenu stack for each top-level menu
122 skip_depth_ = 0; // Reset skip depth
123 for (const auto& item : menu.items) {
124 DrawMenuItem(item);
125 }
126 ImGui::EndMenu();
127 }
128 }
129}
130
132 switch (item.type) {
134 if (skip_depth_ == 0) {
135 ImGui::Separator();
136 }
137 break;
138
140 if (skip_depth_ == 0) {
141 std::string label = item.icon.empty()
142 ? item.label
143 : absl::StrCat(item.icon, " ", item.label);
144 ImGui::BeginDisabled();
145 ImGui::MenuItem(label.c_str(), nullptr, false, false);
146 ImGui::EndDisabled();
147 }
148 break;
149 }
150
152 // If we're already skipping, increment skip depth and continue
153 if (skip_depth_ > 0) {
154 skip_depth_++;
155 submenu_stack_.push_back(false);
156 break;
157 }
158
159 std::string label = item.icon.empty()
160 ? item.label
161 : absl::StrCat(item.icon, " ", item.label);
162
163 bool enabled = !item.enabled || item.enabled();
164 bool opened = false;
165
166 if (!enabled) {
167 // Disabled submenu - show as disabled item but don't open
168 ImGui::BeginDisabled();
169 ImGui::MenuItem(label.c_str(), nullptr, false, false);
170 ImGui::EndDisabled();
171 submenu_stack_.push_back(false);
172 skip_depth_++; // Skip contents of disabled submenu
173 } else {
174 // BeginMenu returns true if submenu is currently open/visible
175 opened = ImGui::BeginMenu(label.c_str());
176 submenu_stack_.push_back(opened);
177 if (!opened) {
178 skip_depth_++; // Skip contents of closed submenu
179 }
180 }
181 break;
182 }
183
185 // Decrement skip depth if we were skipping
186 if (skip_depth_ > 0) {
187 skip_depth_--;
188 }
189
190 // Pop the stack and call EndMenu only if submenu was opened
191 if (!submenu_stack_.empty()) {
192 bool was_opened = submenu_stack_.back();
193 submenu_stack_.pop_back();
194 if (was_opened && skip_depth_ == 0) {
195 ImGui::EndMenu();
196 }
197 }
198 break;
199
201 if (skip_depth_ > 0) {
202 break; // Skip items in closed submenus
203 }
204
205 std::string label = item.icon.empty()
206 ? item.label
207 : absl::StrCat(item.icon, " ", item.label);
208
209 bool enabled = !item.enabled || item.enabled();
210 bool checked = item.checked && item.checked();
211
212 const char* shortcut_str = item.shortcut.empty()
213 ? nullptr
214 : item.shortcut.c_str();
215
216 if (ImGui::MenuItem(label.c_str(), shortcut_str, checked, enabled)) {
217 if (item.callback) {
218 item.callback();
219 }
220 }
221 break;
222 }
223 }
224}
225
227 menus_.clear();
228 current_menu_ = nullptr;
229}
230
231} // namespace editor
232} // 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.
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_
Main namespace for the application.
std::vector< MenuItem > items