8#include "absl/status/status.h"
9#include "absl/strings/str_format.h"
51 return label.empty() ? nullptr : label.c_str();
56#if !defined(__EMSCRIPTEN__) && !(defined(__APPLE__) && TARGET_OS_IOS == 1)
60 if (ImGui::GetCurrentContext() ==
nullptr) {
63 const ImGuiViewport* viewport = ImGui::GetMainViewport();
64 if (viewport ==
nullptr || viewport->PlatformHandle ==
nullptr) {
67 const auto window_id =
68 static_cast<Uint32
>(
reinterpret_cast<intptr_t
>(viewport->PlatformHandle));
69 return SDL_GetWindowFromID(window_id);
74 if (window ==
nullptr) {
78 return (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) != 0;
80 return (SDL_GetWindowFlags(window) &
81 (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_FULLSCREEN_DESKTOP)) != 0;
87 if (window ==
nullptr) {
91 return SDL_SetWindowFullscreen(window, fullscreen);
93 return SDL_SetWindowFullscreen(
94 window, fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0) == 0;
98constexpr bool kSupportsWindowFullscreen =
false;
99bool IsMainWindowFullscreen() {
102bool SetMainWindowFullscreen(
bool) {
114 : editor_manager_(editor_manager),
115 menu_builder_(menu_builder),
116 rom_manager_(rom_manager),
117 project_manager_(project_manager),
118 editor_registry_(editor_registry),
119 session_coordinator_(session_coordinator),
120 toast_manager_(toast_manager),
121 popup_manager_(popup_manager) {}
144 ImGui::OpenPopup(
"Save Layout Snapshot##menu_orch_save_snapshot");
147 if (ImGui::BeginPopupModal(
"Save Layout Snapshot##menu_orch_save_snapshot",
148 nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
149 ImGui::TextUnformatted(
"Snapshot name:");
150 ImGui::SetNextItemWidth(320.0f);
151 const bool submitted =
154 ImGuiInputTextFlags_EnterReturnsTrue);
157 if ((ImGui::Button(
"Save", ImVec2(120, 0)) || submitted) && has_name) {
162 ImGui::CloseCurrentPopup();
165 if (ImGui::Button(
"Cancel", ImVec2(120, 0))) {
167 ImGui::CloseCurrentPopup();
252 absl::StrFormat(
"switch.%d",
266 if (entries.empty()) {
269 for (
const auto& entry : entries) {
270 const std::string path = entry.path;
271 const std::string label =
272 entry.exists ? entry.label : entry.label +
" (missing)";
273 const bool exists = entry.exists;
276 nullptr, [exists]() {
return exists; });
281 nullptr, [has_entries = !entries.empty()]() {
return has_entries; });
352 const auto names = themes.GetAvailableThemes();
356 for (
const std::string& name : names) {
358 name.c_str(),
nullptr,
361 if (manager.IsPreviewActive()) {
362 manager.EndPreview();
364 manager.ApplyTheme(name);
422 nullptr, []() {
return kSupportsWindowFullscreen; },
423 []() {
return IsMainWindowFullscreen(); })
440 [drawers]() { drawers->ToggleLastDrawer(); },
nullptr,
nullptr,
441 [drawers]() {
return drawers->IsDrawerExpanded(); });
463 absl::StrFormat(
"%s Window Browser",
ICON_MD_APPS).c_str(),
486 if (all_categories.empty()) {
487 ImGui::TextDisabled(
"No windows available");
492 for (
const auto& category : all_categories) {
494 std::string label = category;
495 if (category == active_category) {
501 if (ImGui::BeginMenu(label.c_str())) {
508 ImGui::TextDisabled(
"No windows in this category");
522 for (
const auto& card : cards) {
525 const char* shortcut =
526 card.shortcut_hint.empty() ? nullptr : card.shortcut_hint.c_str();
529 std::string item_label =
536 if (ImGui::MenuItem(item_label.c_str(), shortcut)) {
563#ifdef YAZE_ENABLE_TESTING
595 std::map<std::string, std::vector<WorkflowItem>> grouped_items;
600 for (
const auto& category : categories) {
601 for (
const auto& descriptor :
603 if (descriptor.workflow_group.empty()) {
607 item.id = absl::StrFormat(
"panel.%s", descriptor.card_id);
608 item.group = descriptor.workflow_group;
609 item.label = descriptor.workflow_label.empty()
610 ? descriptor.display_name
611 : descriptor.workflow_label;
613 descriptor.workflow_description.empty()
614 ? absl::StrFormat(
"Open %s", descriptor.display_name)
615 : descriptor.workflow_description;
616 item.shortcut = descriptor.shortcut_hint;
617 item.priority = descriptor.workflow_priority;
618 item.callback = [
this, session_id, panel_id = descriptor.card_id]() {
623 item.enabled = descriptor.enabled_condition;
624 const std::string group = item.group.empty() ?
"General" : item.group;
625 grouped_items[group].push_back(std::move(item));
631 const std::string group = action.group.empty() ?
"General" : action.group;
632 grouped_items[group].push_back(action);
635 if (grouped_items.empty()) {
640 for (
auto& [group, items] : grouped_items) {
641 std::sort(items.begin(), items.end(),
642 [](
const WorkflowItem& lhs,
const WorkflowItem& rhs) {
643 if (lhs.priority != rhs.priority) {
644 return lhs.priority < rhs.priority;
646 return lhs.label < rhs.label;
649 for (
const auto& item : items) {
652 item.label.c_str(), item.callback,
653 item.shortcut.empty() ? nullptr : item.shortcut.c_str(), enabled);
660void MenuOrchestrator::AddRomAnalysisMenuItems() {
664 "ROM Information",
ICON_MD_INFO, [
this]() { OnShowRomInfo(); },
665 nullptr, [
this]() {
return HasActiveRom(); })
668 nullptr, [
this]() {
return HasActiveRom(); })
671 [
this]() { popup_manager_.Show(PopupID::kRomBackups); },
nullptr,
672 [
this]() {
return HasActiveRom(); })
675 nullptr, [
this]() {
return HasActiveRom(); })
679 [
this]() { OnExportBpsPatch(); },
nullptr,
680 [
this]() {
return HasActiveRom(); })
682 "Apply BPS Patch...",
ICON_MD_BUILD, [
this]() { OnApplyBpsPatch(); },
683 nullptr, [
this]() {
return HasActiveRom(); })
687 menu_builder_.BeginSubMenu(
"ZSCustomOverworld",
ICON_MD_CODE)
689 "Check ROM Version",
ICON_MD_INFO, [
this]() { OnCheckRomVersion(); },
690 nullptr, [
this]() {
return HasActiveRom(); })
693 [
this]() { OnOpenOverworldForUpgrade(); },
nullptr,
694 [
this]() {
return HasActiveRom(); })
696 [
this]() { OnToggleCustomLoading(); })
700void MenuOrchestrator::AddAsarIntegrationMenuItems() {
702 menu_builder_.BeginSubMenu(
"Asar Integration",
ICON_MD_BUILD)
704 [
this]() { popup_manager_.Show(PopupID::kAsarIntegration); })
706 "Toggle ASM Patch",
ICON_MD_CODE, [
this]() { OnToggleAsarPatch(); },
707 nullptr, [
this]() {
return HasActiveRom(); })
711void MenuOrchestrator::AddDevelopmentMenuItems() {
715 "Memory Editor",
ICON_MD_MEMORY, [
this]() { OnShowMemoryEditor(); },
716 nullptr, [
this]() {
return HasActiveRom(); })
718 [
this]() { OnShowAssemblyEditor(); })
720 [
this]() { popup_manager_.Show(PopupID::kFeatureFlags); })
722 [
this]() { OnShowPerformanceDashboard(); })
726void MenuOrchestrator::AddTestingMenuItems() {
729#ifdef YAZE_ENABLE_TESTING
735 [
this]() { OnShowTestDashboard(); },
736 GetShortcutForAction(
"Test Dashboard").c_str());
738 menu_builder_.DisabledItem(
739 "Testing support disabled (YAZE_ENABLE_TESTING=OFF)",
ICON_MD_INFO);
741 menu_builder_.EndMenu();
747void MenuOrchestrator::AddSessionsSubmenu() {
748 if (ImGui::BeginMenu(absl::StrFormat(
"%s Sessions",
ICON_MD_TAB).c_str())) {
749 if (ImGui::MenuItem(absl::StrFormat(
"%s New Session",
ICON_MD_ADD).c_str(),
750 ShortcutOrNull(GetShortcutForAction(
"New Session")))) {
751 OnCreateNewSession();
756 nullptr,
false, HasActiveRom())) {
757 OnDuplicateCurrentSession();
761 ShortcutOrNull(GetShortcutForAction(
"Close Session")),
false,
762 HasMultipleSessions())) {
763 OnCloseCurrentSession();
769 ShortcutOrNull(GetShortcutForAction(
"Session Switcher")),
false,
770 HasMultipleSessions())) {
771 OnShowSessionSwitcher();
775 OnShowSessionManager();
783void MenuOrchestrator::AddLayoutSubmenu() {
784 const bool layout_enabled = HasCurrentEditor();
785 auto apply_preset = [
this](
const char* name) {
787 editor_manager_->ApplyLayoutPreset(name);
789 auto apply_profile = [
this](
const char* name) {
791 editor_manager_->ApplyLayoutProfile(name);
794 if (ImGui::BeginMenu(
796 if (ImGui::MenuItem(absl::StrFormat(
"%s Open Layout Designer",
799 nullptr,
false, window_manager_ !=
nullptr)) {
800 OnShowLayoutDesigner();
804 if (ImGui::MenuItem(absl::StrFormat(
"%s Save Layout",
ICON_MD_SAVE).c_str(),
805 ShortcutOrNull(GetShortcutForAction(
"Save Layout")))) {
806 OnSaveWorkspaceLayout();
810 ShortcutOrNull(GetShortcutForAction(
"Load Layout")))) {
811 OnLoadWorkspaceLayout();
815 ShortcutOrNull(GetShortcutForAction(
"Reset Layout")))) {
816 OnResetWorkspaceLayout();
821 nullptr,
false, layout_enabled)) {
823 editor_manager_->ResetCurrentEditorLayout();
826 "Restore Layout Before Default",
nullptr,
false,
827 editor_manager_ && editor_manager_->HasPreviousEditorLayout())) {
828 editor_manager_->RestorePreviousEditorLayout();
832 if (ImGui::BeginMenu(
838 nullptr,
false, layout_enabled)) {
839 save_snapshot_name_buffer_[0] =
'\0';
840 open_save_snapshot_modal_ =
true;
844 std::vector<std::string> named =
845 editor_manager_ ? editor_manager_->ListLayoutSnapshots()
846 : std::vector<std::string>{};
847 if (!named.empty()) {
849 for (
const auto& name : named) {
850 ImGui::PushID(name.c_str());
853 nullptr,
false, layout_enabled)) {
855 editor_manager_->RestoreLayoutSnapshot(name);
857 ImGui::SameLine(ImGui::GetContentRegionAvail().x);
860 editor_manager_->DeleteLayoutSnapshot(name);
871 nullptr,
false, layout_enabled)) {
873 editor_manager_->CaptureTemporaryLayoutSnapshot();
877 nullptr,
false, layout_enabled)) {
879 editor_manager_->RestoreTemporaryLayoutSnapshot();
884 nullptr,
false, layout_enabled)) {
886 editor_manager_->ClearTemporaryLayoutSnapshot();
892 if (ImGui::BeginMenu(
894 if (ImGui::MenuItem(absl::StrFormat(
"%s Code",
ICON_MD_CODE).c_str(),
895 nullptr,
false, layout_enabled)) {
896 apply_profile(
"code");
900 false, layout_enabled)) {
901 apply_profile(
"debug");
903 if (ImGui::MenuItem(absl::StrFormat(
"%s Mapping",
ICON_MD_MAP).c_str(),
904 nullptr,
false, layout_enabled)) {
905 apply_profile(
"mapping");
909 nullptr,
false, layout_enabled)) {
910 apply_profile(
"chat");
915 nullptr,
false, layout_enabled)) {
916 apply_preset(
"Minimal");
920 nullptr,
false, layout_enabled)) {
921 OnLoadDeveloperLayout();
925 nullptr,
false, layout_enabled)) {
926 OnLoadDesignerLayout();
928 if (ImGui::MenuItem(absl::StrFormat(
"%s Modder",
ICON_MD_BUILD).c_str(),
929 nullptr,
false, layout_enabled)) {
930 OnLoadModderLayout();
933 absl::StrFormat(
"%s Overworld Expert",
ICON_MD_MAP).c_str(),
934 nullptr,
false, layout_enabled)) {
935 apply_preset(
"Overworld Expert");
939 nullptr,
false, layout_enabled)) {
940 apply_preset(
"Dungeon Expert");
944 false, layout_enabled)) {
945 apply_preset(
"Testing");
949 false, layout_enabled)) {
950 apply_preset(
"Audio");
954 absl::StrFormat(
"%s Manage Presets...",
ICON_MD_TUNE).c_str())) {
955 OnShowLayoutPresets();
965void MenuOrchestrator::AddSidebarSubmenu() {
966 if (!user_settings_) {
970 if (!ImGui::BeginMenu(
975 auto persist = [
this]() {
976 (void)user_settings_->Save();
978 auto& prefs = user_settings_->prefs();
982 nullptr,
false, !prefs.sidebar_order.empty())) {
983 prefs.sidebar_order.clear();
988 nullptr,
false, !prefs.sidebar_hidden.empty())) {
989 prefs.sidebar_hidden.clear();
995 const size_t session_id = session_coordinator_.GetActiveSessionId();
996 std::vector<std::string> categories;
997 if (window_manager_) {
998 categories = window_manager_->GetAllCategories(session_id);
1002 std::vector<std::string> pinned_list;
1003 std::vector<std::string> hidden_list;
1004 for (
const auto& cat : categories) {
1005 if (cat == WorkspaceWindowManager::kDashboardCategory)
1007 if (prefs.sidebar_pinned.count(cat))
1008 pinned_list.push_back(cat);
1009 if (prefs.sidebar_hidden.count(cat))
1010 hidden_list.push_back(cat);
1013 if (ImGui::BeginMenu(
1015 if (pinned_list.empty()) {
1016 ImGui::TextDisabled(
"(none)");
1018 for (
const auto& cat : pinned_list) {
1019 ImGui::PushID(cat.c_str());
1020 if (ImGui::MenuItem(
1021 absl::StrFormat(
"%s Unpin %s",
ICON_MD_CLOSE, cat).c_str())) {
1022 prefs.sidebar_pinned.erase(cat);
1031 if (ImGui::BeginMenu(
1033 if (hidden_list.empty()) {
1034 ImGui::TextDisabled(
"(none)");
1036 for (
const auto& cat : hidden_list) {
1037 ImGui::PushID(cat.c_str());
1038 if (ImGui::MenuItem(
1041 prefs.sidebar_hidden.erase(cat);
1054 if (ImGui::BeginMenu(absl::StrFormat(
"%s Customize",
ICON_MD_TUNE).c_str())) {
1055 if (categories.empty()) {
1056 ImGui::TextDisabled(
"No categories available");
1058 for (
const auto& cat : categories) {
1059 if (cat == WorkspaceWindowManager::kDashboardCategory)
1061 ImGui::PushID(cat.c_str());
1062 const bool pinned = prefs.sidebar_pinned.count(cat) > 0;
1063 const bool hidden = prefs.sidebar_hidden.count(cat) > 0;
1064 if (ImGui::BeginMenu(cat.c_str())) {
1065 if (ImGui::MenuItem(pinned ?
"Unpin from Top" :
"Pin to Top",
nullptr,
1068 prefs.sidebar_pinned.erase(cat);
1070 prefs.sidebar_pinned.insert(cat);
1074 if (ImGui::MenuItem(hidden ?
"Show on Sidebar" :
"Hide from Sidebar",
1077 prefs.sidebar_hidden.erase(cat);
1079 prefs.sidebar_hidden.insert(cat);
1094void MenuOrchestrator::BuildHelpMenu() {
1095 menu_builder_.BeginMenu(
"Help");
1097 menu_builder_.EndMenu();
1100void MenuOrchestrator::AddHelpMenuItems() {
1104 [
this]() { OnShowGettingStarted(); })
1106 [
this]() { OnShowComingFromZScream(); })
1110 if (window_manager_) {
1111 window_manager_->TriggerShowShortcuts();
1114 GetShortcutForAction(
"Keyboard Shortcuts").c_str())
1116 [
this]() { OnShowBuildInstructions(); })
1120 [
this]() { OnShowSupportedFeatures(); })
1124 [
this]() { OnShowTroubleshooting(); })
1126 [
this]() { OnShowContributing(); })
1130 GetShortcutForAction(
"Show About").c_str());
1132 menu_builder_.Separator();
1134 for (
const std::string& locale :
1137 locale.c_str(),
nullptr,
1144 menu_builder_.EndMenu();
1148void MenuOrchestrator::ClearMenu() {
1149 menu_builder_.Clear();
1152void MenuOrchestrator::RefreshMenu() {
1153 menu_needs_refresh_ =
true;
1157void MenuOrchestrator::OnOpenRom() {
1159 if (editor_manager_) {
1160 auto status = editor_manager_->LoadRom();
1162 toast_manager_.Show(
1163 absl::StrFormat(
"Failed to load ROM: %s", status.message()),
1169void MenuOrchestrator::OnSaveRom() {
1171 if (editor_manager_) {
1172 auto status = editor_manager_->SaveRom();
1174 if (absl::IsCancelled(status)) {
1177 toast_manager_.Show(
1178 absl::StrFormat(
"Failed to save ROM: %s", status.message()),
1184void MenuOrchestrator::OnSaveRomAs() {
1185 popup_manager_.Show(PopupID::kSaveAs);
1188void MenuOrchestrator::OnCreateProject() {
1190 if (editor_manager_) {
1191 auto status = editor_manager_->CreateNewProject();
1193 toast_manager_.Show(
1194 absl::StrFormat(
"Failed to create project: %s", status.message()),
1200void MenuOrchestrator::OnOpenProject() {
1202 if (editor_manager_) {
1203 auto status = editor_manager_->OpenProject();
1205 toast_manager_.Show(
1206 absl::StrFormat(
"Failed to open project: %s", status.message()),
1212void MenuOrchestrator::OnSaveProject() {
1214 if (editor_manager_) {
1215 auto status = editor_manager_->SaveProject();
1217 toast_manager_.Show(
1218 absl::StrFormat(
"Failed to save project: %s", status.message()),
1221 toast_manager_.Show(
"Project saved successfully", ToastType::kSuccess);
1226void MenuOrchestrator::OnSaveProjectAs() {
1228 if (editor_manager_) {
1229 auto status = editor_manager_->SaveProjectAs();
1231 toast_manager_.Show(
1232 absl::StrFormat(
"Failed to save project as: %s", status.message()),
1238void MenuOrchestrator::OnShowProjectManagement() {
1240 if (editor_manager_) {
1241 editor_manager_->ShowProjectManagement();
1245void MenuOrchestrator::OnShowProjectFileEditor() {
1247 if (editor_manager_) {
1248 editor_manager_->ShowProjectFileEditor();
1252void MenuOrchestrator::OnOpenRecentFile(
const std::string& path) {
1253 if (!editor_manager_) {
1257 auto status = editor_manager_->OpenRomOrProject(path);
1259 toast_manager_.Show(
1260 absl::StrFormat(
"Failed to open %s: %s", path, status.message()),
1265void MenuOrchestrator::OnClearRecentFiles() {
1271void MenuOrchestrator::OnCloseRom() {
1272 if (editor_manager_) {
1273 editor_manager_->CloseRom();
1277void MenuOrchestrator::OnRevertRom() {
1278 if (!editor_manager_) {
1281 auto status = editor_manager_->RevertRomToSaved();
1283 toast_manager_.Show(absl::StrFormat(
"Revert failed: %s", status.message()),
1284 ToastType::kError, 6.0f);
1289void MenuOrchestrator::OnUndo() {
1290 if (editor_manager_) {
1291 auto* current_editor = editor_manager_->GetCurrentEditor();
1292 if (current_editor) {
1295 auto status = current_editor->
Undo();
1297 if (!desc.empty()) {
1298 toast_manager_.Show(absl::StrFormat(
"Undid: %s", desc),
1299 ToastType::kInfo, 2.0f);
1302 toast_manager_.Show(
1303 absl::StrFormat(
"Undo failed: %s", status.message()),
1310void MenuOrchestrator::OnRedo() {
1311 if (editor_manager_) {
1312 auto* current_editor = editor_manager_->GetCurrentEditor();
1313 if (current_editor) {
1316 auto status = current_editor->
Redo();
1318 if (!desc.empty()) {
1319 toast_manager_.Show(absl::StrFormat(
"Redid: %s", desc),
1320 ToastType::kInfo, 2.0f);
1323 toast_manager_.Show(
1324 absl::StrFormat(
"Redo failed: %s", status.message()),
1331void MenuOrchestrator::OnCut() {
1332 if (!editor_manager_) {
1335 auto* current_editor = editor_manager_->GetCurrentEditor();
1336 if (!current_editor) {
1339 auto status = current_editor->
Cut();
1343 if (absl::IsUnimplemented(status)) {
1346 toast_manager_.Show(
"Cut is not available in this editor", ToastType::kInfo,
1350 toast_manager_.Show(absl::StrFormat(
"Cut failed: %s", status.message()),
1354void MenuOrchestrator::OnCopy() {
1355 if (!editor_manager_) {
1358 auto* current_editor = editor_manager_->GetCurrentEditor();
1359 if (!current_editor) {
1362 auto status = current_editor->
Copy();
1366 if (absl::IsUnimplemented(status)) {
1369 toast_manager_.Show(
"Copy is not available in this editor",
1370 ToastType::kInfo, 2.0f);
1373 toast_manager_.Show(absl::StrFormat(
"Copy failed: %s", status.message()),
1377void MenuOrchestrator::OnPaste() {
1378 if (!editor_manager_) {
1381 auto* current_editor = editor_manager_->GetCurrentEditor();
1382 if (!current_editor) {
1385 auto status = current_editor->
Paste();
1389 if (absl::IsUnimplemented(status)) {
1392 toast_manager_.Show(
"Paste is not available in this editor",
1393 ToastType::kInfo, 2.0f);
1396 toast_manager_.Show(absl::StrFormat(
"Paste failed: %s", status.message()),
1400void MenuOrchestrator::OnFind() {
1401 if (!editor_manager_) {
1404 auto* current_editor = editor_manager_->GetCurrentEditor();
1405 if (!current_editor) {
1408 auto status = current_editor->
Find();
1412 if (absl::IsUnimplemented(status)) {
1415 toast_manager_.Show(
"Find is not available in this editor",
1416 ToastType::kInfo, 2.0f);
1419 toast_manager_.Show(absl::StrFormat(
"Find failed: %s", status.message()),
1426 if (editor_manager_) {
1427 editor_manager_->SwitchToEditor(editor_type);
1431void MenuOrchestrator::OnShowEditorSelection() {
1433 if (editor_manager_) {
1434 if (
auto* ui = editor_manager_->ui_coordinator()) {
1435 ui->ShowEditorSelection();
1440void MenuOrchestrator::OnShowDisplaySettings() {
1441 popup_manager_.Show(PopupID::kDisplaySettings);
1448 const std::string& action) {
1449 if (manager ==
nullptr) {
1453 if (shortcut ==
nullptr || !shortcut->
callback) {
1466void MenuOrchestrator::OnZoomIn() {
1467 if (RunShortcutAction(shortcut_manager_,
"ui.font_scale_increase") ||
1472 editor_manager_->user_settings().prefs().font_global_scale +
1474 editor_manager_->SetFontGlobalScale(
1475 std::clamp(scale, kMenuFontScaleMin, kMenuFontScaleMax));
1478void MenuOrchestrator::OnZoomOut() {
1479 if (RunShortcutAction(shortcut_manager_,
"ui.font_scale_decrease") ||
1484 editor_manager_->user_settings().prefs().font_global_scale -
1486 editor_manager_->SetFontGlobalScale(
1487 std::clamp(scale, kMenuFontScaleMin, kMenuFontScaleMax));
1490void MenuOrchestrator::OnZoomReset() {
1491 if (RunShortcutAction(shortcut_manager_,
"ui.font_scale_reset") ||
1495 editor_manager_->SetFontGlobalScale(1.0f);
1498void MenuOrchestrator::OnToggleFullscreen() {
1499 if (!SetMainWindowFullscreen(!IsMainWindowFullscreen())) {
1500 toast_manager_.Show(
"Fullscreen is not available for this window",
1501 ToastType::kWarning);
1505void MenuOrchestrator::OnShowHexEditor() {
1507 if (editor_manager_) {
1508 editor_manager_->window_manager().OpenWindow(
1509 editor_manager_->GetCurrentSessionId(),
"Hex Editor");
1513void MenuOrchestrator::OnShowPanelBrowser() {
1514 if (editor_manager_) {
1515 if (
auto* ui = editor_manager_->ui_coordinator()) {
1516 ui->SetWindowBrowserVisible(
true);
1521void MenuOrchestrator::OnShowPanelFinder() {
1522 if (editor_manager_) {
1523 if (
auto* ui = editor_manager_->ui_coordinator()) {
1524 ui->ShowPanelFinder();
1529void MenuOrchestrator::OnShowWelcomeScreen() {
1530 if (editor_manager_) {
1531 if (
auto* ui = editor_manager_->ui_coordinator()) {
1532 ui->SetWelcomeScreenVisible(
true);
1537#ifdef YAZE_BUILD_AGENT_UI
1538void MenuOrchestrator::OnShowAIAgent() {
1539 if (editor_manager_) {
1540 if (
auto* ui = editor_manager_->ui_coordinator()) {
1541 ui->SetAIAgentVisible(
true);
1546void MenuOrchestrator::OnShowProposalDrawer() {
1547 if (editor_manager_) {
1548 if (
auto* ui = editor_manager_->ui_coordinator()) {
1549 ui->SetProposalDrawerVisible(
true);
1556void MenuOrchestrator::OnCreateNewSession() {
1557 session_coordinator_.CreateNewSession();
1560void MenuOrchestrator::OnDuplicateCurrentSession() {
1561 session_coordinator_.DuplicateCurrentSession();
1564void MenuOrchestrator::OnCloseCurrentSession() {
1565 if (editor_manager_) {
1566 editor_manager_->CloseCurrentSession();
1568 session_coordinator_.CloseCurrentSession();
1572void MenuOrchestrator::OnShowSessionSwitcher() {
1574 if (editor_manager_) {
1575 if (
auto* ui = editor_manager_->ui_coordinator()) {
1576 ui->ShowSessionSwitcher();
1581void MenuOrchestrator::OnShowSessionManager() {
1582 popup_manager_.Show(PopupID::kSessionManager);
1586void MenuOrchestrator::OnShowAllWindows() {
1588 if (editor_manager_) {
1589 if (
auto* ui = editor_manager_->ui_coordinator()) {
1590 ui->ShowAllWindows();
1595void MenuOrchestrator::OnHideAllWindows() {
1597 if (editor_manager_) {
1598 editor_manager_->HideAllWindows();
1602void MenuOrchestrator::OnResetWorkspaceLayout() {
1604 if (editor_manager_) {
1605 editor_manager_->QueueDeferredAction([
this]() {
1606 editor_manager_->ResetWorkspaceLayout();
1607 toast_manager_.Show(
"Layout reset to default", ToastType::kInfo);
1612void MenuOrchestrator::OnSaveWorkspaceLayout() {
1614 if (editor_manager_) {
1615 editor_manager_->SaveWorkspaceLayout();
1619void MenuOrchestrator::OnLoadWorkspaceLayout() {
1621 if (editor_manager_) {
1622 editor_manager_->LoadWorkspaceLayout();
1626void MenuOrchestrator::OnShowLayoutPresets() {
1627 popup_manager_.Show(PopupID::kLayoutPresets);
1630void MenuOrchestrator::OnShowLayoutDesigner() {
1632 if (manager ==
nullptr && editor_manager_ !=
nullptr) {
1633 manager = &editor_manager_->window_manager();
1635 if (manager ==
nullptr) {
1636 toast_manager_.Show(
"Layout Designer unavailable: no window manager",
1644 WorkspaceWindowManager::kDashboardCategory) {
1648 const bool already_open =
1649 manager->
IsWindowOpen(session_id, kLayoutDesignerWindowId);
1650 if (!already_open &&
1651 !manager->
OpenWindow(session_id, kLayoutDesignerWindowId)) {
1652 toast_manager_.Show(
1653 "Layout Designer is not registered in the active session",
1664void MenuOrchestrator::OnLoadDeveloperLayout() {
1665 if (editor_manager_) {
1666 editor_manager_->ApplyLayoutPreset(
"Developer");
1670void MenuOrchestrator::OnLoadDesignerLayout() {
1671 if (editor_manager_) {
1672 editor_manager_->ApplyLayoutPreset(
"Designer");
1676void MenuOrchestrator::OnLoadModderLayout() {
1677 if (editor_manager_) {
1678 editor_manager_->ApplyLayoutPreset(
"Modder");
1683void MenuOrchestrator::OnShowGlobalSearch() {
1684 if (editor_manager_) {
1685 if (
auto* ui = editor_manager_->ui_coordinator()) {
1686 ui->ShowGlobalSearch();
1691void MenuOrchestrator::OnShowCommandPalette() {
1692 if (editor_manager_) {
1693 if (
auto* ui = editor_manager_->ui_coordinator()) {
1694 ui->ShowCommandPalette();
1699void MenuOrchestrator::OnShowPerformanceDashboard() {
1700 if (editor_manager_) {
1701 if (
auto* ui = editor_manager_->ui_coordinator()) {
1702 ui->SetPerformanceDashboardVisible(
true);
1707void MenuOrchestrator::OnShowImGuiDemo() {
1708 if (editor_manager_) {
1709 editor_manager_->ShowImGuiDemo();
1713void MenuOrchestrator::OnShowImGuiMetrics() {
1714 if (editor_manager_) {
1715 editor_manager_->ShowImGuiMetrics();
1719void MenuOrchestrator::OnShowMemoryEditor() {
1720 if (editor_manager_) {
1721 editor_manager_->window_manager().OpenWindow(
1722 editor_manager_->GetCurrentSessionId(),
"Memory Editor");
1726void MenuOrchestrator::OnShowResourceLabelManager() {
1727 if (editor_manager_) {
1728 if (
auto* ui = editor_manager_->ui_coordinator()) {
1729 ui->SetResourceLabelManagerVisible(
true);
1734#ifdef YAZE_ENABLE_TESTING
1735void MenuOrchestrator::OnShowTestDashboard() {
1736 if (editor_manager_) {
1737 editor_manager_->ShowTestDashboard();
1744void MenuOrchestrator::OnShowAbout() {
1745 popup_manager_.Show(PopupID::kAbout);
1748void MenuOrchestrator::OnShowGettingStarted() {
1749 popup_manager_.Show(PopupID::kGettingStarted);
1752void MenuOrchestrator::OnShowComingFromZScream() {
1753 popup_manager_.Show(PopupID::kComingFromZScream);
1756void MenuOrchestrator::OnShowBuildInstructions() {
1757 popup_manager_.Show(PopupID::kBuildInstructions);
1760void MenuOrchestrator::OnShowCLIUsage() {
1761 popup_manager_.Show(PopupID::kCLIUsage);
1764void MenuOrchestrator::OnShowTroubleshooting() {
1765 popup_manager_.Show(PopupID::kTroubleshooting);
1768void MenuOrchestrator::OnShowContributing() {
1769 popup_manager_.Show(PopupID::kContributing);
1772void MenuOrchestrator::OnShowWhatsNew() {
1773 popup_manager_.Show(PopupID::kWhatsNew);
1776void MenuOrchestrator::OnShowSupportedFeatures() {
1777 popup_manager_.Show(PopupID::kSupportedFeatures);
1781void MenuOrchestrator::OnShowRomInfo() {
1782 popup_manager_.Show(PopupID::kRomInfo);
1785void MenuOrchestrator::OnCreateBackup() {
1786 if (editor_manager_) {
1787 auto status = rom_manager_.CreateBackup(editor_manager_->GetCurrentRom());
1789 toast_manager_.Show(
"Backup created successfully", ToastType::kSuccess);
1791 toast_manager_.Show(
1792 absl::StrFormat(
"Backup failed: %s", status.message()),
1798void MenuOrchestrator::OnValidateRom() {
1799 if (editor_manager_) {
1800 auto status = rom_manager_.ValidateRom(editor_manager_->GetCurrentRom());
1802 toast_manager_.Show(
"ROM validation passed", ToastType::kSuccess);
1804 toast_manager_.Show(
1805 absl::StrFormat(
"ROM validation failed: %s", status.message()),
1811void MenuOrchestrator::OnShowSettings() {
1813 if (editor_manager_) {
1814 editor_manager_->SwitchToEditor(EditorType::kSettings);
1818void MenuOrchestrator::OnQuit() {
1819 if (editor_manager_) {
1820 editor_manager_->Quit();
1825bool MenuOrchestrator::CanSaveRom()
const {
1826 auto* rom = editor_manager_ ? editor_manager_->GetCurrentRom() :
nullptr;
1827 return rom ? rom_manager_.IsRomLoaded(rom) :
false;
1830bool MenuOrchestrator::CanSaveProject()
const {
1832 return HasProjectFile() &&
1833 editor_manager_->IsCurrentProjectContextOwnedBySession(
1834 editor_manager_->GetCurrentSessionId());
1837bool MenuOrchestrator::HasActiveRom()
const {
1838 auto* rom = editor_manager_ ? editor_manager_->GetCurrentRom() :
nullptr;
1839 return rom ? rom_manager_.IsRomLoaded(rom) :
false;
1842bool MenuOrchestrator::HasActiveProject()
const {
1843 return project_manager_.HasActiveProject();
1846bool MenuOrchestrator::HasProjectFile()
const {
1849 const auto* project =
1850 editor_manager_ ? editor_manager_->GetCurrentProject() :
nullptr;
1851 return project && !project->filepath.empty();
1854bool MenuOrchestrator::HasCurrentEditor()
const {
1855 return editor_manager_ && editor_manager_->GetCurrentEditor() !=
nullptr;
1858bool MenuOrchestrator::HasMultipleSessions()
const {
1859 return session_coordinator_.HasMultipleSessions();
1862bool MenuOrchestrator::CurrentEditorCanUndo()
const {
1864 editor_manager_ ? editor_manager_->GetCurrentEditor() :
nullptr;
1865 return editor !=
nullptr && editor->
CanUndo();
1868bool MenuOrchestrator::CurrentEditorCanRedo()
const {
1870 editor_manager_ ? editor_manager_->GetCurrentEditor() :
nullptr;
1871 return editor !=
nullptr && editor->
CanRedo();
1874bool MenuOrchestrator::CurrentEditorCanCut()
const {
1876 editor_manager_ ? editor_manager_->GetCurrentEditor() :
nullptr;
1877 return editor !=
nullptr && editor->
CanCut();
1880bool MenuOrchestrator::CurrentEditorCanCopy()
const {
1882 editor_manager_ ? editor_manager_->GetCurrentEditor() :
nullptr;
1883 return editor !=
nullptr && editor->
CanCopy();
1886bool MenuOrchestrator::CurrentEditorCanPaste()
const {
1888 editor_manager_ ? editor_manager_->GetCurrentEditor() :
nullptr;
1889 return editor !=
nullptr && editor->
CanPaste();
1892bool MenuOrchestrator::CurrentEditorCanFind()
const {
1894 editor_manager_ ? editor_manager_->GetCurrentEditor() :
nullptr;
1895 return editor !=
nullptr && editor->
CanFind();
1899std::string MenuOrchestrator::GetRomFilename()
const {
1900 auto* rom = editor_manager_ ? editor_manager_->GetCurrentRom() :
nullptr;
1901 return rom ? rom_manager_.GetRomFilename(rom) :
"";
1904std::string MenuOrchestrator::GetProjectName()
const {
1905 return project_manager_.GetProjectName();
1908std::string MenuOrchestrator::GetCurrentEditorName()
const {
1910 return "Unknown Editor";
1914std::string MenuOrchestrator::GetShortcutForAction(
1915 const std::string& action)
const {
1923void MenuOrchestrator::OnCheckRomVersion() {
1924 if (!editor_manager_)
1926 auto* rom = editor_manager_->GetCurrentRom();
1932 std::string version_str =
1933 (version == 0xFF) ?
"Vanilla" : absl::StrFormat(
"v%d", version);
1935 toast_manager_.Show(
1936 absl::StrFormat(
"ROM: %s | ZSCustomOverworld: %s", rom->
title().c_str(),
1937 version_str.c_str()),
1938 ToastType::kInfo, 5.0f);
1941void MenuOrchestrator::OnOpenOverworldForUpgrade() {
1944 if (!editor_manager_ || !HasActiveRom())
1946 editor_manager_->SwitchToEditor(EditorType::kOverworld);
1947 toast_manager_.Show(
1948 "Use the Upgrade button in the Overworld toolbar to apply "
1949 "ZSCustomOverworld",
1950 ToastType::kInfo, 4.0f);
1953void MenuOrchestrator::OnToggleCustomLoading() {
1955 flags.overworld.kLoadCustomOverworld = !flags.overworld.kLoadCustomOverworld;
1957 toast_manager_.Show(
1959 "Custom Overworld Loading: %s",
1960 flags.overworld.kLoadCustomOverworld ?
"Enabled" :
"Disabled"),
1964void MenuOrchestrator::OnToggleAsarPatch() {
1965 if (!editor_manager_)
1967 auto* rom = editor_manager_->GetCurrentRom();
1972 flags.overworld.kApplyZSCustomOverworldASM =
1973 !flags.overworld.kApplyZSCustomOverworldASM;
1975 toast_manager_.Show(
1977 "ZSCustomOverworld ASM Application: %s",
1978 flags.overworld.kApplyZSCustomOverworldASM ?
"Enabled" :
"Disabled"),
1982void MenuOrchestrator::OnShowAssemblyEditor() {
1983 if (editor_manager_) {
1984 editor_manager_->SwitchToEditor(EditorType::kAssembly);
1988void MenuOrchestrator::OnExportBpsPatch() {
1989 if (!editor_manager_)
1991 auto* rom = editor_manager_->GetCurrentRom();
1997 std::string original_path =
1999 if (original_path.empty()) {
2005 auto load_status = original_rom.
LoadFromFile(original_path);
2006 if (!load_status.ok()) {
2007 toast_manager_.Show(absl::StrFormat(
"Failed to load original ROM: %s",
2008 load_status.message()),
2014 std::vector<uint8_t> patch_data;
2015 auto create_status =
2017 if (!create_status.ok()) {
2018 toast_manager_.Show(absl::StrFormat(
"Failed to create BPS patch: %s",
2019 create_status.message()),
2025 std::string default_name = rom->
short_name() +
".bps";
2026 std::string save_path =
2028 if (save_path.empty()) {
2033 if (save_path.size() < 4 ||
2034 save_path.substr(save_path.size() - 4) !=
".bps") {
2035 save_path +=
".bps";
2039 std::ofstream file(save_path, std::ios::binary);
2040 if (!file.is_open()) {
2041 toast_manager_.Show(
2042 absl::StrFormat(
"Failed to open file for writing: %s", save_path),
2047 file.write(
reinterpret_cast<const char*
>(patch_data.data()),
2052 toast_manager_.Show(
2053 absl::StrFormat(
"Failed to write patch file: %s", save_path),
2058 toast_manager_.Show(absl::StrFormat(
"BPS patch exported: %s (%zu bytes)",
2059 save_path, patch_data.size()),
2060 ToastType::kSuccess);
2063void MenuOrchestrator::OnApplyBpsPatch() {
2064 if (!editor_manager_)
2066 auto* rom = editor_manager_->GetCurrentRom();
2072 options.
filters.push_back({
"BPS Patch",
"bps"});
2074 if (patch_path.empty()) {
2079 std::ifstream file(patch_path, std::ios::binary);
2080 if (!file.is_open()) {
2081 toast_manager_.Show(
2082 absl::StrFormat(
"Failed to open patch file: %s", patch_path),
2087 std::vector<uint8_t> patch_data((std::istreambuf_iterator<char>(file)),
2088 std::istreambuf_iterator<char>());
2091 if (patch_data.empty()) {
2092 toast_manager_.Show(
"Patch file is empty", ToastType::kError);
2097 std::vector<uint8_t> patched_rom;
2100 if (!apply_status.ok()) {
2101 toast_manager_.Show(absl::StrFormat(
"Failed to apply BPS patch: %s",
2102 apply_status.message()),
2109 if (!load_status.ok()) {
2110 toast_manager_.Show(absl::StrFormat(
"Failed to load patched ROM data: %s",
2111 load_status.message()),
2117 toast_manager_.Show(
"BPS patch applied successfully", ToastType::kSuccess);
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
absl::Status LoadFromFile(const std::string &filename, const LoadOptions &options=LoadOptions::Defaults())
void set_dirty(bool dirty)
const auto & vector() const
absl::Status LoadFromData(const std::vector< uint8_t > &data, const LoadOptions &options=LoadOptions::Defaults())
The EditorManager controls the main editor window and manages the various editor classes.
bool SaveLayoutSnapshotAs(const std::string &name)
bool CanRevertRom() const
RightDrawerManager * right_drawer_manager()
FeatureHintManager * feature_hint_manager()
Manages editor types, categories, and lifecycle.
Interface for editor classes.
virtual absl::Status Cut()=0
virtual absl::Status Copy()=0
virtual absl::Status Redo()=0
virtual std::string GetRedoDescription() const
virtual bool CanFind() const
virtual bool CanRedo() const
virtual bool CanCut() const
virtual absl::Status Find()=0
virtual bool CanUndo() const
virtual bool CanPaste() const
virtual absl::Status Paste()=0
virtual absl::Status Undo()=0
virtual bool CanCopy() const
virtual std::string GetUndoDescription() const
bool MaybeShow(std::string_view id)
Handles all project file operations with ROM-first workflow.
Handles all ROM file I/O operations.
High-level orchestrator for multi-session UI.
size_t GetActiveSessionId() const
Stable workspace identity that is never reused while this coordinator lives.
const Shortcut * FindShortcut(const std::string &name) const
void SetEnabled(bool enabled)
Enable or disable the status bar.
Central registry for all editor cards with session awareness and dependency injection.
void HideAllWindowsInCategory(size_t session_id, const std::string &category)
size_t GetActiveSessionId() const
std::vector< WindowDescriptor > GetWindowsInCategory(size_t session_id, const std::string &category) const
void ToggleSidebarVisibility()
void SetWindowPinned(size_t session_id, const std::string &base_window_id, bool pinned)
void SetActiveCategory(const std::string &category, bool notify=true)
std::string GetActiveCategory() const
bool IsWindowOpen(size_t session_id, const std::string &base_window_id) const
bool OpenWindow(size_t session_id, const std::string &base_window_id)
std::vector< std::string > GetAllCategories(size_t session_id) const
void HideAllWindowsInSession(size_t session_id)
void ShowAllWindowsInSession(size_t session_id)
bool ToggleWindow(size_t session_id, const std::string &base_window_id)
bool IsSidebarVisible() const
void ShowAllWindowsInCategory(size_t session_id, const std::string &category)
static ThemeManager & Get()
const std::string & GetCurrentThemeName() const
std::vector< std::string > GetAvailableLocales() const
void SetLanguage(const std::string &locale)
static LanguageManager & Get()
const std::string & GetCurrentLocale() const
static RecentFilesManager & GetInstance()
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...
#define ICON_MD_DEVELOPER_MODE
#define ICON_MD_FOLDER_OPEN
#define ICON_MD_CONTENT_CUT
#define ICON_MD_VOLUNTEER_ACTIVISM
#define ICON_MD_FILE_OPEN
#define ICON_MD_CHECK_BOX
#define ICON_MD_EXIT_TO_APP
#define ICON_MD_VIEW_QUILT
#define ICON_MD_WORKSPACE_PREMIUM
#define ICON_MD_BOOKMARKS
#define ICON_MD_FOLDER_SPECIAL
#define ICON_MD_VIEW_LIST
#define ICON_MD_NEW_RELEASES
#define ICON_MD_PLAY_ARROW
#define ICON_MD_SWAP_HORIZ
#define ICON_MD_DESIGN_SERVICES
#define ICON_MD_COMPARE_ARROWS
#define ICON_MD_DIFFERENCE
#define ICON_MD_SWITCH_ACCOUNT
#define ICON_MD_VISIBILITY
#define ICON_MD_BUG_REPORT
#define ICON_MD_BUILD_CIRCLE
#define ICON_MD_MUSIC_NOTE
#define ICON_MD_CONTENT_PASTE
#define ICON_MD_FULLSCREEN
#define ICON_MD_VISIBILITY_OFF
#define ICON_MD_DISPLAY_SETTINGS
#define ICON_MD_BOOKMARK_ADD
#define ICON_MD_VIEW_COMPACT
#define ICON_MD_DASHBOARD_CUSTOMIZE
#define ICON_MD_CHECK_CIRCLE
#define ICON_MD_DESCRIPTION
#define ICON_MD_HORIZONTAL_RULE
#define ICON_MD_CREATE_NEW_FOLDER
#define ICON_MD_DASHBOARD
#define ICON_MD_ZOOM_OUT_MAP
#define ICON_MD_CONTENT_COPY
#define ICON_MD_ANALYTICS
#define ICON_MD_RESTART_ALT
#define ICON_MD_CHECK_BOX_OUTLINE_BLANK
#define ICON_MD_VIEW_SIDEBAR
#define ICON_MD_BOOKMARK_REMOVE
#define ICON_MD_SMART_TOY
#define ICON_MD_DELETE_SWEEP
std::vector< ActionDef > GetAll()
constexpr const char * kZScreamSave
std::vector< RecentFileMenuEntry > BuildRecentFileMenuEntries(const std::vector< std::string > &recent_paths, size_t max_entries, const RecentFileExistsFn &exists)
std::string ShortcutLabelForAction(const ShortcutManager *shortcut_manager, absl::string_view action)
absl::Status ApplyBpsPatch(const std::vector< uint8_t > &source, const std::vector< uint8_t > &patch, std::vector< uint8_t > &output)
absl::Status CreateBpsPatch(const std::vector< uint8_t > &source, const std::vector< uint8_t > &target, std::vector< uint8_t > &patch)
FileDialogOptions MakeRomFileDialogOptions(bool include_all_files)
constexpr int OverworldCustomASMHasBeenApplied
SDL2/SDL3 compatibility layer.
std::function< void()> callback
Metadata for a dockable editor window (formerly PanelInfo)
bool IsListedInWindowBrowser() const
std::vector< FileDialogFilter > filters