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