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