yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
popup_manager.cc
Go to the documentation of this file.
1#include "popup_manager.h"
2
3#include <cstring>
4#include <ctime>
5#include <filesystem>
6#include <functional>
7#include <initializer_list>
8
9#include "absl/status/status.h"
10#include "absl/strings/match.h"
11#include "absl/strings/str_format.h"
15#include "app/gui/core/icons.h"
16#include "app/gui/core/input.h"
17#include "app/gui/core/style.h"
21#include "imgui/misc/cpp/imgui_stdlib.h"
22#include "util/file_util.h"
23#include "util/hex.h"
24#include "yaze.h"
25
26namespace yaze {
27namespace editor {
28
29using namespace ImGui;
30
32 : editor_manager_(editor_manager), status_(absl::OkStatus()) {}
33
35 // ============================================================================
36 // POPUP REGISTRATION
37 // ============================================================================
38 // All popups must be registered here BEFORE any menu callbacks can trigger
39 // them. This method is called in EditorManager constructor BEFORE
40 // MenuOrchestrator and UICoordinator are created, ensuring safe
41 // initialization order.
42 //
43 // Popup Registration Format:
44 // popups_[PopupID::kConstant] = {
45 // .name = PopupID::kConstant,
46 // .type = PopupType::kXxx,
47 // .is_visible = false,
48 // .allow_resize = false/true,
49 // .draw_function = [this]() { DrawXxxPopup(); }
50 // };
51 // ============================================================================
52
53 // File Operations
55 false, false, [this]() { DrawSaveAsPopup(); }};
57 false, true,
58 [this]() { DrawSaveScopePopup(); }};
60 PopupType::kFileOperation, false, false,
61 [this]() { DrawNewProjectPopup(); }};
63 PopupType::kFileOperation, false, false,
64 [this]() { DrawManageProjectPopup(); }};
66 PopupType::kFileOperation, false, true,
67 [this]() { DrawRomBackupManagerPopup(); }};
68
69 // Information
71 [this]() { DrawAboutPopup(); }};
73 false, [this]() { DrawRomInfoPopup(); }};
76 [this]() { DrawSupportedFeaturesPopup(); }};
78 false, false,
79 [this]() { DrawOpenRomHelpPopup(); }};
80
81 // Help Documentation
83 PopupType::kHelp, false, false,
84 [this]() { DrawGettingStartedPopup(); }};
87 [this]() { DrawAsarIntegrationPopup(); }};
90 [this]() { DrawBuildInstructionsPopup(); }};
92 false, [this]() { DrawCLIUsagePopup(); }};
95 [this]() { DrawTroubleshootingPopup(); }};
97 false, false,
98 [this]() { DrawContributingPopup(); }};
100 false, [this]() { DrawWhatsNewPopup(); }};
101
102 // Settings
105 true, // Resizable
106 [this]() { DrawDisplaySettingsPopup(); }};
108 PopupID::kFeatureFlags, PopupType::kSettings, false, true, // Resizable
109 [this]() { DrawFeatureFlagsPopup(); }};
110
111 // Workspace
113 false, false,
114 [this]() { DrawWorkspaceHelpPopup(); }};
117 [this]() { DrawSessionLimitWarningPopup(); }};
120 [this]() { DrawLayoutResetConfirmPopup(); }};
121
123 PopupType::kSettings, false, false,
124 [this]() { DrawLayoutPresetsPopup(); }};
125
127 PopupType::kSettings, false, true,
128 [this]() { DrawSessionManagerPopup(); }};
129
130 // Debug/Testing
132 false, true, // Resizable
133 [this]() { DrawDataIntegrityPopup(); }};
134
137 false, [this]() { DrawDungeonPotItemSaveConfirmPopup(); }};
140 [this]() { DrawRomWriteConfirmPopup(); }};
143 [this]() { DrawWriteConflictWarningPopup(); }};
144}
145
147 // Draw status popup if needed
149
150 // Draw all registered popups
151 for (auto& [name, params] : popups_) {
152 if (params.is_visible) {
153 OpenPopup(name.c_str());
154
155 // Use allow_resize flag from popup definition
156 ImGuiWindowFlags popup_flags = params.allow_resize
157 ? ImGuiWindowFlags_None
158 : ImGuiWindowFlags_AlwaysAutoResize;
159
160 if (BeginPopupModal(name.c_str(), nullptr, popup_flags)) {
161 params.draw_function();
162 EndPopup();
163 }
164 }
165 }
166}
167
168void PopupManager::Show(const char* name) {
169 if (!name) {
170 return; // Safety check for null pointer
171 }
172
173 std::string name_str(name);
174 auto it = popups_.find(name_str);
175 if (it != popups_.end()) {
176 it->second.is_visible = true;
177 } else {
178 // Log warning for unregistered popup
179 printf(
180 "[PopupManager] Warning: Popup '%s' not registered. Available popups: ",
181 name);
182 for (const auto& [key, _] : popups_) {
183 printf("'%s' ", key.c_str());
184 }
185 printf("\n");
186 }
187}
188
189void PopupManager::Hide(const char* name) {
190 if (!name) {
191 return; // Safety check for null pointer
192 }
193
194 std::string name_str(name);
195 auto it = popups_.find(name_str);
196 if (it != popups_.end()) {
197 it->second.is_visible = false;
198 CloseCurrentPopup();
199 }
200}
201
202bool PopupManager::IsVisible(const char* name) const {
203 if (!name) {
204 return false; // Safety check for null pointer
205 }
206
207 std::string name_str(name);
208 auto it = popups_.find(name_str);
209 if (it != popups_.end()) {
210 return it->second.is_visible;
211 }
212 return false;
213}
214
215void PopupManager::SetStatus(const absl::Status& status) {
216 if (!status.ok()) {
217 show_status_ = true;
218 prev_status_ = status;
219 status_ = status;
220 }
221}
222
223bool PopupManager::BeginCentered(const char* name) {
224 ImGuiIO const& io = GetIO();
225 ImVec2 pos(io.DisplaySize.x * 0.5f, io.DisplaySize.y * 0.5f);
226 SetNextWindowPos(pos, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
227 ImGuiWindowFlags flags =
228 ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration |
229 ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings;
230 return Begin(name, nullptr, flags);
231}
232
234 if (show_status_ && BeginCentered("StatusWindow")) {
235 Text("%s", ICON_MD_ERROR);
236 Text("%s", prev_status_.ToString().c_str());
237 Spacing();
238 NextColumn();
239 Columns(1);
240 Separator();
241 NewLine();
242 SameLine(128);
243 if (Button("OK", ::yaze::gui::kDefaultModalSize) ||
244 IsKeyPressed(ImGuiKey_Space)) {
245 show_status_ = false;
246 status_ = absl::OkStatus();
247 }
248 SameLine();
249 if (Button(ICON_MD_CONTENT_COPY, ImVec2(50, 0))) {
250 SetClipboardText(prev_status_.ToString().c_str());
251 }
252 End();
253 }
254}
255
257 Text("Yet Another Zelda3 Editor - v%s", editor_manager_->version().c_str());
258 Text("Written by: scawful");
259 Spacing();
260 Text("Special Thanks: Zarby89, JaredBrian");
261 Separator();
262
263 if (Button("Close", ::yaze::gui::kDefaultModalSize)) {
264 Hide("About");
265 }
266}
267
269 auto* current_rom = editor_manager_->GetCurrentRom();
270 if (!current_rom)
271 return;
272
273 Text("Title: %s", current_rom->title().c_str());
274 Text("ROM Size: %s", util::HexLongLong(current_rom->size()).c_str());
275 Text("ROM Hash: %s", editor_manager_->GetCurrentRomHash().empty()
276 ? "(unknown)"
278
279 auto* project = editor_manager_->GetCurrentProject();
280 if (project && project->project_opened()) {
281 Separator();
282 Text("Role: %s",
283 project::RomRoleToString(project->rom_metadata.role).c_str());
284 Text("Write Policy: %s",
285 project::RomWritePolicyToString(project->rom_metadata.write_policy)
286 .c_str());
287 Text("Expected Hash: %s",
288 project->rom_metadata.expected_hash.empty()
289 ? "(unset)"
290 : project->rom_metadata.expected_hash.c_str());
292 const auto& theme = gui::ThemeManager::Get().GetCurrentTheme();
293 TextColored(gui::ConvertColorToImVec4(theme.warning),
294 "ROM hash mismatch detected");
295 }
296 }
297
298 if (Button("Close", ::yaze::gui::kDefaultModalSize) ||
299 IsKeyPressed(ImGuiKey_Escape)) {
300 Hide("ROM Information");
301 }
302}
303
305 using namespace ImGui;
306
307 Text("%s Save ROM to new location", ICON_MD_SAVE_AS);
308 Separator();
309
310 static std::string save_as_filename = "";
311 if (editor_manager_->GetCurrentRom() && save_as_filename.empty()) {
312 save_as_filename = editor_manager_->GetCurrentRom()->title();
313 }
314
315 InputText("Filename", &save_as_filename);
316 Separator();
317
318 if (Button(absl::StrFormat("%s Browse...", ICON_MD_FOLDER_OPEN).c_str(),
320 auto file_path =
321 util::FileDialogWrapper::ShowSaveFileDialog(save_as_filename, "sfc");
322 if (!file_path.empty()) {
323 save_as_filename = file_path;
324 }
325 }
326
327 SameLine();
328 if (Button(absl::StrFormat("%s Save", ICON_MD_SAVE).c_str(),
330 if (!save_as_filename.empty()) {
331 // Ensure proper file extension
332 std::string final_filename = save_as_filename;
333 if (final_filename.find(".sfc") == std::string::npos &&
334 final_filename.find(".smc") == std::string::npos) {
335 final_filename += ".sfc";
336 }
337
338 auto status = editor_manager_->SaveRomAs(final_filename);
339 if (status.ok()) {
340 save_as_filename = "";
342 }
343 }
344 }
345
346 SameLine();
347 if (Button(absl::StrFormat("%s Cancel", ICON_MD_CANCEL).c_str(),
349 save_as_filename = "";
351 }
352}
353
355 using namespace ImGui;
356
357 Text("%s Save Scope", ICON_MD_SAVE);
358 Separator();
359 TextWrapped(
360 "Controls which data is written during File > Save ROM. "
361 "Changes apply immediately.");
362 Separator();
363
364 if (CollapsingHeader("Overworld", ImGuiTreeNodeFlags_DefaultOpen)) {
365 Checkbox("Save Overworld Maps",
366 &core::FeatureFlags::get().overworld.kSaveOverworldMaps);
367 Checkbox("Save Overworld Entrances",
368 &core::FeatureFlags::get().overworld.kSaveOverworldEntrances);
369 Checkbox("Save Overworld Exits",
370 &core::FeatureFlags::get().overworld.kSaveOverworldExits);
371 Checkbox("Save Overworld Items",
372 &core::FeatureFlags::get().overworld.kSaveOverworldItems);
373 Checkbox("Save Overworld Properties",
374 &core::FeatureFlags::get().overworld.kSaveOverworldProperties);
375 }
376
377 if (CollapsingHeader("Dungeon", ImGuiTreeNodeFlags_DefaultOpen)) {
378 Checkbox("Save Dungeon Maps", &core::FeatureFlags::get().kSaveDungeonMaps);
379 Checkbox("Save Objects", &core::FeatureFlags::get().dungeon.kSaveObjects);
380 Checkbox("Save Sprites", &core::FeatureFlags::get().dungeon.kSaveSprites);
381 Checkbox("Save Room Headers",
382 &core::FeatureFlags::get().dungeon.kSaveRoomHeaders);
383 Checkbox("Save Torches", &core::FeatureFlags::get().dungeon.kSaveTorches);
384 Checkbox("Save Pits", &core::FeatureFlags::get().dungeon.kSavePits);
385 Checkbox("Save Blocks", &core::FeatureFlags::get().dungeon.kSaveBlocks);
386 Checkbox("Save Collision",
387 &core::FeatureFlags::get().dungeon.kSaveCollision);
388 Checkbox("Save Chests", &core::FeatureFlags::get().dungeon.kSaveChests);
389 Checkbox("Save Pot Items",
390 &core::FeatureFlags::get().dungeon.kSavePotItems);
391 Checkbox("Save Palettes", &core::FeatureFlags::get().dungeon.kSavePalettes);
392 }
393
394 if (CollapsingHeader("Graphics", ImGuiTreeNodeFlags_DefaultOpen)) {
395 Checkbox("Save Graphics Sheets",
396 &core::FeatureFlags::get().kSaveGraphicsSheet);
397 Checkbox("Save All Palettes", &core::FeatureFlags::get().kSaveAllPalettes);
398 Checkbox("Save Gfx Groups", &core::FeatureFlags::get().kSaveGfxGroups);
399 }
400
401 if (CollapsingHeader("Messages", ImGuiTreeNodeFlags_DefaultOpen)) {
402 Checkbox("Save Message Text", &core::FeatureFlags::get().kSaveMessages);
403 }
404
405 Separator();
406 if (Button("Close", ::yaze::gui::kDefaultModalSize)) {
408 }
409}
410
412 using namespace ImGui;
413
414 auto* rom = editor_manager_->GetCurrentRom();
415 if (!rom || !rom->is_loaded()) {
416 Text("No ROM loaded.");
417 if (Button("Close", ::yaze::gui::kDefaultModalSize)) {
419 }
420 return;
421 }
422
423 const auto* project = editor_manager_->GetCurrentProject();
424 std::string backup_dir;
425 if (project && project->project_opened() &&
426 !project->rom_backup_folder.empty()) {
427 backup_dir = project->GetAbsolutePath(project->rom_backup_folder);
428 } else {
429 backup_dir = std::filesystem::path(rom->filename()).parent_path().string();
430 }
431
432 Text("%s ROM Backups", ICON_MD_BACKUP);
433 Separator();
434 TextWrapped("Backup folder: %s", backup_dir.c_str());
435
436 if (Button(ICON_MD_DELETE_SWEEP " Prune Backups")) {
437 auto status = editor_manager_->PruneRomBackups();
438 if (!status.ok()) {
439 if (auto* toast = editor_manager_->toast_manager()) {
440 toast->Show(absl::StrFormat("Prune failed: %s", status.message()),
442 }
443 } else if (auto* toast = editor_manager_->toast_manager()) {
444 toast->Show("Backups pruned", ToastType::kSuccess);
445 }
446 }
447
448 Separator();
449 auto backups = editor_manager_->GetRomBackups();
450 if (backups.empty()) {
451 TextDisabled("No backups found.");
452 } else if (BeginTable("RomBackupTable", 4,
453 ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders |
454 ImGuiTableFlags_Resizable)) {
455 TableSetupColumn("Timestamp");
456 TableSetupColumn("Size");
457 TableSetupColumn("Filename");
458 TableSetupColumn("Actions");
459 TableHeadersRow();
460
461 auto format_size = [](uintmax_t bytes) {
462 if (bytes > (1024 * 1024)) {
463 return absl::StrFormat("%.2f MB",
464 static_cast<double>(bytes) / (1024 * 1024));
465 }
466 if (bytes > 1024) {
467 return absl::StrFormat("%.1f KB", static_cast<double>(bytes) / 1024.0);
468 }
469 return absl::StrFormat("%llu B", static_cast<unsigned long long>(bytes));
470 };
471
472 for (size_t i = 0; i < backups.size(); ++i) {
473 const auto& backup = backups[i];
474 TableNextRow();
475 TableNextColumn();
476 char time_buffer[32] = "unknown";
477 if (backup.timestamp != 0) {
478 std::tm local_tm{};
479#ifdef _WIN32
480 localtime_s(&local_tm, &backup.timestamp);
481#else
482 localtime_r(&backup.timestamp, &local_tm);
483#endif
484 std::strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%d %H:%M:%S",
485 &local_tm);
486 }
487 TextUnformatted(time_buffer);
488
489 TableNextColumn();
490 TextUnformatted(format_size(backup.size_bytes).c_str());
491
492 TableNextColumn();
493 TextUnformatted(backup.filename.c_str());
494
495 TableNextColumn();
496 PushID(static_cast<int>(i));
497 if (Button(ICON_MD_RESTORE " Restore")) {
498 auto status = editor_manager_->RestoreRomBackup(backup.path);
499 if (!status.ok()) {
500 if (auto* toast = editor_manager_->toast_manager()) {
501 toast->Show(absl::StrFormat("Restore failed: %s", status.message()),
503 }
504 } else if (auto* toast = editor_manager_->toast_manager()) {
505 toast->Show("ROM restored from backup", ToastType::kSuccess);
506 }
507 }
508 SameLine();
509 if (Button(ICON_MD_OPEN_IN_NEW " Open")) {
510 auto status = editor_manager_->OpenRomOrProject(backup.path);
511 if (!status.ok()) {
512 if (auto* toast = editor_manager_->toast_manager()) {
513 toast->Show(absl::StrFormat("Open failed: %s", status.message()),
515 }
516 }
517 }
518 SameLine();
519 if (Button(ICON_MD_CONTENT_COPY " Copy")) {
520 SetClipboardText(backup.path.c_str());
521 }
522 PopID();
523 }
524 EndTable();
525 }
526
527 Separator();
528 if (Button("Close", ::yaze::gui::kDefaultModalSize)) {
530 }
531}
532
534 using namespace ImGui;
535
536 static std::string project_name = "";
537 static std::string project_filepath = "";
538 static std::string rom_filename = "";
539 static std::string labels_filename = "";
540 static std::string code_folder = "";
541
542 InputText("Project Name", &project_name);
543
544 if (Button(absl::StrFormat("%s Destination Folder", ICON_MD_FOLDER).c_str(),
547 }
548 SameLine();
549 Text("%s", project_filepath.empty() ? "(Not set)" : project_filepath.c_str());
550
551 if (Button(absl::StrFormat("%s ROM File", ICON_MD_VIDEOGAME_ASSET).c_str(),
555 }
556 SameLine();
557 Text("%s", rom_filename.empty() ? "(Not set)" : rom_filename.c_str());
558
559 if (Button(absl::StrFormat("%s Labels File", ICON_MD_LABEL).c_str(),
562 }
563 SameLine();
564 Text("%s", labels_filename.empty() ? "(Not set)" : labels_filename.c_str());
565
566 if (Button(absl::StrFormat("%s Code Folder", ICON_MD_CODE).c_str(),
569 }
570 SameLine();
571 Text("%s", code_folder.empty() ? "(Not set)" : code_folder.c_str());
572
573 Separator();
574
575 if (Button(absl::StrFormat("%s Choose Project File Location", ICON_MD_SAVE)
576 .c_str(),
578 auto project_file_path =
580 if (!project_file_path.empty()) {
581 if (!(absl::EndsWith(project_file_path, ".yaze") ||
582 absl::EndsWith(project_file_path, ".yazeproj"))) {
583 project_file_path += ".yaze";
584 }
585 project_filepath = project_file_path;
586 }
587 }
588
589 if (Button(absl::StrFormat("%s Create Project", ICON_MD_ADD).c_str(),
591 if (!project_filepath.empty() && !project_name.empty()) {
592 auto status = editor_manager_->CreateNewProject();
593 if (status.ok()) {
594 // Clear fields
595 project_name = "";
596 project_filepath = "";
597 rom_filename = "";
598 labels_filename = "";
599 code_folder = "";
601 }
602 }
603 }
604 SameLine();
605 if (Button(absl::StrFormat("%s Cancel", ICON_MD_CANCEL).c_str(),
607 // Clear fields
608 project_name = "";
609 project_filepath = "";
610 rom_filename = "";
611 labels_filename = "";
612 code_folder = "";
614 }
615}
616
618 const auto& theme = gui::ThemeManager::Get().GetCurrentTheme();
619 const ImVec4 status_ok = gui::ConvertColorToImVec4(theme.success);
620 const ImVec4 status_warn = gui::ConvertColorToImVec4(theme.warning);
621 const ImVec4 status_info = gui::ConvertColorToImVec4(theme.info);
622 const ImVec4 status_error = gui::ConvertColorToImVec4(theme.error);
623
624 auto status_color = [&](const char* status) -> ImVec4 {
625 if (strcmp(status, "Stable") == 0 || strcmp(status, "Working") == 0) {
626 return status_ok;
627 }
628 if (strcmp(status, "Beta") == 0 || strcmp(status, "Experimental") == 0) {
629 return status_warn;
630 }
631 if (strcmp(status, "Preview") == 0) {
632 return status_info;
633 }
634 if (strcmp(status, "Not available") == 0) {
635 return status_error;
636 }
637 return status_info;
638 };
639
640 struct FeatureRow {
641 const char* feature;
642 const char* status;
643 const char* persistence;
644 const char* notes;
645 };
646
647 auto draw_table = [&](const char* table_id,
648 std::initializer_list<FeatureRow> rows) {
649 ImGuiTableFlags flags = ImGuiTableFlags_BordersInnerH |
650 ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable;
651 if (!BeginTable(table_id, 4, flags)) {
652 return;
653 }
654 TableSetupColumn("Feature", ImGuiTableColumnFlags_WidthStretch);
655 TableSetupColumn("Status", ImGuiTableColumnFlags_WidthFixed, 120.0f);
656 TableSetupColumn("Save/Load", ImGuiTableColumnFlags_WidthFixed, 180.0f);
657 TableSetupColumn("Notes", ImGuiTableColumnFlags_WidthStretch);
658 TableHeadersRow();
659
660 for (const auto& row : rows) {
661 TableNextRow();
662 TableSetColumnIndex(0);
663 TextUnformatted(row.feature);
664 TableSetColumnIndex(1);
665 TextColored(status_color(row.status), "%s", row.status);
666 TableSetColumnIndex(2);
667 TextUnformatted(row.persistence);
668 TableSetColumnIndex(3);
669 TextWrapped("%s", row.notes);
670 }
671
672 EndTable();
673 };
674
675 TextDisabled(
676 "Status: Stable = production ready, Beta = usable with gaps, "
677 "Experimental = WIP, Preview = web parity in progress.");
678 TextDisabled("See Settings > Feature Flags for ROM-specific toggles.");
679 Spacing();
680
681 if (CollapsingHeader("Desktop App (yaze)", ImGuiTreeNodeFlags_DefaultOpen)) {
682 draw_table("desktop_features",
683 {
684 {"ROM load/save", "Stable", "ROM + backups",
685 "Backups on save when enabled."},
686 {"Overworld Editor", "Stable", "ROM",
687 "Maps/entrances/exits/items; version-gated."},
688 {"Dungeon Editor", "Stable", "ROM",
689 "Room objects/tiles/palettes persist."},
690 {"Palette Editor", "Stable", "ROM",
691 "Palette edits persist; JSON IO pending."},
692 {"Graphics Editor", "Beta", "ROM",
693 "Sheet edits persist; tooling still expanding."},
694 {"Sprite Editor", "Stable", "ROM", "Sprite edits persist."},
695 {"Message Editor", "Stable", "ROM", "Text edits persist."},
696 {"Screen Editor", "Experimental", "ROM (partial)",
697 "Save coverage incomplete."},
698 {"Hex Editor", "Beta", "ROM", "Search UX incomplete."},
699 {"Assembly/Asar", "Beta", "ROM + project",
700 "Patch apply + symbol export."},
701 {"Emulator", "Beta", "Runtime only",
702 "Save-state UI partially wired."},
703 {"Music Editor", "Experimental", "ROM (partial)",
704 "Serialization in progress."},
705 {"Agent UI", "Experimental", ".yaze/agent",
706 "Requires AI provider configuration."},
707 {"Settings/Layouts", "Beta", ".yaze config",
708 "Layout serialization improving."},
709 });
710 }
711
712 if (CollapsingHeader("z3ed CLI")) {
713 draw_table("cli_features",
714 {
715 {"ROM read/write/validate", "Stable", "ROM file",
716 "Direct command execution."},
717 {"Agent workflows", "Stable", ".yaze/proposals + sandboxes",
718 "Commit writes ROM; revert reloads."},
719 {"Snapshots/restore", "Stable", "Sandbox copies",
720 "Supports YAZE_SANDBOX_ROOT override."},
721 {"Doctor/test suites", "Stable", "Reports",
722 "Structured output for automation."},
723 {"TUI/REPL", "Stable", "Session history",
724 "Interactive command palette + logs."},
725 });
726 }
727
728 if (CollapsingHeader("Web/WASM Preview")) {
729 draw_table(
730 "web_features",
731 {
732 {"ROM load/save", "Preview", "IndexedDB + download",
733 "Drag/drop or picker; download for backups."},
734 {"Editors (OW/Dungeon/Palette/etc.)", "Preview",
735 "IndexedDB + download", "Parity work in progress."},
736 {"Hex Editor", "Working", "IndexedDB + download",
737 "Direct ROM editing available."},
738 {"Asar patching", "Preview", "ROM", "Basic patch apply support."},
739 {"Emulator", "Not available", "N/A", "Desktop only."},
740 {"Collaboration", "Experimental", "Server",
741 "Requires yaze-server."},
742 {"AI features", "Preview", "Server", "Requires AI-enabled server."},
743 });
744 }
745
746 if (Button("Close", ::yaze::gui::kDefaultModalSize)) {
748 }
749}
750
752 Text("File -> Open");
753 Text("Select a ROM file to open");
754 Text("Supported ROMs (headered or unheadered):");
755 Text("The Legend of Zelda: A Link to the Past");
756 Text("US Version 1.0");
757 Text("JP Version 1.0");
758 Spacing();
759 TextWrapped("ROM files are not bundled. Use a clean, legally obtained copy.");
760
761 if (Button("Close", ::yaze::gui::kDefaultModalSize)) {
762 Hide("Open a ROM");
763 }
764}
765
767 Text("Project Menu");
768 Text("Create a new project or open an existing one.");
769 Text("Save the project to save the current state of the project.");
770 TextWrapped(
771 "To save a project, you need to first open a ROM and initialize your "
772 "code path and labels file. Label resource manager can be found in "
773 "the View menu. Code path is set in the Code editor after opening a "
774 "folder.");
775
776 if (Button("Close", ::yaze::gui::kDefaultModalSize)) {
777 Hide("Manage Project");
778 }
779}
780
782 TextWrapped("Welcome to YAZE v%s!", YAZE_VERSION_STRING);
783 TextWrapped(
784 "YAZE lets you modify 'The Legend of Zelda: A Link to the Past' (US or "
785 "JP) ROMs with modern tooling.");
786 Spacing();
787 TextWrapped("Release Highlights:");
788 BulletText(
789 "AI-assisted workflows via z3ed agent and in-app panels "
790 "(Ollama/Gemini/OpenAI/Anthropic)");
791 BulletText("Clear feature status panels and improved help/tooltips");
792 BulletText("Unified .yaze storage across desktop/CLI/web");
793 Spacing();
794 TextWrapped("General Tips:");
795 BulletText("Open a clean ROM and save a backup before editing");
796 BulletText("Use Help (F1) for context-aware guidance and shortcuts");
797 BulletText(
798 "Configure AI providers (Ollama/Gemini/OpenAI/Anthropic) in Settings > "
799 "Agent");
800
801 if (Button("Close", ::yaze::gui::kDefaultModalSize)) {
802 Hide("Getting Started");
803 }
804}
805
807 TextWrapped("Asar 65816 Assembly Integration");
808 TextWrapped("YAZE includes full Asar assembler support for ROM patching.");
809 Spacing();
810 TextWrapped("Features:");
811 BulletText("Cross-platform ROM patching with assembly code");
812 BulletText("Symbol export with addresses and opcodes");
813 BulletText("Assembly validation with detailed error reporting");
814 BulletText("Memory-safe patch application with size checks");
815
816 if (Button("Close", ::yaze::gui::kDefaultModalSize)) {
817 Hide("Asar Integration");
818 }
819}
820
822 TextWrapped("Build Instructions");
823 TextWrapped("YAZE uses modern CMake for cross-platform builds.");
824 Spacing();
825 TextWrapped("Quick Start (examples):");
826 BulletText("cmake --preset mac-dbg | lin-dbg | win-dbg");
827 BulletText("cmake --build --preset <preset> --target yaze");
828 Spacing();
829 TextWrapped("AI Builds:");
830 BulletText("cmake --preset mac-ai | lin-ai | win-ai");
831 BulletText("cmake --build --preset <preset> --target yaze z3ed");
832 Spacing();
833 TextWrapped("Docs: docs/public/build/quick-reference.md");
834
835 if (Button("Close", ::yaze::gui::kDefaultModalSize)) {
836 Hide("Build Instructions");
837 }
838}
839
841 TextWrapped("Command Line Interface (z3ed)");
842 TextWrapped("Scriptable ROM editing and AI agent workflows.");
843 Spacing();
844 TextWrapped("Commands:");
845 BulletText("z3ed rom-info --rom=zelda3.sfc");
846 BulletText("z3ed agent simple-chat --rom=zelda3.sfc --ai_provider=auto");
847 BulletText("z3ed agent plan --rom=zelda3.sfc");
848 BulletText("z3ed test-list --format json");
849 BulletText("z3ed patch apply-asar patch.asm --rom=zelda3.sfc");
850 BulletText("z3ed help dungeon-place-sprite");
851 Spacing();
852 TextWrapped("Storage:");
853 BulletText("Agent plans/proposals live under ~/.yaze (see docs for details)");
854
855 if (Button("Close", ::yaze::gui::kDefaultModalSize)) {
856 Hide("CLI Usage");
857 }
858}
859
861 TextWrapped("Troubleshooting");
862 TextWrapped("Common issues and solutions:");
863 Spacing();
864 BulletText("ROM won't load: Check file format (SFC/SMC supported)");
865 BulletText(
866 "AI agent missing: Start Ollama or set GEMINI_API_KEY/OPENAI_API_KEY/"
867 "ANTHROPIC_API_KEY (web uses AI_AGENT_ENDPOINT)");
868 BulletText("Graphics issues: Disable experimental flags in Settings");
869 BulletText("Performance: Enable hardware acceleration in display settings");
870 BulletText("Crashes: Check ROM file integrity and available memory");
871 BulletText("Layout issues: Reset workspace layouts from View > Layouts");
872
873 if (Button("Close", ::yaze::gui::kDefaultModalSize)) {
874 Hide("Troubleshooting");
875 }
876}
877
879 TextWrapped("Contributing to YAZE");
880 TextWrapped("YAZE is open source and welcomes contributions!");
881 Spacing();
882 TextWrapped("How to contribute:");
883 BulletText("Fork the repository on GitHub");
884 BulletText("Create feature branches for new work");
885 BulletText("Follow C++ coding standards");
886 BulletText("Include tests for new features");
887 BulletText("Submit pull requests for review");
888
889 if (Button("Close", ::yaze::gui::kDefaultModalSize)) {
890 Hide("Contributing");
891 }
892}
893
895 TextWrapped("What's New in YAZE v%s", YAZE_VERSION_STRING);
896 Spacing();
897
898 if (CollapsingHeader(
899 absl::StrFormat("%s User Interface & Theming", ICON_MD_PALETTE)
900 .c_str(),
901 ImGuiTreeNodeFlags_DefaultOpen)) {
902 BulletText("Feature status/persistence summaries across desktop/CLI/web");
903 BulletText("Shortcut/help panels now match configured keybindings");
904 BulletText("Refined onboarding tips and error messaging");
905 BulletText("Help text refreshed across desktop, CLI, and web");
906 }
907
908 if (CollapsingHeader(
909 absl::StrFormat("%s Development & Build System", ICON_MD_BUILD)
910 .c_str(),
911 ImGuiTreeNodeFlags_DefaultOpen)) {
912 BulletText("Asar 65816 assembler integration for ROM patching");
913 BulletText("z3ed CLI + TUI for scripting, test/doctor, and automation");
914 BulletText("Modern CMake presets for desktop, AI, and web builds");
915 BulletText("Unified version + storage references for 0.5.1");
916 }
917
918 if (CollapsingHeader(
919 absl::StrFormat("%s Core Improvements", ICON_MD_SETTINGS).c_str())) {
920 BulletText("Improved project metadata + .yaze storage alignment");
921 BulletText("Stronger error reporting and status feedback");
922 BulletText("Performance and stability improvements across editors");
923 BulletText("Expanded logging and diagnostics tooling");
924 }
925
926 if (CollapsingHeader(
927 absl::StrFormat("%s Editor Features", ICON_MD_EDIT).c_str())) {
928 BulletText("Music editor updates with SPC parsing/playback");
929 BulletText("AI agent-assisted editing workflows (multi-provider + vision)");
930 BulletText("Expanded overworld/dungeon tooling and palette accuracy");
931 BulletText("Web/WASM preview with collaboration hooks");
932 }
933
934 Spacing();
935 if (Button(
936 absl::StrFormat("%s View Release Notes", ICON_MD_DESCRIPTION).c_str(),
937 ImVec2(-1, 30))) {
938 // Close this popup and show theme settings
940 // Could trigger release notes panel opening here
941 }
942
943 if (Button("Close", ::yaze::gui::kDefaultModalSize)) {
945 }
946}
947
949 TextWrapped("Workspace Management");
950 TextWrapped(
951 "YAZE supports multiple ROM sessions and flexible workspace layouts.");
952 Spacing();
953
954 TextWrapped("Session Management:");
955 BulletText("Ctrl+Shift+N: Create new session");
956 BulletText("Ctrl+Shift+W: Close current session");
957 BulletText("Ctrl+Tab: Quick session switcher");
958 BulletText("Each session maintains its own ROM and editor state");
959
960 Spacing();
961 TextWrapped("Layout Management:");
962 BulletText("Drag window tabs to dock/undock");
963 BulletText("Ctrl+Shift+S: Save current layout");
964 BulletText("Ctrl+Shift+O: Load saved layout");
965 BulletText("F11: Maximize current window");
966
967 Spacing();
968 TextWrapped("Preset Layouts:");
969 BulletText("Developer: Code, memory, testing tools");
970 BulletText("Designer: Graphics, palettes, sprites");
971 BulletText("Modder: All gameplay editing tools");
972
973 if (Button("Close", ::yaze::gui::kDefaultModalSize)) {
974 Hide("Workspace Help");
975 }
976}
977
979 TextColored(gui::GetWarningColor(), "%s Warning", ICON_MD_WARNING);
980 TextWrapped("You have reached the recommended session limit.");
981 TextWrapped("Having too many sessions open may impact performance.");
982 Spacing();
983 TextWrapped("Consider closing unused sessions or saving your work.");
984
985 if (Button("Understood", ::yaze::gui::kDefaultModalSize)) {
986 Hide("Session Limit Warning");
987 }
988 SameLine();
989 if (Button("Open Session Manager", ::yaze::gui::kDefaultModalSize)) {
990 Hide("Session Limit Warning");
991 // This would trigger the session manager to open
992 }
993}
994
996 TextColored(gui::GetWarningColor(), "%s Confirm Reset", ICON_MD_WARNING);
997 TextWrapped("This will reset your current workspace layout to default.");
998 TextWrapped("Any custom window arrangements will be lost.");
999 Spacing();
1000 TextWrapped("Do you want to continue?");
1001
1002 if (Button("Reset Layout", ::yaze::gui::kDefaultModalSize)) {
1003 Hide("Layout Reset Confirm");
1004 // This would trigger the actual reset
1005 }
1006 SameLine();
1007 if (Button("Cancel", ::yaze::gui::kDefaultModalSize)) {
1008 Hide("Layout Reset Confirm");
1009 }
1010}
1011
1013 TextColored(gui::GetInfoColor(), "%s Layout Presets", ICON_MD_DASHBOARD);
1014 Separator();
1015 Spacing();
1016
1017 TextWrapped("Choose a workspace preset to quickly configure your layout:");
1018 Spacing();
1019
1020 // Get named presets from LayoutPresets
1021 struct PresetInfo {
1022 const char* name;
1023 const char* icon;
1024 const char* description;
1025 std::function<PanelLayoutPreset()> getter;
1026 };
1027
1028 PresetInfo presets[] = {
1029 {"Minimal", ICON_MD_CROP_FREE,
1030 "Essential cards only - maximum editing space",
1031 []() { return LayoutPresets::GetMinimalPreset(); }},
1032 {"Developer", ICON_MD_BUG_REPORT,
1033 "Debug and development focused - CPU/Memory/Breakpoints",
1034 []() { return LayoutPresets::GetDeveloperPreset(); }},
1035 {"Designer", ICON_MD_PALETTE,
1036 "Visual and artistic focused - Graphics/Palettes/Sprites",
1037 []() { return LayoutPresets::GetDesignerPreset(); }},
1038 {"Modder", ICON_MD_BUILD,
1039 "Full-featured - All tools available for comprehensive editing",
1040 []() { return LayoutPresets::GetModderPreset(); }},
1041 {"Overworld Expert", ICON_MD_MAP,
1042 "Complete overworld editing toolkit with all map tools",
1043 []() { return LayoutPresets::GetOverworldArtistPreset(); }},
1044 {"Dungeon Expert", ICON_MD_DOOR_SLIDING,
1045 "Complete dungeon editing toolkit with room tools",
1046 []() { return LayoutPresets::GetDungeonMasterPreset(); }},
1047 {"Testing", ICON_MD_SCIENCE, "Quality assurance and ROM testing layout",
1048 []() { return LayoutPresets::GetLogicDebuggerPreset(); }},
1049 {"Audio", ICON_MD_MUSIC_NOTE, "Music and sound editing layout",
1050 []() { return LayoutPresets::GetAudioEngineerPreset(); }},
1051 };
1052
1053 constexpr int kPresetCount = 8;
1054
1055 // Draw preset buttons in a grid
1056 float button_width = 200.0f;
1057 float button_height = 50.0f;
1058
1059 for (int i = 0; i < kPresetCount; i++) {
1060 if (i % 2 != 0)
1061 SameLine();
1062
1063 {
1064 gui::StyleVarGuard align_guard(ImGuiStyleVar_ButtonTextAlign,
1065 ImVec2(0.0f, 0.5f));
1066 if (Button(absl::StrFormat("%s %s", presets[i].icon, presets[i].name)
1067 .c_str(),
1068 ImVec2(button_width, button_height))) {
1069 // Apply the preset
1070 auto preset = presets[i].getter();
1071 auto& window_manager = editor_manager_->window_manager();
1072 // Hide all panels first
1073 window_manager.HideAll();
1074 // Show preset panels
1075 for (const auto& panel_id : preset.default_visible_panels) {
1076 window_manager.OpenWindow(panel_id);
1077 }
1079 }
1080 }
1081
1082 if (IsItemHovered()) {
1083 BeginTooltip();
1084 TextUnformatted(presets[i].description);
1085 EndTooltip();
1086 }
1087 }
1088
1089 Spacing();
1090 Separator();
1091 Spacing();
1092
1093 // Reset current editor to defaults
1094 if (Button(
1095 absl::StrFormat("%s Reset Current Editor", ICON_MD_REFRESH).c_str(),
1096 ImVec2(-1, 0))) {
1097 auto& window_manager = editor_manager_->window_manager();
1098 auto* current_editor = editor_manager_->GetCurrentEditor();
1099 if (current_editor) {
1100 auto current_type = current_editor->type();
1101 window_manager.ResetToDefaults(0, current_type);
1102 }
1104 }
1105
1106 Spacing();
1107 if (Button("Close", ImVec2(-1, 0))) {
1109 }
1110}
1111
1113 TextColored(gui::GetInfoColor(), "%s Session Manager", ICON_MD_TAB);
1114 Separator();
1115 Spacing();
1116
1117 size_t session_count = editor_manager_->GetActiveSessionCount();
1118 size_t active_session = editor_manager_->GetCurrentSessionId();
1119
1120 Text("Active Sessions: %zu", session_count);
1121 Spacing();
1122
1123 // Session table
1124 if (BeginTable("SessionTable", 4,
1125 ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
1126 TableSetupColumn("#", ImGuiTableColumnFlags_WidthFixed, 30.0f);
1127 TableSetupColumn("ROM", ImGuiTableColumnFlags_WidthStretch);
1128 TableSetupColumn("Status", ImGuiTableColumnFlags_WidthFixed, 80.0f);
1129 TableSetupColumn("Actions", ImGuiTableColumnFlags_WidthFixed, 120.0f);
1130 TableHeadersRow();
1131
1132 for (size_t i = 0; i < session_count; i++) {
1133 TableNextRow();
1134
1135 // Session number
1136 TableSetColumnIndex(0);
1137 Text("%zu", i + 1);
1138
1139 // ROM name (simplified - show current ROM for active session)
1140 TableSetColumnIndex(1);
1141 if (i == active_session) {
1142 auto* rom = editor_manager_->GetCurrentRom();
1143 if (rom && rom->is_loaded()) {
1144 TextUnformatted(rom->filename().c_str());
1145 } else {
1146 TextDisabled("(No ROM loaded)");
1147 }
1148 } else {
1149 TextDisabled("Session %zu", i + 1);
1150 }
1151
1152 // Status indicator
1153 TableSetColumnIndex(2);
1154 if (i == active_session) {
1155 TextColored(gui::GetSuccessColor(), "%s Active", ICON_MD_CHECK_CIRCLE);
1156 } else {
1157 TextDisabled("Inactive");
1158 }
1159
1160 // Actions
1161 TableSetColumnIndex(3);
1162 PushID(static_cast<int>(i));
1163
1164 if (i != active_session) {
1165 if (SmallButton("Switch")) {
1167 }
1168 SameLine();
1169 }
1170
1171 BeginDisabled(session_count <= 1);
1172 if (SmallButton("Close")) {
1174 }
1175 EndDisabled();
1176
1177 PopID();
1178 }
1179
1180 EndTable();
1181 }
1182
1183 Spacing();
1184 Separator();
1185 Spacing();
1186
1187 // New session button
1188 if (Button(absl::StrFormat("%s New Session", ICON_MD_ADD).c_str(),
1189 ImVec2(-1, 0))) {
1191 }
1192
1193 Spacing();
1194 if (Button("Close", ImVec2(-1, 0))) {
1196 }
1197}
1198
1200 // Set a comfortable default size with natural constraints
1201 SetNextWindowSize(ImVec2(900, 700), ImGuiCond_FirstUseEver);
1202 SetNextWindowSizeConstraints(ImVec2(600, 400), ImVec2(FLT_MAX, FLT_MAX));
1203
1204 Text("%s Display & Theme Settings", ICON_MD_DISPLAY_SETTINGS);
1205 TextWrapped("Customize your YAZE experience - accessible anytime!");
1206 Separator();
1207
1208 // Create a child window for scrollable content to avoid table conflicts
1209 // Use remaining space minus the close button area
1210 float available_height =
1211 GetContentRegionAvail().y - 60; // Reserve space for close button
1212 if (BeginChild("DisplaySettingsContent", ImVec2(0, available_height), true,
1213 ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
1214 // Use the popup-safe version to avoid table conflicts
1216
1217 Separator();
1218 gui::TextWithSeparators("Font Manager");
1220
1221 // Global font scale (moved from the old display settings window)
1222 ImGuiIO& io = GetIO();
1223 Separator();
1224 Text("Global Font Scale");
1225 float font_global_scale = io.FontGlobalScale;
1226 if (SliderFloat("##global_scale", &font_global_scale, 0.5f, 2.0f, "%.2f")) {
1227 if (editor_manager_) {
1228 editor_manager_->SetFontGlobalScale(font_global_scale);
1229 } else {
1230 io.FontGlobalScale = font_global_scale;
1231 }
1232 }
1233 }
1234 EndChild();
1235
1236 Separator();
1237 if (Button("Close", ::yaze::gui::kDefaultModalSize)) {
1238 Hide("Display Settings");
1239 }
1240}
1241
1243 using namespace ImGui;
1244
1245 // Display feature flags editor using the existing FlagsMenu system
1246 Text("Feature Flags Configuration");
1247 Separator();
1248
1249 BeginChild("##FlagsContent", ImVec2(0, -30), true);
1250
1251 // Use the feature flags menu system
1252 static gui::FlagsMenu flags_menu;
1253
1254 if (BeginTabBar("FlagCategories")) {
1255 if (BeginTabItem("Overworld")) {
1256 flags_menu.DrawOverworldFlags();
1257 EndTabItem();
1258 }
1259 if (BeginTabItem("Dungeon")) {
1260 flags_menu.DrawDungeonFlags();
1261 EndTabItem();
1262 }
1263 if (BeginTabItem("Resources")) {
1264 flags_menu.DrawResourceFlags();
1265 EndTabItem();
1266 }
1267 if (BeginTabItem("System")) {
1268 flags_menu.DrawSystemFlags();
1269 EndTabItem();
1270 }
1271 EndTabBar();
1272 }
1273
1274 EndChild();
1275
1276 Separator();
1277 if (Button("Close", ::yaze::gui::kDefaultModalSize)) {
1279 }
1280}
1281
1283 using namespace ImGui;
1284
1285 Text("Data Integrity Check Results");
1286 Separator();
1287
1288 BeginChild("##IntegrityContent", ImVec2(0, -30), true);
1289
1290 // Placeholder for data integrity results
1291 // In a full implementation, this would show test results
1292 Text("ROM Data Integrity:");
1293 Separator();
1294 TextColored(gui::GetSuccessColor(), "✓ ROM header valid");
1295 TextColored(gui::GetSuccessColor(), "✓ Checksum valid");
1296 TextColored(gui::GetSuccessColor(), "✓ Graphics data intact");
1297 TextColored(gui::GetSuccessColor(), "✓ Map data intact");
1298
1299 Spacing();
1300 Text("No issues detected.");
1301
1302 EndChild();
1303
1304 Separator();
1305 if (Button("Close", ::yaze::gui::kDefaultModalSize)) {
1307 }
1308}
1309
1311 using namespace ImGui;
1312
1313 if (!editor_manager_) {
1314 Text("Editor manager unavailable.");
1315 if (Button("Close", ::yaze::gui::kDefaultModalSize)) {
1317 }
1318 return;
1319 }
1320
1321 const int unloaded = editor_manager_->pending_pot_item_unloaded_rooms();
1322 const int total = editor_manager_->pending_pot_item_total_rooms();
1323
1324 Text("Pot Item Save Confirmation");
1325 Separator();
1326 TextWrapped(
1327 "Dungeon pot item saving is enabled, but %d of %d rooms are not loaded.",
1328 unloaded, total);
1329 Spacing();
1330 TextWrapped(
1331 "Saving now can overwrite pot items in unloaded rooms. Choose how to "
1332 "proceed:");
1333
1334 Spacing();
1335 if (Button("Save without pot items", ImVec2(0, 0))) {
1339 return;
1340 }
1341 SameLine();
1342 if (Button("Save anyway", ImVec2(0, 0))) {
1346 return;
1347 }
1348 SameLine();
1349 if (Button("Cancel", ImVec2(0, 0))) {
1353 }
1354}
1355
1357 using namespace ImGui;
1358
1359 if (!editor_manager_) {
1360 Text("Editor manager unavailable.");
1361 if (Button("Close", ::yaze::gui::kDefaultModalSize)) {
1363 }
1364 return;
1365 }
1366
1368 auto policy = project::RomWritePolicyToString(
1370 const auto expected = editor_manager_->GetProjectExpectedRomHash();
1371 const auto actual = editor_manager_->GetCurrentRomHash();
1372 const auto* project = editor_manager_->GetCurrentProject();
1373 const auto actual_path = editor_manager_->GetCurrentRom()
1375 : std::string();
1376 const auto editable_path =
1377 project && project->hack_manifest.loaded() &&
1378 !project->hack_manifest.build_pipeline().dev_rom.empty()
1379 ? project->GetAbsolutePath(
1380 project->hack_manifest.build_pipeline().dev_rom)
1381 : (project ? project->rom_filename : std::string());
1382
1383 Text("ROM Write Confirmation");
1384 Separator();
1385 TextWrapped(
1386 "The loaded ROM hash does not match the project's expected hash.");
1387 Spacing();
1388 Text("Role: %s", role.c_str());
1389 Text("Write policy: %s", policy.c_str());
1390 Text("Loaded ROM: %s",
1391 actual_path.empty() ? "(unknown)" : actual_path.c_str());
1392 Text("Editable target: %s",
1393 editable_path.empty() ? "(unset)" : editable_path.c_str());
1394 Text("Expected: %s", expected.empty() ? "(unset)" : expected.c_str());
1395 Text("Actual: %s", actual.empty() ? "(unknown)" : actual.c_str());
1396 Spacing();
1397 TextWrapped(
1398 "Proceeding will write to the current ROM file. This may corrupt a base "
1399 "or release ROM if it is not the intended editable project ROM.");
1400
1401 Spacing();
1402 if (Button("Save anyway", ImVec2(0, 0))) {
1405 auto status = editor_manager_->SaveRom();
1406 if (!status.ok() && !absl::IsCancelled(status)) {
1407 if (auto* toast = editor_manager_->toast_manager()) {
1408 toast->Show(absl::StrFormat("Save failed: %s", status.message()),
1410 }
1411 }
1412 return;
1413 }
1414 SameLine();
1415 if (Button("Cancel", ImVec2(0, 0)) || IsKeyPressed(ImGuiKey_Escape)) {
1418 }
1419}
1420
1422 using namespace ImGui;
1423
1424 if (!editor_manager_) {
1425 Text("Editor manager unavailable.");
1426 if (Button("Close", ::yaze::gui::kDefaultModalSize)) {
1428 }
1429 return;
1430 }
1431
1432 const auto& conflicts = editor_manager_->pending_write_conflicts();
1433
1434 TextColored(gui::GetWarningColor(), "%s Write Conflict Warning",
1436 Separator();
1437 TextWrapped(
1438 "The following ROM addresses are owned by ASM hooks and will be "
1439 "overwritten on next build. Saving now will write data that asar "
1440 "will replace.");
1441 Spacing();
1442
1443 if (!conflicts.empty()) {
1444 if (BeginTable("WriteConflictTable", 3,
1445 ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders |
1446 ImGuiTableFlags_Resizable)) {
1447 TableSetupColumn("Address", ImGuiTableColumnFlags_WidthFixed, 120.0f);
1448 TableSetupColumn("Ownership", ImGuiTableColumnFlags_WidthFixed, 140.0f);
1449 TableSetupColumn("Module", ImGuiTableColumnFlags_WidthStretch);
1450 TableHeadersRow();
1451
1452 for (const auto& conflict : conflicts) {
1453 TableNextRow();
1454 TableNextColumn();
1455 Text("$%06X", conflict.address);
1456 TableNextColumn();
1457 TextUnformatted(
1458 core::AddressOwnershipToString(conflict.ownership).c_str());
1459 TableNextColumn();
1460 if (!conflict.module.empty()) {
1461 TextUnformatted(conflict.module.c_str());
1462 } else {
1463 TextDisabled("(unknown)");
1464 }
1465 }
1466 EndTable();
1467 }
1468 }
1469
1470 Spacing();
1471 Text("%zu conflict(s) detected.", conflicts.size());
1472 Spacing();
1473
1474 if (Button("Save Anyway", ImVec2(0, 0))) {
1477 auto status = editor_manager_->SaveRom();
1478 if (!status.ok() && !absl::IsCancelled(status)) {
1479 if (auto* toast = editor_manager_->toast_manager()) {
1480 toast->Show(absl::StrFormat("Save failed: %s", status.message()),
1482 }
1483 }
1484 return;
1485 }
1486 SameLine();
1487 if (Button("Cancel", ImVec2(0, 0)) || IsKeyPressed(ImGuiKey_Escape)) {
1490 }
1491}
1492
1493} // namespace editor
1494} // namespace yaze
auto filename() const
Definition rom.h:145
bool is_loaded() const
Definition rom.h:132
auto title() const
Definition rom.h:137
static Flags & get()
Definition features.h:118
The EditorManager controls the main editor window and manages the various editor classes.
absl::Status SaveRomAs(const std::string &filename)
void SwitchToSession(size_t index)
absl::Status RestoreRomBackup(const std::string &backup_path)
Rom * GetCurrentRom() const override
std::vector< editor::RomFileManager::BackupEntry > GetRomBackups() const
project::RomWritePolicy GetProjectRomWritePolicy() const
absl::Status CreateNewProject(const std::string &template_name="Basic ROM Hack")
std::string GetCurrentRomHash() const
void SetFontGlobalScale(float scale)
void ResolvePotItemSaveConfirmation(PotItemSaveDecision decision)
WorkspaceWindowManager & window_manager()
auto GetCurrentEditor() const -> Editor *
std::string GetProjectExpectedRomHash() const
const std::vector< core::WriteConflict > & pending_write_conflicts() const
project::RomRole GetProjectRomRole() const
project::YazeProject * GetCurrentProject()
int pending_pot_item_unloaded_rooms() const
absl::Status OpenRomOrProject(const std::string &filename)
void RemoveSession(size_t index)
EditorType type() const
Definition editor.h:293
static PanelLayoutPreset GetLogicDebuggerPreset()
Get the "logic debugger" workspace preset (QA and debug focused)
static PanelLayoutPreset GetDungeonMasterPreset()
Get the "dungeon master" workspace preset.
static PanelLayoutPreset GetAudioEngineerPreset()
Get the "audio engineer" workspace preset (music focused)
static PanelLayoutPreset GetDesignerPreset()
Get the "designer" workspace preset (visual-focused)
static PanelLayoutPreset GetOverworldArtistPreset()
Get the "overworld artist" workspace preset.
static PanelLayoutPreset GetModderPreset()
Get the "modder" workspace preset (full-featured)
static PanelLayoutPreset GetMinimalPreset()
Get the "minimal" workspace preset (minimal cards)
static PanelLayoutPreset GetDeveloperPreset()
Get the "developer" workspace preset (debug-focused)
void SetStatus(const absl::Status &status)
bool IsVisible(const char *name) const
void Show(const char *name)
void Hide(const char *name)
PopupManager(EditorManager *editor_manager)
std::unordered_map< std::string, PopupParams > popups_
EditorManager * editor_manager_
bool BeginCentered(const char *name)
RAII guard for ImGui style vars.
Definition style_guard.h:68
const Theme & GetCurrentTheme() const
static ThemeManager & Get()
static std::string ShowSaveFileDialog(const std::string &default_name="", const std::string &default_extension="")
ShowSaveFileDialog opens a save file dialog and returns the selected filepath. Uses global feature fl...
static std::string ShowOpenFileDialog()
ShowOpenFileDialog opens a file dialog and returns the selected filepath. Uses global feature flag to...
static std::string ShowOpenFolderDialog()
ShowOpenFolderDialog opens a file dialog and returns the selected folder path. Uses global feature fl...
#define YAZE_VERSION_STRING
#define ICON_MD_FOLDER_OPEN
Definition icons.h:813
#define ICON_MD_SETTINGS
Definition icons.h:1699
#define ICON_MD_CANCEL
Definition icons.h:364
#define ICON_MD_WARNING
Definition icons.h:2123
#define ICON_MD_DOOR_SLIDING
Definition icons.h:614
#define ICON_MD_SAVE_AS
Definition icons.h:1646
#define ICON_MD_REFRESH
Definition icons.h:1572
#define ICON_MD_MAP
Definition icons.h:1173
#define ICON_MD_CODE
Definition icons.h:434
#define ICON_MD_LABEL
Definition icons.h:1053
#define ICON_MD_VIDEOGAME_ASSET
Definition icons.h:2076
#define ICON_MD_BUG_REPORT
Definition icons.h:327
#define ICON_MD_EDIT
Definition icons.h:645
#define ICON_MD_ERROR
Definition icons.h:686
#define ICON_MD_MUSIC_NOTE
Definition icons.h:1264
#define ICON_MD_RESTORE
Definition icons.h:1605
#define ICON_MD_DISPLAY_SETTINGS
Definition icons.h:587
#define ICON_MD_ADD
Definition icons.h:86
#define ICON_MD_SCIENCE
Definition icons.h:1656
#define ICON_MD_CHECK_CIRCLE
Definition icons.h:400
#define ICON_MD_DESCRIPTION
Definition icons.h:539
#define ICON_MD_BUILD
Definition icons.h:328
#define ICON_MD_DASHBOARD
Definition icons.h:517
#define ICON_MD_SAVE
Definition icons.h:1644
#define ICON_MD_TAB
Definition icons.h:1930
#define ICON_MD_FOLDER
Definition icons.h:809
#define ICON_MD_BACKUP
Definition icons.h:231
#define ICON_MD_PALETTE
Definition icons.h:1370
#define ICON_MD_OPEN_IN_NEW
Definition icons.h:1354
#define ICON_MD_CONTENT_COPY
Definition icons.h:465
#define ICON_MD_CROP_FREE
Definition icons.h:495
#define ICON_MD_DELETE_SWEEP
Definition icons.h:533
Definition input.cc:22
std::string AddressOwnershipToString(AddressOwnership ownership)
constexpr const char * kRomInfo
constexpr const char * kLayoutPresets
constexpr const char * kSaveScope
constexpr const char * kAbout
constexpr const char * kSessionManager
constexpr const char * kTroubleshooting
constexpr const char * kRomBackups
constexpr const char * kWhatsNew
constexpr const char * kSupportedFeatures
constexpr const char * kDataIntegrity
constexpr const char * kManageProject
constexpr const char * kNewProject
constexpr const char * kSaveAs
constexpr const char * kDisplaySettings
constexpr const char * kSessionLimitWarning
constexpr const char * kCLIUsage
constexpr const char * kLayoutResetConfirm
constexpr const char * kWriteConflictWarning
constexpr const char * kAsarIntegration
constexpr const char * kOpenRomHelp
constexpr const char * kFeatureFlags
constexpr const char * kDungeonPotItemSaveConfirm
constexpr const char * kGettingStarted
constexpr const char * kContributing
constexpr const char * kBuildInstructions
constexpr const char * kWorkspaceHelp
constexpr const char * kRomWriteConfirm
ImVec4 ConvertColorToImVec4(const Color &color)
Definition color.h:134
void DrawFontManager()
Definition style.cc:1326
ImVec4 GetSuccessColor()
Definition ui_helpers.cc:48
ImVec4 GetWarningColor()
Definition ui_helpers.cc:53
void DrawDisplaySettingsForPopup(ImGuiStyle *ref)
Definition style.cc:872
constexpr ImVec2 kDefaultModalSize
Definition input.h:21
ImVec4 GetInfoColor()
Definition ui_helpers.cc:63
void TextWithSeparators(const absl::string_view &text)
Definition style.cc:1320
std::string RomRoleToString(RomRole role)
Definition project.cc:192
std::string RomWritePolicyToString(RomWritePolicy policy)
Definition project.cc:220
std::string HexLongLong(uint64_t qword, HexStringParams params)
Definition hex.cc:63
FileDialogOptions MakeRomFileDialogOptions(bool include_all_files)
Definition file_util.cc:87
Defines default panel visibility for an editor type.
std::string GetAbsolutePath(const std::string &relative_path) const
Definition project.cc:1319
Public YAZE API umbrella header.