yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
editor_selection_dialog.cc
Go to the documentation of this file.
2
3#include <algorithm>
4#include <fstream>
5#include <sstream>
6
7#include "absl/strings/str_cat.h"
8#include "absl/strings/str_format.h"
11#include "app/gui/core/style.h"
12#include "imgui/imgui.h"
13#include "util/file_util.h"
14
15namespace yaze {
16namespace editor {
17
19 // Initialize editor metadata with distinct colors
20 // Use platform-aware shortcut strings (Cmd on macOS, Ctrl elsewhere)
21 const char* ctrl = gui::GetCtrlDisplayName();
22 editors_ = {
24 "Edit overworld maps, entrances, and properties",
25 absl::StrFormat("%s+1", ctrl), false, true,
26 ImVec4(0.133f, 0.545f, 0.133f, 1.0f)}, // Hyrule green
27
29 "Design dungeon rooms, layouts, and mechanics",
30 absl::StrFormat("%s+2", ctrl), false, true,
31 ImVec4(0.502f, 0.0f, 0.502f, 1.0f)}, // Ganon purple
32
34 "Modify tiles, palettes, and graphics sets",
35 absl::StrFormat("%s+3", ctrl), false, true,
36 ImVec4(1.0f, 0.843f, 0.0f, 1.0f)}, // Triforce gold
37
39 "Edit sprite graphics and properties",
40 absl::StrFormat("%s+4", ctrl), false, true,
41 ImVec4(1.0f, 0.647f, 0.0f, 1.0f)}, // Spirit orange
42
44 "Edit dialogue, signs, and text",
45 absl::StrFormat("%s+5", ctrl), false, true,
46 ImVec4(0.196f, 0.6f, 0.8f, 1.0f)}, // Master sword blue
47
49 "Configure music and sound effects",
50 absl::StrFormat("%s+6", ctrl), false, true,
51 ImVec4(0.416f, 0.353f, 0.804f, 1.0f)}, // Shadow purple
52
54 "Edit color palettes and animations",
55 absl::StrFormat("%s+7", ctrl), false, true,
56 ImVec4(0.863f, 0.078f, 0.235f, 1.0f)}, // Heart red
57
59 "Edit title screen and ending screens",
60 absl::StrFormat("%s+8", ctrl), false, true,
61 ImVec4(0.4f, 0.8f, 1.0f, 1.0f)}, // Sky blue
62
64 "Write and edit assembly code",
65 absl::StrFormat("%s+9", ctrl), false, false,
66 ImVec4(0.8f, 0.8f, 0.8f, 1.0f)}, // Silver
67
69 "Direct ROM memory editing and comparison",
70 absl::StrFormat("%s+0", ctrl), false, true,
71 ImVec4(0.2f, 0.8f, 0.4f, 1.0f)}, // Matrix green
72
74 "Test and debug your ROM in real-time with live debugging",
75 absl::StrFormat("%s+Shift+E", ctrl), false, true,
76 ImVec4(0.2f, 0.6f, 1.0f, 1.0f)}, // Emulator blue
77
79 "Configure AI agent, collaboration, and automation",
80 absl::StrFormat("%s+Shift+A", ctrl), false, false,
81 ImVec4(0.8f, 0.4f, 1.0f, 1.0f)}, // Purple/magenta
82 };
83
85}
86
87bool EditorSelectionDialog::Show(bool* p_open) {
88 // Sync internal state with external flag
89 if (p_open && *p_open && !is_open_) {
90 is_open_ = true;
91 }
92
93 if (!is_open_) {
94 if (p_open)
95 *p_open = false;
96 return false;
97 }
98
99 bool editor_selected = false;
100 bool* window_open = p_open ? p_open : &is_open_;
101
102 // Set window properties immediately before Begin to prevent them from
103 // affecting tooltips
104 ImVec2 center = ImGui::GetMainViewport()->GetCenter();
105 ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
106 ImGui::SetNextWindowSize(
107 ImVec2(950, 650),
108 ImGuiCond_Appearing); // Slightly larger for better layout
109
110 if (ImGui::Begin("Editor Selection", window_open,
111 ImGuiWindowFlags_NoCollapse)) {
113
114 ImGui::Separator();
115 ImGui::Spacing();
116
117 // Quick access buttons for recently used
118 if (!recent_editors_.empty()) {
120 ImGui::Separator();
121 ImGui::Spacing();
122 }
123
124 // Main editor grid
125 ImGui::Text(ICON_MD_APPS " All Editors");
126 ImGui::Spacing();
127
128 float button_size = 200.0f;
129 int columns =
130 static_cast<int>(ImGui::GetContentRegionAvail().x / button_size);
131 columns = std::max(columns, 1);
132
133 if (ImGui::BeginTable("##EditorGrid", columns, ImGuiTableFlags_None)) {
134 for (size_t i = 0; i < editors_.size(); ++i) {
135 ImGui::TableNextColumn();
136
137 EditorType prev_selection = selected_editor_;
138 DrawEditorPanel(editors_[i], static_cast<int>(i));
139
140 // Check if an editor was just selected
141 if (selected_editor_ != prev_selection) {
142 editor_selected = true;
146 }
147 // Auto-dismiss after selection
148 is_open_ = false;
149 if (p_open) {
150 *p_open = false;
151 }
152 }
153 }
154 ImGui::EndTable();
155 }
156 }
157 ImGui::End();
158
159 // Sync state back
160 if (p_open && !(*p_open)) {
161 is_open_ = false;
162 }
163
164 // DO NOT auto-dismiss here. Let the callback/EditorManager handle it.
165 // This allows the dialog to be used as a persistent switcher if desired.
166
167 return editor_selected;
168}
169
171 ImDrawList* draw_list = ImGui::GetWindowDrawList();
172 ImVec2 header_start = ImGui::GetCursorScreenPos();
173
174 ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[2]); // Large font
175
176 // Colorful gradient title
177 ImVec4 title_color = ImVec4(1.0f, 0.843f, 0.0f, 1.0f); // Triforce gold
178 ImGui::TextColored(title_color, ICON_MD_EDIT " Select an Editor");
179
180 ImGui::PopFont();
181
182 // Subtitle with gradient separator
183 ImVec2 subtitle_pos = ImGui::GetCursorScreenPos();
184 ImGui::TextColored(ImVec4(0.8f, 0.8f, 0.8f, 1.0f),
185 "Choose an editor to begin working on your ROM. "
186 "You can open multiple editors simultaneously.");
187}
188
190 ImGui::TextColored(ImVec4(1.0f, 0.843f, 0.0f, 1.0f),
191 ICON_MD_HISTORY " Recently Used");
192 ImGui::Spacing();
193
194 for (EditorType type : recent_editors_) {
195 // Find editor info
196 auto it = std::find_if(
197 editors_.begin(), editors_.end(),
198 [type](const EditorInfo& info) { return info.type == type; });
199
200 if (it != editors_.end()) {
201 // Use editor's theme color for button
202 ImVec4 color = it->color;
203 ImGui::PushStyleColor(
204 ImGuiCol_Button,
205 ImVec4(color.x * 0.5f, color.y * 0.5f, color.z * 0.5f, 0.7f));
206 ImGui::PushStyleColor(
207 ImGuiCol_ButtonHovered,
208 ImVec4(color.x * 0.7f, color.y * 0.7f, color.z * 0.7f, 0.9f));
209 ImGui::PushStyleColor(ImGuiCol_ButtonActive, color);
210
211 if (ImGui::Button(absl::StrCat(it->icon, " ", it->name).c_str(),
212 ImVec2(150, 35))) {
213 selected_editor_ = type;
214 }
215
216 ImGui::PopStyleColor(3);
217
218 if (ImGui::IsItemHovered()) {
219 ImGui::SetTooltip("%s", it->description);
220 }
221
222 ImGui::SameLine();
223 }
224 }
225
226 ImGui::NewLine();
227}
228
230 ImGui::PushID(index);
231
232 ImVec2 button_size(180, 120);
233 ImVec2 cursor_pos = ImGui::GetCursorScreenPos();
234 ImDrawList* draw_list = ImGui::GetWindowDrawList();
235
236 // Panel styling with gradients
237 bool is_recent = std::find(recent_editors_.begin(), recent_editors_.end(),
238 info.type) != recent_editors_.end();
239
240 // Create gradient background
241 ImVec4 base_color = info.color;
242 ImU32 color_top = ImGui::GetColorU32(ImVec4(
243 base_color.x * 0.4f, base_color.y * 0.4f, base_color.z * 0.4f, 0.8f));
244 ImU32 color_bottom = ImGui::GetColorU32(ImVec4(
245 base_color.x * 0.2f, base_color.y * 0.2f, base_color.z * 0.2f, 0.9f));
246
247 // Draw gradient card background
248 draw_list->AddRectFilledMultiColor(
249 cursor_pos,
250 ImVec2(cursor_pos.x + button_size.x, cursor_pos.y + button_size.y),
251 color_top, color_top, color_bottom, color_bottom);
252
253 // Colored border
254 ImU32 border_color =
255 is_recent
256 ? ImGui::GetColorU32(
257 ImVec4(base_color.x, base_color.y, base_color.z, 1.0f))
258 : ImGui::GetColorU32(ImVec4(base_color.x * 0.6f, base_color.y * 0.6f,
259 base_color.z * 0.6f, 0.7f));
260 draw_list->AddRect(
261 cursor_pos,
262 ImVec2(cursor_pos.x + button_size.x, cursor_pos.y + button_size.y),
263 border_color, 4.0f, 0, is_recent ? 3.0f : 2.0f);
264
265 // Recent indicator badge
266 if (is_recent) {
267 ImVec2 badge_pos(cursor_pos.x + button_size.x - 25, cursor_pos.y + 5);
268 draw_list->AddCircleFilled(badge_pos, 12, ImGui::GetColorU32(base_color),
269 16);
270 ImGui::SetCursorScreenPos(ImVec2(badge_pos.x - 6, badge_pos.y - 8));
271 ImGui::TextColored(ImVec4(1, 1, 1, 1), ICON_MD_STAR);
272 }
273
274 // Make button transparent (we draw our own background)
275 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
276 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
277 ImVec4(base_color.x * 0.3f, base_color.y * 0.3f,
278 base_color.z * 0.3f, 0.5f));
279 ImGui::PushStyleColor(ImGuiCol_ButtonActive,
280 ImVec4(base_color.x * 0.5f, base_color.y * 0.5f,
281 base_color.z * 0.5f, 0.7f));
282
283 ImGui::SetCursorScreenPos(cursor_pos);
284 bool clicked =
285 ImGui::Button(absl::StrCat("##", info.name).c_str(), button_size);
286 bool is_hovered = ImGui::IsItemHovered();
287
288 ImGui::PopStyleColor(3);
289
290 // Draw icon with colored background circle
291 ImVec2 icon_center(cursor_pos.x + button_size.x / 2, cursor_pos.y + 30);
292 ImU32 icon_bg = ImGui::GetColorU32(base_color);
293 draw_list->AddCircleFilled(icon_center, 22, icon_bg, 32);
294
295 // Draw icon
296 ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[2]); // Larger font for icon
297 ImVec2 icon_size = ImGui::CalcTextSize(info.icon);
298 ImGui::SetCursorScreenPos(
299 ImVec2(icon_center.x - icon_size.x / 2, icon_center.y - icon_size.y / 2));
300 ImGui::TextColored(ImVec4(1, 1, 1, 1), "%s", info.icon);
301 ImGui::PopFont();
302
303 // Draw name
304 ImGui::SetCursorScreenPos(ImVec2(cursor_pos.x + 10, cursor_pos.y + 65));
305 ImGui::PushTextWrapPos(cursor_pos.x + button_size.x - 10);
306 ImVec2 name_size = ImGui::CalcTextSize(info.name);
307 ImGui::SetCursorScreenPos(ImVec2(
308 cursor_pos.x + (button_size.x - name_size.x) / 2, cursor_pos.y + 65));
309 ImGui::TextColored(base_color, "%s", info.name);
310 ImGui::PopTextWrapPos();
311
312 // Draw shortcut hint if available
313 if (!info.shortcut.empty()) {
314 ImGui::SetCursorScreenPos(
315 ImVec2(cursor_pos.x + 10, cursor_pos.y + button_size.y - 20));
316 ImGui::TextColored(ImVec4(0.6f, 0.6f, 0.6f, 1.0f), "%s", info.shortcut.c_str());
317 }
318
319 // Hover glow effect
320 if (is_hovered) {
321 ImU32 glow_color = ImGui::GetColorU32(
322 ImVec4(base_color.x, base_color.y, base_color.z, 0.2f));
323 draw_list->AddRectFilled(
324 cursor_pos,
325 ImVec2(cursor_pos.x + button_size.x, cursor_pos.y + button_size.y),
326 glow_color, 4.0f);
327 }
328
329 // Enhanced tooltip with fixed sizing
330 if (is_hovered) {
331 // Force tooltip to have a fixed max width to prevent flickering
332 ImGui::SetNextWindowSize(ImVec2(300, 0), ImGuiCond_Always);
333 ImGui::BeginTooltip();
334 ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[1]); // Medium font
335 ImGui::TextColored(base_color, "%s %s", info.icon, info.name);
336 ImGui::PopFont();
337 ImGui::Separator();
338 ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + 280);
339 ImGui::TextWrapped("%s", info.description);
340 ImGui::PopTextWrapPos();
341 if (!info.shortcut.empty()) {
342 ImGui::Spacing();
343 ImGui::TextColored(base_color, ICON_MD_KEYBOARD " %s", info.shortcut.c_str());
344 }
345 if (is_recent) {
346 ImGui::Spacing();
347 ImGui::TextColored(ImVec4(1.0f, 0.843f, 0.0f, 1.0f),
348 ICON_MD_STAR " Recently used");
349 }
350 ImGui::EndTooltip();
351 }
352
353 if (clicked) {
354 selected_editor_ = info.type;
355 }
356
357 ImGui::PopID();
358}
359
361 // Remove if already in list
362 auto it = std::find(recent_editors_.begin(), recent_editors_.end(), type);
363 if (it != recent_editors_.end()) {
364 recent_editors_.erase(it);
365 }
366
367 // Add to front
368 recent_editors_.insert(recent_editors_.begin(), type);
369
370 // Limit size
371 if (recent_editors_.size() > kMaxRecentEditors) {
373 }
374
376}
377
379 try {
380 auto data = util::LoadFileFromConfigDir("recent_editors.txt");
381 if (!data.empty()) {
382 std::istringstream ss(data);
383 std::string line;
384 while (std::getline(ss, line) &&
386 int type_int = std::stoi(line);
387 if (type_int >= 0 &&
388 type_int < static_cast<int>(EditorType::kSettings)) {
389 recent_editors_.push_back(static_cast<EditorType>(type_int));
390 }
391 }
392 }
393 } catch (...) {
394 // Ignore errors, just start with empty recent list
395 }
396}
397
399 try {
400 std::ostringstream ss;
401 for (EditorType type : recent_editors_) {
402 ss << static_cast<int>(type) << "\n";
403 }
404 util::SaveFile("recent_editors.txt", ss.str());
405 } catch (...) {
406 // Ignore save errors
407 }
408}
409
410} // namespace editor
411} // namespace yaze
void SaveRecentEditors()
Save recently used editors to settings.
void LoadRecentEditors()
Load recently used editors from settings.
std::function< void(EditorType)> selection_callback_
void DrawEditorPanel(const EditorInfo &info, int index)
void MarkRecentlyUsed(EditorType type)
Mark an editor as recently used.
bool Show(bool *p_open=nullptr)
Show the dialog.
#define ICON_MD_APPS
Definition icons.h:168
#define ICON_MD_EMOJI_EMOTIONS
Definition icons.h:672
#define ICON_MD_DATA_ARRAY
Definition icons.h:519
#define ICON_MD_STAR
Definition icons.h:1848
#define ICON_MD_MAP
Definition icons.h:1173
#define ICON_MD_CODE
Definition icons.h:434
#define ICON_MD_VIDEOGAME_ASSET
Definition icons.h:2076
#define ICON_MD_CHAT_BUBBLE
Definition icons.h:395
#define ICON_MD_EDIT
Definition icons.h:645
#define ICON_MD_CASTLE
Definition icons.h:380
#define ICON_MD_MUSIC_NOTE
Definition icons.h:1264
#define ICON_MD_KEYBOARD
Definition icons.h:1028
#define ICON_MD_PALETTE
Definition icons.h:1370
#define ICON_MD_TV
Definition icons.h:2032
#define ICON_MD_COLOR_LENS
Definition icons.h:440
#define ICON_MD_SMART_TOY
Definition icons.h:1781
#define ICON_MD_HISTORY
Definition icons.h:946
const char * GetCtrlDisplayName()
Get the display name for the primary modifier key.
void SaveFile(const std::string &filename, const std::string &contents)
Definition file_util.cc:56
std::string LoadFileFromConfigDir(const std::string &filename)
Loads a file from the user's config directory.
Definition file_util.cc:38
Metadata about an available editor.