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
448 Separator();
449 TextWrapped(
450 tr("A restored backup is staged in this session. Save ROM to commit "
451 "it. Discard reloads the backing ROM and abandons staged ROM-buffer "
452 "edits; resolve pending dungeon or palette edits first."));
453 if (Button(ICON_MD_UNDO " Discard Restored Backup")) {
455 if (!status.ok()) {
456 if (auto* toast = editor_manager_->toast_manager()) {
457 toast->Show(absl::StrFormat("Discard failed: %s", status.message()),
459 }
460 } else if (auto* toast = editor_manager_->toast_manager()) {
461 toast->Show("Restored backup discarded; reloaded ROM from disk",
463 }
464 }
465 Separator();
466 }
467
468 if (Button(ICON_MD_DELETE_SWEEP " Prune Backups")) {
469 auto status = editor_manager_->PruneRomBackups();
470 if (!status.ok()) {
471 if (auto* toast = editor_manager_->toast_manager()) {
472 toast->Show(absl::StrFormat("Prune failed: %s", status.message()),
474 }
475 } else if (auto* toast = editor_manager_->toast_manager()) {
476 toast->Show("Backups pruned", ToastType::kSuccess);
477 }
478 }
479
480 Separator();
481 auto backups = editor_manager_->GetRomBackups();
482 if (backups.empty()) {
483 TextDisabled(tr("No backups found."));
484 } else if (BeginTable("RomBackupTable", 4,
485 ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders |
486 ImGuiTableFlags_Resizable)) {
487 TableSetupColumn("Timestamp");
488 TableSetupColumn("Size");
489 TableSetupColumn("Filename");
490 TableSetupColumn("Actions");
491 TableHeadersRow();
492
493 auto format_size = [](uintmax_t bytes) {
494 if (bytes > (1024 * 1024)) {
495 return absl::StrFormat("%.2f MB",
496 static_cast<double>(bytes) / (1024 * 1024));
497 }
498 if (bytes > 1024) {
499 return absl::StrFormat("%.1f KB", static_cast<double>(bytes) / 1024.0);
500 }
501 return absl::StrFormat("%llu B", static_cast<unsigned long long>(bytes));
502 };
503
504 for (size_t i = 0; i < backups.size(); ++i) {
505 const auto& backup = backups[i];
506 TableNextRow();
507 TableNextColumn();
508 char time_buffer[32] = "unknown";
509 if (backup.timestamp != 0) {
510 std::tm local_tm{};
511#ifdef _WIN32
512 localtime_s(&local_tm, &backup.timestamp);
513#else
514 localtime_r(&backup.timestamp, &local_tm);
515#endif
516 std::strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%d %H:%M:%S",
517 &local_tm);
518 }
519 TextUnformatted(time_buffer);
520
521 TableNextColumn();
522 TextUnformatted(format_size(backup.size_bytes).c_str());
523
524 TableNextColumn();
525 TextUnformatted(backup.filename.c_str());
526
527 TableNextColumn();
528 PushID(static_cast<int>(i));
529 if (Button(ICON_MD_RESTORE " Restore")) {
530 auto status = editor_manager_->RestoreRomBackup(backup.path);
531 if (!status.ok()) {
532 if (auto* toast = editor_manager_->toast_manager()) {
533 toast->Show(absl::StrFormat("Restore failed: %s", status.message()),
535 }
536 } else if (auto* toast = editor_manager_->toast_manager()) {
537 toast->Show("Backup loaded; inspect it, then save ROM to commit",
539 }
540 }
541 SameLine();
542 if (Button(ICON_MD_OPEN_IN_NEW " Open")) {
543 auto status = editor_manager_->OpenRomOrProject(backup.path);
544 if (!status.ok()) {
545 if (auto* toast = editor_manager_->toast_manager()) {
546 toast->Show(absl::StrFormat("Open failed: %s", status.message()),
548 }
549 }
550 }
551 SameLine();
552 if (Button(ICON_MD_CONTENT_COPY " Copy")) {
553 SetClipboardText(backup.path.c_str());
554 }
555 PopID();
556 }
557 EndTable();
558 }
559
560 Separator();
561 if (Button(tr("Close"), ::yaze::gui::kDefaultModalSize)) {
563 }
564}
565
567 using namespace ImGui;
568
569 static std::string project_name = "";
570 static std::string project_filepath = "";
571 static std::string rom_filename = "";
572 static std::string labels_filename = "";
573 static std::string code_folder = "";
574
575 InputText(tr("Project Name"), &project_name);
576
577 if (Button(absl::StrFormat("%s Destination Folder", ICON_MD_FOLDER).c_str(),
580 }
581 SameLine();
582 Text("%s", project_filepath.empty() ? "(Not set)" : project_filepath.c_str());
583
584 if (Button(absl::StrFormat("%s ROM File", ICON_MD_VIDEOGAME_ASSET).c_str(),
588 }
589 SameLine();
590 Text("%s", rom_filename.empty() ? "(Not set)" : rom_filename.c_str());
591
592 if (Button(absl::StrFormat("%s Labels File", ICON_MD_LABEL).c_str(),
595 }
596 SameLine();
597 Text("%s", labels_filename.empty() ? "(Not set)" : labels_filename.c_str());
598
599 if (Button(absl::StrFormat("%s Code Folder", ICON_MD_CODE).c_str(),
602 }
603 SameLine();
604 Text("%s", code_folder.empty() ? "(Not set)" : code_folder.c_str());
605
606 Separator();
607
608 if (Button(absl::StrFormat("%s Choose Project File Location", ICON_MD_SAVE)
609 .c_str(),
611 auto project_file_path =
613 if (!project_file_path.empty()) {
614 if (!(absl::EndsWith(project_file_path, ".yaze") ||
615 absl::EndsWith(project_file_path, ".yazeproj"))) {
616 project_file_path += ".yaze";
617 }
618 project_filepath = project_file_path;
619 }
620 }
621
622 if (Button(absl::StrFormat("%s Create Project", ICON_MD_ADD).c_str(),
624 if (!project_filepath.empty() && !project_name.empty() &&
625 !rom_filename.empty()) {
627 "Basic ROM Hack", rom_filename, project_name, project_filepath);
628 if (status.ok()) {
629 auto* project = editor_manager_->GetCurrentProject();
630 if (project) {
631 if (!labels_filename.empty()) {
632 project->labels_filename = labels_filename;
633 }
634 if (!code_folder.empty()) {
635 project->code_folder = code_folder;
636 }
637 if (!labels_filename.empty() || !code_folder.empty()) {
639 status = editor_manager_->SaveProject();
640 }
641 }
642 }
643 if (status.ok()) {
644 // Clear fields
645 project_name = "";
646 project_filepath = "";
647 rom_filename = "";
648 labels_filename = "";
649 code_folder = "";
651 } else {
652 SetStatus(status);
653 }
654 }
655 }
656 SameLine();
657 if (Button(absl::StrFormat("%s Cancel", ICON_MD_CANCEL).c_str(),
659 // Clear fields
660 project_name = "";
661 project_filepath = "";
662 rom_filename = "";
663 labels_filename = "";
664 code_folder = "";
666 }
667}
668
670 const auto& theme = gui::ThemeManager::Get().GetCurrentTheme();
671 const ImVec4 status_ok = gui::ConvertColorToImVec4(theme.success);
672 const ImVec4 status_warn = gui::ConvertColorToImVec4(theme.warning);
673 const ImVec4 status_info = gui::ConvertColorToImVec4(theme.info);
674 const ImVec4 status_error = gui::ConvertColorToImVec4(theme.error);
675
676 auto status_color = [&](const char* status) -> ImVec4 {
677 if (strcmp(status, "Stable") == 0 || strcmp(status, "Working") == 0) {
678 return status_ok;
679 }
680 if (strcmp(status, "Beta") == 0 || strcmp(status, "Experimental") == 0) {
681 return status_warn;
682 }
683 if (strcmp(status, "Preview") == 0) {
684 return status_info;
685 }
686 if (strcmp(status, "Not available") == 0) {
687 return status_error;
688 }
689 return status_info;
690 };
691
692 struct FeatureRow {
693 const char* feature;
694 const char* status;
695 const char* persistence;
696 const char* notes;
697 };
698
699 auto draw_table = [&](const char* table_id,
700 std::initializer_list<FeatureRow> rows) {
701 ImGuiTableFlags flags = ImGuiTableFlags_BordersInnerH |
702 ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable;
703 if (!BeginTable(table_id, 4, flags)) {
704 return;
705 }
706 TableSetupColumn("Feature", ImGuiTableColumnFlags_WidthStretch);
707 TableSetupColumn("Status", ImGuiTableColumnFlags_WidthFixed, 120.0f);
708 TableSetupColumn("Save/Load", ImGuiTableColumnFlags_WidthFixed, 180.0f);
709 TableSetupColumn("Notes", ImGuiTableColumnFlags_WidthStretch);
710 TableHeadersRow();
711
712 for (const auto& row : rows) {
713 TableNextRow();
714 TableSetColumnIndex(0);
715 TextUnformatted(row.feature);
716 TableSetColumnIndex(1);
717 TextColored(status_color(row.status), "%s", row.status);
718 TableSetColumnIndex(2);
719 TextUnformatted(row.persistence);
720 TableSetColumnIndex(3);
721 TextWrapped("%s", row.notes);
722 }
723
724 EndTable();
725 };
726
727 TextDisabled(
728 tr("Status: Stable = production ready, Beta = usable with gaps, "
729 "Experimental = WIP, Preview = web parity in progress."));
730 TextDisabled(tr("See Settings > Feature Flags for ROM-specific toggles."));
731 Spacing();
732
733 if (CollapsingHeader(tr("Desktop App (yaze)"),
734 ImGuiTreeNodeFlags_DefaultOpen)) {
735 draw_table("desktop_features",
736 {
737 {"ROM load/save", "Stable", "ROM + backups",
738 "Backups on save when enabled."},
739 {"Overworld Editor", "Stable", "ROM",
740 "Maps/entrances/exits/items; version-gated."},
741 {"Dungeon Editor", "Stable", "ROM",
742 "Room objects/tiles/palettes persist."},
743 {"Palette Editor", "Stable", "ROM",
744 "Palette edits persist; JSON IO pending."},
745 {"Graphics Editor", "Beta", "ROM",
746 "Sheet edits persist; tooling still expanding."},
747 {"Sprite Editor", "Stable", "ROM", "Sprite edits persist."},
748 {"Message Editor", "Stable", "ROM", "Text edits persist."},
749 {"Screen Editor", "Experimental", "ROM (partial)",
750 "Save coverage incomplete."},
751 {"Hex Editor", "Beta", "ROM", "Search UX incomplete."},
752 {"Assembly/Asar", "Beta", "ROM + project",
753 "Patch apply + symbol export."},
754 {"Emulator", "Beta", "Runtime only",
755 "Save-state UI partially wired."},
756 {"Music Editor", "Experimental", "ROM (partial)",
757 "Serialization in progress."},
758 {"Agent UI", "Experimental", ".yaze/agent",
759 "Requires AI provider configuration."},
760 {"Settings/Layouts", "Beta", ".yaze config",
761 "Layout serialization improving."},
762 });
763 }
764
765 if (CollapsingHeader(tr("z3ed CLI"))) {
766 draw_table("cli_features",
767 {
768 {"ROM read/write/validate", "Stable", "ROM file",
769 "Direct command execution."},
770 {"Agent workflows", "Stable", ".yaze/proposals + sandboxes",
771 "Commit writes ROM; revert reloads."},
772 {"Snapshots/restore", "Stable", "Sandbox copies",
773 "Supports YAZE_SANDBOX_ROOT override."},
774 {"Doctor/test suites", "Stable", "Reports",
775 "Structured output for automation."},
776 {"TUI/REPL", "Stable", "Session history",
777 "Interactive command palette + logs."},
778 });
779 }
780
781 if (CollapsingHeader(tr("Web/WASM Preview"))) {
782 draw_table(
783 "web_features",
784 {
785 {"ROM load/save", "Preview", "IndexedDB + download",
786 "Drag/drop or picker; download for backups."},
787 {"Editors (OW/Dungeon/Palette/etc.)", "Preview",
788 "IndexedDB + download", "Parity work in progress."},
789 {"Hex Editor", "Working", "IndexedDB + download",
790 "Direct ROM editing available."},
791 {"Asar patching", "Preview", "ROM", "Basic patch apply support."},
792 {"Emulator", "Not available", "N/A", "Desktop only."},
793 {"Collaboration", "Experimental", "Server",
794 "Requires yaze-server."},
795 {"AI features", "Preview", "Server", "Requires AI-enabled server."},
796 });
797 }
798
799 if (Button(tr("Close"), ::yaze::gui::kDefaultModalSize)) {
801 }
802}
803
805 Text(tr("File -> Open"));
806 Text(tr("Select a ROM file to open"));
807 Text(tr("Supported ROMs (headered or unheadered):"));
808 Text(tr("The Legend of Zelda: A Link to the Past"));
809 Text(tr("US Version 1.0"));
810 Text(tr("JP Version 1.0"));
811 Spacing();
812 TextWrapped(
813 tr("ROM files are not bundled. Use a clean, legally obtained copy."));
814
815 if (Button(tr("Close"), ::yaze::gui::kDefaultModalSize)) {
816 Hide("Open a ROM");
817 }
818}
819
821 Text(tr("Project Menu"));
822 Text(tr("Create a new project or open an existing one."));
823 Text(tr("Save the project to save the current state of the project."));
824 TextWrapped(
825 tr("To save a project, you need to first open a ROM and initialize your "
826 "code path and labels file. Label resource manager can be found in "
827 "the View menu. Code path is set in the Code editor after opening a "
828 "folder."));
829
830 if (Button(tr("Close"), ::yaze::gui::kDefaultModalSize)) {
831 Hide("Manage Project");
832 }
833}
834
836 TextWrapped(tr("Welcome to YAZE v%s!"), YAZE_VERSION_STRING);
837 TextWrapped(tr(
838 "YAZE lets you modify 'The Legend of Zelda: A Link to the Past' (US or "
839 "JP) ROMs with modern tooling."));
840 Spacing();
841 TextWrapped(tr("Release Highlights:"));
842 BulletText(
843 tr("AI-assisted workflows via z3ed agent and in-app panels "
844 "(Ollama/Gemini/OpenAI/Anthropic)"));
845 BulletText(tr("Clear feature status panels and improved help/tooltips"));
846 BulletText(tr("Unified .yaze storage across desktop/CLI/web"));
847 Spacing();
848 TextWrapped(tr("General Tips:"));
849 BulletText(tr("Open a clean ROM and save a backup before editing"));
850 BulletText(tr("Use Help (F1) for context-aware guidance and shortcuts"));
851 BulletText(tr(
852 "Configure AI providers (Ollama/Gemini/OpenAI/Anthropic) in Settings > "
853 "Agent"));
854
855 if (Button(tr("Close"), ::yaze::gui::kDefaultModalSize)) {
856 Hide("Getting Started");
857 }
858}
859
861 TextWrapped(tr("Asar 65816 Assembly Integration"));
862 TextWrapped(
863 tr("YAZE includes full Asar assembler support for ROM patching."));
864 Spacing();
865 TextWrapped(tr("Features:"));
866 BulletText(tr("Cross-platform ROM patching with assembly code"));
867 BulletText(tr("Symbol export with addresses and opcodes"));
868 BulletText(tr("Assembly validation with detailed error reporting"));
869 BulletText(tr("Memory-safe patch application with size checks"));
870
871 if (Button(tr("Close"), ::yaze::gui::kDefaultModalSize)) {
872 Hide("Asar Integration");
873 }
874}
875
877 TextWrapped(tr("Build Instructions"));
878 TextWrapped(tr("YAZE uses modern CMake for cross-platform builds."));
879 Spacing();
880 TextWrapped(tr("Quick Start (examples):"));
881 BulletText(tr("cmake --preset mac-dbg | lin-dbg | win-dbg"));
882 BulletText(tr("cmake --build --preset <preset> --target yaze"));
883 Spacing();
884 TextWrapped(tr("AI Builds:"));
885 BulletText(tr("cmake --preset mac-ai | lin-ai | win-ai"));
886 BulletText(tr("cmake --build --preset <preset> --target yaze z3ed"));
887 Spacing();
888 TextWrapped(tr("Docs: docs/public/build/quick-reference.md"));
889
890 if (Button(tr("Close"), ::yaze::gui::kDefaultModalSize)) {
891 Hide("Build Instructions");
892 }
893}
894
896 TextWrapped(tr("Command Line Interface (z3ed)"));
897 TextWrapped(tr("Scriptable ROM editing and AI agent workflows."));
898 Spacing();
899 TextWrapped(tr("Commands:"));
900 BulletText(tr("z3ed rom-info --rom=zelda3.sfc"));
901 BulletText(tr("z3ed agent simple-chat --rom=zelda3.sfc --ai_provider=auto"));
902 BulletText(tr("z3ed agent plan --rom=zelda3.sfc"));
903 BulletText(tr("z3ed test-list --format json"));
904 BulletText(tr("z3ed patch apply-asar patch.asm --rom=zelda3.sfc"));
905 BulletText(tr("z3ed help dungeon-place-sprite"));
906 Spacing();
907 TextWrapped(tr("Storage:"));
908 BulletText(
909 tr("Agent plans/proposals live under ~/.yaze (see docs for details)"));
910
911 if (Button(tr("Close"), ::yaze::gui::kDefaultModalSize)) {
912 Hide("CLI Usage");
913 }
914}
915
917 TextWrapped(tr("Troubleshooting"));
918 TextWrapped(tr("Common issues and solutions:"));
919 Spacing();
920 BulletText(tr("ROM won't load: Check file format (SFC/SMC supported)"));
921 BulletText(
922 tr("AI agent missing: Start Ollama or set GEMINI_API_KEY/OPENAI_API_KEY/"
923 "ANTHROPIC_API_KEY (web uses AI_AGENT_ENDPOINT)"));
924 BulletText(tr("Graphics issues: Disable experimental flags in Settings"));
925 BulletText(
926 tr("Performance: Enable hardware acceleration in display settings"));
927 BulletText(tr("Crashes: Check ROM file integrity and available memory"));
928 BulletText(tr("Layout issues: Reset workspace layouts from View > Layouts"));
929
930 if (Button(tr("Close"), ::yaze::gui::kDefaultModalSize)) {
931 Hide("Troubleshooting");
932 }
933}
934
936 TextWrapped(tr("Contributing to YAZE"));
937 TextWrapped(tr("YAZE is open source and welcomes contributions!"));
938 Spacing();
939 TextWrapped(tr("How to contribute:"));
940 BulletText(tr("Fork the repository on GitHub"));
941 BulletText(tr("Create feature branches for new work"));
942 BulletText(tr("Follow C++ coding standards"));
943 BulletText(tr("Include tests for new features"));
944 BulletText(tr("Submit pull requests for review"));
945
946 if (Button(tr("Close"), ::yaze::gui::kDefaultModalSize)) {
947 Hide("Contributing");
948 }
949}
950
952 TextWrapped(tr("What's New in YAZE v%s"), YAZE_VERSION_STRING);
953 Spacing();
954
955 if (CollapsingHeader(
956 absl::StrFormat("%s User Interface & Theming", ICON_MD_PALETTE)
957 .c_str(),
958 ImGuiTreeNodeFlags_DefaultOpen)) {
959 BulletText(
960 tr("Feature status/persistence summaries across desktop/CLI/web"));
961 BulletText(tr("Shortcut/help panels now match configured keybindings"));
962 BulletText(tr("Refined onboarding tips and error messaging"));
963 BulletText(tr("Help text refreshed across desktop, CLI, and web"));
964 }
965
966 if (CollapsingHeader(
967 absl::StrFormat("%s Development & Build System", ICON_MD_BUILD)
968 .c_str(),
969 ImGuiTreeNodeFlags_DefaultOpen)) {
970 BulletText(tr("Asar 65816 assembler integration for ROM patching"));
971 BulletText(tr("z3ed CLI + TUI for scripting, test/doctor, and automation"));
972 BulletText(tr("Modern CMake presets for desktop, AI, and web builds"));
973 BulletText(tr("Unified version + storage references for 0.5.1"));
974 }
975
976 if (CollapsingHeader(
977 absl::StrFormat("%s Core Improvements", ICON_MD_SETTINGS).c_str())) {
978 BulletText(tr("Improved project metadata + .yaze storage alignment"));
979 BulletText(tr("Stronger error reporting and status feedback"));
980 BulletText(tr("Performance and stability improvements across editors"));
981 BulletText(tr("Expanded logging and diagnostics tooling"));
982 }
983
984 if (CollapsingHeader(
985 absl::StrFormat("%s Editor Features", ICON_MD_EDIT).c_str())) {
986 BulletText(tr("Music editor updates with SPC parsing/playback"));
987 BulletText(
988 tr("AI agent-assisted editing workflows (multi-provider + vision)"));
989 BulletText(tr("Expanded overworld/dungeon tooling and palette accuracy"));
990 BulletText(tr("Web/WASM preview with collaboration hooks"));
991 }
992
993 Spacing();
994 if (Button(
995 absl::StrFormat("%s View Release Notes", ICON_MD_DESCRIPTION).c_str(),
996 ImVec2(-1, 30))) {
997 // Close this popup and show theme settings
999 // Could trigger release notes panel opening here
1000 }
1001
1002 if (Button(tr("Close"), ::yaze::gui::kDefaultModalSize)) {
1004 }
1005}
1006
1008 TextWrapped(tr("Workspace Management"));
1009 TextWrapped(tr(
1010 "YAZE supports multiple ROM sessions and flexible workspace layouts."));
1011 Spacing();
1012
1013 TextWrapped(tr("Session Management:"));
1014 BulletText(tr("Ctrl+Shift+N: Create new session"));
1015 BulletText(tr("Ctrl+Shift+W: Close current session"));
1016 BulletText(tr("Ctrl+Tab: Quick session switcher"));
1017 BulletText(tr("Each session maintains its own ROM and editor state"));
1018
1019 Spacing();
1020 TextWrapped(tr("Layout Management:"));
1021 BulletText(tr("Drag window tabs to dock/undock"));
1022 BulletText(tr("Ctrl+Shift+S: Save current layout"));
1023 BulletText(tr("Ctrl+Shift+O: Load saved layout"));
1024 BulletText(tr("F11: Maximize current window"));
1025
1026 Spacing();
1027 TextWrapped(tr("Preset Layouts:"));
1028 BulletText(tr("Developer: Code, memory, testing tools"));
1029 BulletText(tr("Designer: Graphics, palettes, sprites"));
1030 BulletText(tr("Modder: All gameplay editing tools"));
1031
1032 if (Button(tr("Close"), ::yaze::gui::kDefaultModalSize)) {
1033 Hide("Workspace Help");
1034 }
1035}
1036
1038 TextColored(gui::GetWarningColor(), tr("%s Warning"), ICON_MD_WARNING);
1039 TextWrapped(tr("You have reached the recommended session limit."));
1040 TextWrapped(tr("Having too many sessions open may impact performance."));
1041 Spacing();
1042 TextWrapped(tr("Consider closing unused sessions or saving your work."));
1043
1044 if (Button(tr("Understood"), ::yaze::gui::kDefaultModalSize)) {
1045 Hide("Session Limit Warning");
1046 }
1047 SameLine();
1048 if (Button(tr("Open Session Manager"), ::yaze::gui::kDefaultModalSize)) {
1049 Hide("Session Limit Warning");
1050 // This would trigger the session manager to open
1051 }
1052}
1053
1055 TextColored(gui::GetWarningColor(), tr("%s Confirm Reset"), ICON_MD_WARNING);
1056 TextWrapped(tr("This will reset your current workspace layout to default."));
1057 TextWrapped(tr("Any custom window arrangements will be lost."));
1058 Spacing();
1059 TextWrapped(tr("Do you want to continue?"));
1060
1061 if (Button(tr("Reset Layout"), ::yaze::gui::kDefaultModalSize)) {
1062 Hide("Layout Reset Confirm");
1063 // This would trigger the actual reset
1064 }
1065 SameLine();
1066 if (Button(tr("Cancel"), ::yaze::gui::kDefaultModalSize)) {
1067 Hide("Layout Reset Confirm");
1068 }
1069}
1070
1072 TextColored(gui::GetInfoColor(), tr("%s Layout Presets"), ICON_MD_DASHBOARD);
1073 Separator();
1074 Spacing();
1075
1076 TextWrapped(
1077 tr("Choose a workspace preset to quickly configure your layout:"));
1078 Spacing();
1079
1080 // Get named presets from LayoutPresets
1081 struct PresetInfo {
1082 const char* name;
1083 const char* icon;
1084 const char* description;
1085 std::function<PanelLayoutPreset()> getter;
1086 };
1087
1088 PresetInfo presets[] = {
1089 {"Minimal", ICON_MD_CROP_FREE,
1090 "Essential cards only - maximum editing space",
1091 []() { return LayoutPresets::GetMinimalPreset(); }},
1092 {"Developer", ICON_MD_BUG_REPORT,
1093 "Debug and development focused - CPU/Memory/Breakpoints",
1094 []() { return LayoutPresets::GetDeveloperPreset(); }},
1095 {"Designer", ICON_MD_PALETTE,
1096 "Visual and artistic focused - Graphics/Palettes/Sprites",
1097 []() { return LayoutPresets::GetDesignerPreset(); }},
1098 {"Modder", ICON_MD_BUILD,
1099 "Full-featured - All tools available for comprehensive editing",
1100 []() { return LayoutPresets::GetModderPreset(); }},
1101 {"Overworld Expert", ICON_MD_MAP,
1102 "Complete overworld editing toolkit with all map tools",
1103 []() { return LayoutPresets::GetOverworldArtistPreset(); }},
1104 {"Dungeon Expert", ICON_MD_DOOR_SLIDING,
1105 "Complete dungeon editing toolkit with room tools",
1106 []() { return LayoutPresets::GetDungeonMasterPreset(); }},
1107 {"Testing", ICON_MD_SCIENCE, "Quality assurance and ROM testing layout",
1108 []() { return LayoutPresets::GetLogicDebuggerPreset(); }},
1109 {"Audio", ICON_MD_MUSIC_NOTE, "Music and sound editing layout",
1110 []() { return LayoutPresets::GetAudioEngineerPreset(); }},
1111 };
1112
1113 constexpr int kPresetCount = 8;
1114
1115 // Draw preset buttons in a grid
1116 float button_width = 200.0f;
1117 float button_height = 50.0f;
1118
1119 for (int i = 0; i < kPresetCount; i++) {
1120 if (i % 2 != 0)
1121 SameLine();
1122
1123 {
1124 gui::StyleVarGuard align_guard(ImGuiStyleVar_ButtonTextAlign,
1125 ImVec2(0.0f, 0.5f));
1126 if (Button(absl::StrFormat("%s %s", presets[i].icon, presets[i].name)
1127 .c_str(),
1128 ImVec2(button_width, button_height))) {
1129 // Apply the preset
1130 auto preset = presets[i].getter();
1131 auto& window_manager = editor_manager_->window_manager();
1132 // Hide all panels first
1133 window_manager.HideAll();
1134 // Show preset panels
1135 for (const auto& panel_id : preset.default_visible_panels) {
1136 window_manager.OpenWindow(panel_id);
1137 }
1139 }
1140 }
1141
1142 if (IsItemHovered()) {
1143 BeginTooltip();
1144 TextUnformatted(presets[i].description);
1145 EndTooltip();
1146 }
1147 }
1148
1149 Spacing();
1150 Separator();
1151 Spacing();
1152
1153 // Reset current editor to defaults
1154 if (Button(
1155 absl::StrFormat("%s Reset Current Editor", ICON_MD_REFRESH).c_str(),
1156 ImVec2(-1, 0))) {
1157 auto& window_manager = editor_manager_->window_manager();
1158 auto* current_editor = editor_manager_->GetCurrentEditor();
1159 if (current_editor) {
1160 auto current_type = current_editor->type();
1161 window_manager.ResetToDefaults(0, current_type);
1162 }
1164 }
1165
1166 Spacing();
1167 if (Button(tr("Close"), ImVec2(-1, 0))) {
1169 }
1170}
1171
1173 TextColored(gui::GetInfoColor(), tr("%s Session Manager"), ICON_MD_TAB);
1174 Separator();
1175 Spacing();
1176
1177 size_t session_count = editor_manager_->GetActiveSessionCount();
1178 size_t active_session = editor_manager_->GetCurrentSessionIndex();
1179
1180 Text(tr("Active Sessions: %zu"), session_count);
1181 Spacing();
1182
1183 // Session table
1184 if (BeginTable("SessionTable", 4,
1185 ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
1186 TableSetupColumn("#", ImGuiTableColumnFlags_WidthFixed, 30.0f);
1187 TableSetupColumn("ROM", ImGuiTableColumnFlags_WidthStretch);
1188 TableSetupColumn("Status", ImGuiTableColumnFlags_WidthFixed, 80.0f);
1189 TableSetupColumn("Actions", ImGuiTableColumnFlags_WidthFixed, 120.0f);
1190 TableHeadersRow();
1191
1192 for (size_t i = 0; i < session_count; i++) {
1193 TableNextRow();
1194
1195 // Session number
1196 TableSetColumnIndex(0);
1197 Text("%zu", i + 1);
1198
1199 // ROM name (simplified - show current ROM for active session)
1200 TableSetColumnIndex(1);
1201 if (i == active_session) {
1202 auto* rom = editor_manager_->GetCurrentRom();
1203 if (rom && rom->is_loaded()) {
1204 TextUnformatted(rom->filename().c_str());
1205 } else {
1206 TextDisabled(tr("(No ROM loaded)"));
1207 }
1208 } else {
1209 TextDisabled(tr("Session %zu"), i + 1);
1210 }
1211
1212 // Status indicator
1213 TableSetColumnIndex(2);
1214 if (i == active_session) {
1215 TextColored(gui::GetSuccessColor(), tr("%s Active"),
1217 } else {
1218 TextDisabled(tr("Inactive"));
1219 }
1220
1221 // Actions
1222 TableSetColumnIndex(3);
1223 PushID(static_cast<int>(i));
1224
1225 if (i != active_session) {
1226 if (SmallButton(tr("Switch"))) {
1228 }
1229 SameLine();
1230 }
1231
1232 BeginDisabled(session_count <= 1);
1233 if (SmallButton(tr("Close"))) {
1235 }
1236 EndDisabled();
1237
1238 PopID();
1239 }
1240
1241 EndTable();
1242 }
1243
1244 Spacing();
1245 Separator();
1246 Spacing();
1247
1248 // New session button
1249 if (Button(absl::StrFormat("%s New Session", ICON_MD_ADD).c_str(),
1250 ImVec2(-1, 0))) {
1252 }
1253
1254 Spacing();
1255 if (Button(tr("Close"), ImVec2(-1, 0))) {
1257 }
1258}
1259
1261 // Set a comfortable default size with natural constraints
1262 SetNextWindowSize(ImVec2(900, 700), ImGuiCond_FirstUseEver);
1263 SetNextWindowSizeConstraints(ImVec2(600, 400), ImVec2(FLT_MAX, FLT_MAX));
1264
1265 Text(tr("%s Display & Theme Settings"), ICON_MD_DISPLAY_SETTINGS);
1266 TextWrapped(tr("Customize your YAZE experience - accessible anytime!"));
1267 Separator();
1268
1269 // Create a child window for scrollable content to avoid table conflicts
1270 // Use remaining space minus the close button area
1271 float available_height =
1272 GetContentRegionAvail().y - 60; // Reserve space for close button
1273 if (BeginChild("DisplaySettingsContent", ImVec2(0, available_height), true,
1274 ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
1275 // Use the popup-safe version to avoid table conflicts
1277
1278 Separator();
1279 gui::TextWithSeparators("Font Manager");
1281
1282 // Global font scale (moved from the old display settings window)
1283 ImGuiIO& io = GetIO();
1284 Separator();
1285 Text(tr("Global Font Scale"));
1286 float font_global_scale = io.FontGlobalScale;
1287 if (SliderFloat("##global_scale", &font_global_scale, 0.5f, 2.0f, "%.2f")) {
1288 if (editor_manager_) {
1289 editor_manager_->SetFontGlobalScale(font_global_scale);
1290 } else {
1291 io.FontGlobalScale = font_global_scale;
1292 }
1293 }
1294 }
1295 EndChild();
1296
1297 Separator();
1298 if (Button(tr("Close"), ::yaze::gui::kDefaultModalSize)) {
1299 Hide("Display Settings");
1300 }
1301}
1302
1304 using namespace ImGui;
1305
1306 // Display feature flags editor using the existing FlagsMenu system
1307 Text(tr("Feature Flags Configuration"));
1308 Separator();
1309
1310 BeginChild("##FlagsContent", ImVec2(0, -30), true);
1311
1312 // Use the feature flags menu system
1313 static gui::FlagsMenu flags_menu;
1314
1315 if (BeginTabBar("FlagCategories")) {
1316 if (BeginTabItem(tr("Overworld"))) {
1317 flags_menu.DrawOverworldFlags();
1318 EndTabItem();
1319 }
1320 if (BeginTabItem(tr("Dungeon"))) {
1321 flags_menu.DrawDungeonFlags();
1322 EndTabItem();
1323 }
1324 if (BeginTabItem(tr("Resources"))) {
1325 flags_menu.DrawResourceFlags();
1326 EndTabItem();
1327 }
1328 if (BeginTabItem(tr("System"))) {
1329 flags_menu.DrawSystemFlags();
1330 EndTabItem();
1331 }
1332 EndTabBar();
1333 }
1334
1335 EndChild();
1336
1337 Separator();
1338 if (Button(tr("Close"), ::yaze::gui::kDefaultModalSize)) {
1340 }
1341}
1342
1344 using namespace ImGui;
1345
1346 Text(tr("Data Integrity Check Results"));
1347 Separator();
1348
1349 BeginChild("##IntegrityContent", ImVec2(0, -30), true);
1350
1351 // Placeholder for data integrity results
1352 // In a full implementation, this would show test results
1353 Text(tr("ROM Data Integrity:"));
1354 Separator();
1355 TextColored(gui::GetSuccessColor(), tr("✓ ROM header valid"));
1356 TextColored(gui::GetSuccessColor(), tr("✓ Checksum valid"));
1357 TextColored(gui::GetSuccessColor(), tr("✓ Graphics data intact"));
1358 TextColored(gui::GetSuccessColor(), tr("✓ Map data intact"));
1359
1360 Spacing();
1361 Text(tr("No issues detected."));
1362
1363 EndChild();
1364
1365 Separator();
1366 if (Button(tr("Close"), ::yaze::gui::kDefaultModalSize)) {
1368 }
1369}
1370
1372 using namespace ImGui;
1373
1374 if (!editor_manager_) {
1375 Text(tr("Editor manager unavailable."));
1376 if (Button(tr("Close"), ::yaze::gui::kDefaultModalSize)) {
1378 }
1379 return;
1380 }
1381
1382 const int unloaded = editor_manager_->pending_pot_item_unloaded_rooms();
1383 const int total = editor_manager_->pending_pot_item_total_rooms();
1384
1385 Text(tr("Pot Item Save Confirmation"));
1386 Separator();
1387 TextWrapped(tr("Dungeon pot item saving is enabled, but %d of %d rooms are "
1388 "not loaded."),
1389 unloaded, total);
1390 Spacing();
1391 TextWrapped(
1392 tr("Saving now can overwrite pot items in unloaded rooms. Choose how to "
1393 "proceed:"));
1394
1395 Spacing();
1396 if (Button(tr("Save without pot items"), ImVec2(0, 0))) {
1400 return;
1401 }
1402 SameLine();
1403 if (Button(tr("Save anyway"), ImVec2(0, 0))) {
1407 return;
1408 }
1409 SameLine();
1410 if (Button(tr("Cancel"), ImVec2(0, 0))) {
1414 }
1415}
1416
1418 using namespace ImGui;
1419
1420 if (!editor_manager_) {
1421 Text(tr("Editor manager unavailable."));
1422 if (Button(tr("Close"), ::yaze::gui::kDefaultModalSize)) {
1424 }
1425 return;
1426 }
1427
1429 auto policy = project::RomWritePolicyToString(
1431 const auto expected = editor_manager_->GetProjectExpectedRomHash();
1432 const auto actual = editor_manager_->GetCurrentRomHash();
1433 const auto* project = editor_manager_->GetCurrentProject();
1434 const auto actual_path = editor_manager_->GetCurrentRom()
1436 : std::string();
1437 const auto editable_path =
1438 project && project->hack_manifest.loaded() &&
1439 !project->hack_manifest.build_pipeline().dev_rom.empty()
1440 ? project->GetAbsolutePath(
1441 project->hack_manifest.build_pipeline().dev_rom)
1442 : (project ? project->rom_filename : std::string());
1443
1444 Text(tr("ROM Write Confirmation"));
1445 Separator();
1446 TextWrapped(
1447 tr("The loaded ROM hash does not match the project's expected hash."));
1448 Spacing();
1449 Text(tr("Role: %s"), role.c_str());
1450 Text(tr("Write policy: %s"), policy.c_str());
1451 Text(tr("Loaded ROM: %s"),
1452 actual_path.empty() ? "(unknown)" : actual_path.c_str());
1453 Text(tr("Editable target: %s"),
1454 editable_path.empty() ? "(unset)" : editable_path.c_str());
1455 Text(tr("Expected: %s"), expected.empty() ? "(unset)" : expected.c_str());
1456 Text(tr("Actual: %s"), actual.empty() ? "(unknown)" : actual.c_str());
1457 Spacing();
1458 TextWrapped(tr(
1459 "Proceeding will write to the current ROM file. This may corrupt a base "
1460 "or release ROM if it is not the intended editable project ROM."));
1461
1462 Spacing();
1463 if (Button(tr("Save anyway"), ImVec2(0, 0))) {
1466 auto status = editor_manager_->ResumePendingRomSave();
1467 if (!status.ok() && !absl::IsCancelled(status)) {
1468 if (auto* toast = editor_manager_->toast_manager()) {
1469 toast->Show(absl::StrFormat("Save failed: %s", status.message()),
1471 }
1472 }
1473 return;
1474 }
1475 SameLine();
1476 if (Button(tr("Cancel"), ImVec2(0, 0)) || IsKeyPressed(ImGuiKey_Escape)) {
1479 }
1480}
1481
1483 using namespace ImGui;
1484
1485 if (!editor_manager_) {
1486 Text(tr("Editor manager unavailable."));
1487 if (Button(tr("Close"), ::yaze::gui::kDefaultModalSize)) {
1489 }
1490 return;
1491 }
1492
1493 const auto& conflicts = editor_manager_->pending_write_conflicts();
1494
1495 TextColored(gui::GetWarningColor(), tr("%s Write Conflict Warning"),
1497 Separator();
1498 TextWrapped(
1499 tr("The following ROM addresses are owned by ASM hooks and will be "
1500 "overwritten on next build. Saving now will write data that asar "
1501 "will replace."));
1502 Spacing();
1503
1504 if (!conflicts.empty()) {
1505 if (BeginTable("WriteConflictTable", 3,
1506 ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders |
1507 ImGuiTableFlags_Resizable)) {
1508 TableSetupColumn("Address", ImGuiTableColumnFlags_WidthFixed, 120.0f);
1509 TableSetupColumn("Ownership", ImGuiTableColumnFlags_WidthFixed, 140.0f);
1510 TableSetupColumn("Module", ImGuiTableColumnFlags_WidthStretch);
1511 TableHeadersRow();
1512
1513 for (const auto& conflict : conflicts) {
1514 TableNextRow();
1515 TableNextColumn();
1516 Text("$%06X", conflict.address);
1517 TableNextColumn();
1518 TextUnformatted(
1519 core::AddressOwnershipToString(conflict.ownership).c_str());
1520 TableNextColumn();
1521 if (!conflict.module.empty()) {
1522 TextUnformatted(conflict.module.c_str());
1523 } else {
1524 TextDisabled(tr("(unknown)"));
1525 }
1526 }
1527 EndTable();
1528 }
1529 }
1530
1531 Spacing();
1532 Text(tr("%zu conflict(s) detected."), conflicts.size());
1533 Spacing();
1534
1535 if (Button(tr("Save Anyway"), ImVec2(0, 0))) {
1538 auto status = editor_manager_->ResumePendingRomSave();
1539 if (!status.ok() && !absl::IsCancelled(status)) {
1540 if (auto* toast = editor_manager_->toast_manager()) {
1541 toast->Show(absl::StrFormat("Save failed: %s", status.message()),
1543 }
1544 }
1545 return;
1546 }
1547 SameLine();
1548 if (Button(tr("Cancel"), ImVec2(0, 0)) || IsKeyPressed(ImGuiKey_Escape)) {
1551 }
1552}
1553
1555 using namespace ImGui;
1556
1557 if (!editor_manager_) {
1559 return;
1560 }
1561
1564 return;
1565 }
1566
1567 Text(tr("%s Unsaved Session Changes"), ICON_MD_WARNING);
1568 Separator();
1569 TextWrapped("%s",
1571 Spacing();
1572
1573 const std::string save_label =
1575 if (Button(save_label.c_str(), ::yaze::gui::kDefaultModalSize)) {
1577 return;
1578 }
1579
1580 SameLine();
1581 const std::string continue_label =
1583 if (Button(continue_label.c_str(), ::yaze::gui::kDefaultModalSize)) {
1585 return;
1586 }
1587
1588 SameLine();
1589 if (Button(tr("Cancel"), ::yaze::gui::kDefaultModalSize) ||
1590 IsKeyPressed(ImGuiKey_Escape)) {
1592 }
1593}
1594
1595} // namespace editor
1596} // 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
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()
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: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.