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) {}
145 ImGui::OpenPopup(
"Save Layout Snapshot##menu_orch_save_snapshot");
148 if (ImGui::BeginPopupModal(
"Save Layout Snapshot##menu_orch_save_snapshot",
149 nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
150 ImGui::TextUnformatted(
"Snapshot name:");
151 ImGui::SetNextItemWidth(320.0f);
152 const bool submitted =
155 ImGuiInputTextFlags_EnterReturnsTrue);
158 if ((ImGui::Button(
"Save", ImVec2(120, 0)) || submitted) && has_name) {
163 ImGui::CloseCurrentPopup();
166 if (ImGui::Button(
"Cancel", ImVec2(120, 0))) {
168 ImGui::CloseCurrentPopup();
249 (project->IsOracleCompatible() ||
250 !project->lyra_terrain_file.empty());
270 absl::StrFormat(
"switch.%d",
284 if (entries.empty()) {
287 for (
const auto& entry : entries) {
288 const std::string path = entry.path;
289 const std::string label =
290 entry.exists ? entry.label : entry.label +
" (missing)";
291 const bool exists = entry.exists;
294 nullptr, [exists]() {
return exists; });
299 nullptr, [has_entries = !entries.empty()]() {
return has_entries; });
347 editor->DrawEditMenuItems();
377 const auto names = themes.GetAvailableThemes();
381 for (
const std::string& name : names) {
383 name.c_str(),
nullptr,
386 if (manager.IsPreviewActive()) {
387 manager.EndPreview();
389 manager.ApplyTheme(name);
447 nullptr, []() {
return kSupportsWindowFullscreen; },
448 []() {
return IsMainWindowFullscreen(); })
461 if (!current_editor) {
465 const char* menu_title =
nullptr;
466 switch (current_editor->
type()) {
468 menu_title =
"Overworld";
471 menu_title =
"Dungeon";
474 menu_title =
"Graphics";
477 menu_title =
"Music";
483 if (menu_title !=
nullptr) {
485 menu_title, [current_editor]() { current_editor->
DrawEditorMenu(); });
498 [drawers]() { drawers->ToggleLastDrawer(); },
nullptr,
nullptr,
499 [drawers]() {
return drawers->IsDrawerExpanded(); });
521 absl::StrFormat(
"%s Window Browser",
ICON_MD_APPS).c_str(),
544 if (all_categories.empty()) {
545 ImGui::TextDisabled(
"No windows available");
550 for (
const auto& category : all_categories) {
552 std::string label = category;
553 if (category == active_category) {
559 if (ImGui::BeginMenu(label.c_str())) {
566 ImGui::TextDisabled(
"No windows in this category");
580 for (
const auto& card : cards) {
583 const char* shortcut =
584 card.shortcut_hint.empty() ? nullptr : card.shortcut_hint.c_str();
587 std::string item_label =
594 if (ImGui::MenuItem(item_label.c_str(), shortcut)) {
621#ifdef YAZE_ENABLE_TESTING
653 std::map<std::string, std::vector<WorkflowItem>> grouped_items;
658 for (
const auto& category : categories) {
659 for (
const auto& descriptor :
661 if (descriptor.workflow_group.empty()) {
665 item.id = absl::StrFormat(
"panel.%s", descriptor.card_id);
666 item.group = descriptor.workflow_group;
667 item.label = descriptor.workflow_label.empty()
668 ? descriptor.display_name
669 : descriptor.workflow_label;
671 descriptor.workflow_description.empty()
672 ? absl::StrFormat(
"Open %s", descriptor.display_name)
673 : descriptor.workflow_description;
674 item.shortcut = descriptor.shortcut_hint;
675 item.priority = descriptor.workflow_priority;
676 item.callback = [
this, session_id, panel_id = descriptor.card_id]() {
681 item.enabled = descriptor.enabled_condition;
682 const std::string group = item.group.empty() ?
"General" : item.group;
683 grouped_items[group].push_back(std::move(item));
689 const std::string group = action.group.empty() ?
"General" : action.group;
690 grouped_items[group].push_back(action);
693 if (grouped_items.empty()) {
698 for (
auto& [group, items] : grouped_items) {
699 std::sort(items.begin(), items.end(),
700 [](
const WorkflowItem& lhs,
const WorkflowItem& rhs) {
701 if (lhs.priority != rhs.priority) {
702 return lhs.priority < rhs.priority;
704 return lhs.label < rhs.label;
707 for (
const auto& item : items) {
710 item.label.c_str(), item.callback,
711 item.shortcut.empty() ? nullptr : item.shortcut.c_str(), enabled);
718void MenuOrchestrator::AddRomAnalysisMenuItems() {
722 "ROM Information",
ICON_MD_INFO, [
this]() { OnShowRomInfo(); },
723 nullptr, [
this]() {
return HasActiveRom(); })
726 nullptr, [
this]() {
return HasActiveRom(); })
729 [
this]() { popup_manager_.Show(PopupID::kRomBackups); },
nullptr,
730 [
this]() {
return HasActiveRom(); })
733 nullptr, [
this]() {
return HasActiveRom(); })
737 [
this]() { OnExportBpsPatch(); },
nullptr,
738 [
this]() {
return HasActiveRom(); })
740 "Apply BPS Patch...",
ICON_MD_BUILD, [
this]() { OnApplyBpsPatch(); },
741 nullptr, [
this]() {
return HasActiveRom(); })
745 menu_builder_.BeginSubMenu(
"ZSCustomOverworld",
ICON_MD_CODE)
747 "Check ROM Version",
ICON_MD_INFO, [
this]() { OnCheckRomVersion(); },
748 nullptr, [
this]() {
return HasActiveRom(); })
751 [
this]() { OnOpenOverworldForUpgrade(); },
nullptr,
752 [
this]() {
return HasActiveRom(); })
754 [
this]() { OnToggleCustomLoading(); })
758void MenuOrchestrator::AddAsarIntegrationMenuItems() {
760 menu_builder_.BeginSubMenu(
"Asar Integration",
ICON_MD_BUILD)
762 [
this]() { popup_manager_.Show(PopupID::kAsarIntegration); })
764 "Toggle ASM Patch",
ICON_MD_CODE, [
this]() { OnToggleAsarPatch(); },
765 nullptr, [
this]() {
return HasActiveRom(); })
769void MenuOrchestrator::AddDevelopmentMenuItems() {
773 "Memory Editor",
ICON_MD_MEMORY, [
this]() { OnShowMemoryEditor(); },
774 nullptr, [
this]() {
return HasActiveRom(); })
776 [
this]() { OnShowAssemblyEditor(); })
778 [
this]() { popup_manager_.Show(PopupID::kFeatureFlags); })
780 [
this]() { OnShowPerformanceDashboard(); })
784void MenuOrchestrator::AddTestingMenuItems() {
787#ifdef YAZE_ENABLE_TESTING
793 [
this]() { OnShowTestDashboard(); },
794 GetShortcutForAction(
"Test Dashboard").c_str());
796 menu_builder_.DisabledItem(
797 "Testing support disabled (YAZE_ENABLE_TESTING=OFF)",
ICON_MD_INFO);
799 menu_builder_.EndMenu();
805void MenuOrchestrator::AddSessionsSubmenu() {
806 if (ImGui::BeginMenu(absl::StrFormat(
"%s Sessions",
ICON_MD_TAB).c_str())) {
807 if (ImGui::MenuItem(absl::StrFormat(
"%s New Session",
ICON_MD_ADD).c_str(),
808 ShortcutOrNull(GetShortcutForAction(
"New Session")))) {
809 OnCreateNewSession();
814 nullptr,
false, HasActiveRom())) {
815 OnDuplicateCurrentSession();
819 ShortcutOrNull(GetShortcutForAction(
"Close Session")),
false,
820 HasMultipleSessions())) {
821 OnCloseCurrentSession();
827 ShortcutOrNull(GetShortcutForAction(
"Session Switcher")),
false,
828 HasMultipleSessions())) {
829 OnShowSessionSwitcher();
833 OnShowSessionManager();
841void MenuOrchestrator::AddLayoutSubmenu() {
842 const bool layout_enabled = HasCurrentEditor();
843 auto apply_preset = [
this](
const char* name) {
845 editor_manager_->ApplyLayoutPreset(name);
847 auto apply_profile = [
this](
const char* name) {
849 editor_manager_->ApplyLayoutProfile(name);
852 if (ImGui::BeginMenu(
854 if (ImGui::MenuItem(absl::StrFormat(
"%s Open Layout Designer",
857 nullptr,
false, window_manager_ !=
nullptr)) {
858 OnShowLayoutDesigner();
862 if (ImGui::MenuItem(absl::StrFormat(
"%s Save Layout",
ICON_MD_SAVE).c_str(),
863 ShortcutOrNull(GetShortcutForAction(
"Save Layout")))) {
864 OnSaveWorkspaceLayout();
868 ShortcutOrNull(GetShortcutForAction(
"Load Layout")))) {
869 OnLoadWorkspaceLayout();
873 ShortcutOrNull(GetShortcutForAction(
"Reset Layout")))) {
874 OnResetWorkspaceLayout();
879 nullptr,
false, layout_enabled)) {
881 editor_manager_->ResetCurrentEditorLayout();
884 "Restore Layout Before Default",
nullptr,
false,
885 editor_manager_ && editor_manager_->HasPreviousEditorLayout())) {
886 editor_manager_->RestorePreviousEditorLayout();
890 if (ImGui::BeginMenu(
896 nullptr,
false, layout_enabled)) {
897 save_snapshot_name_buffer_[0] =
'\0';
898 open_save_snapshot_modal_ =
true;
902 std::vector<std::string> named =
903 editor_manager_ ? editor_manager_->ListLayoutSnapshots()
904 : std::vector<std::string>{};
905 if (!named.empty()) {
907 for (
const auto& name : named) {
908 ImGui::PushID(name.c_str());
911 nullptr,
false, layout_enabled)) {
913 editor_manager_->RestoreLayoutSnapshot(name);
915 ImGui::SameLine(ImGui::GetContentRegionAvail().x);
918 editor_manager_->DeleteLayoutSnapshot(name);
929 nullptr,
false, layout_enabled)) {
931 editor_manager_->CaptureTemporaryLayoutSnapshot();
935 nullptr,
false, layout_enabled)) {
937 editor_manager_->RestoreTemporaryLayoutSnapshot();
942 nullptr,
false, layout_enabled)) {
944 editor_manager_->ClearTemporaryLayoutSnapshot();
950 if (ImGui::BeginMenu(
952 if (ImGui::MenuItem(absl::StrFormat(
"%s Code",
ICON_MD_CODE).c_str(),
953 nullptr,
false, layout_enabled)) {
954 apply_profile(
"code");
958 false, layout_enabled)) {
959 apply_profile(
"debug");
961 if (ImGui::MenuItem(absl::StrFormat(
"%s Mapping",
ICON_MD_MAP).c_str(),
962 nullptr,
false, layout_enabled)) {
963 apply_profile(
"mapping");
967 nullptr,
false, layout_enabled)) {
968 apply_profile(
"chat");
973 nullptr,
false, layout_enabled)) {
974 apply_preset(
"Minimal");
978 nullptr,
false, layout_enabled)) {
979 OnLoadDeveloperLayout();
983 nullptr,
false, layout_enabled)) {
984 OnLoadDesignerLayout();
986 if (ImGui::MenuItem(absl::StrFormat(
"%s Modder",
ICON_MD_BUILD).c_str(),
987 nullptr,
false, layout_enabled)) {
988 OnLoadModderLayout();
991 absl::StrFormat(
"%s Overworld Expert",
ICON_MD_MAP).c_str(),
992 nullptr,
false, layout_enabled)) {
993 apply_preset(
"Overworld Expert");
997 nullptr,
false, layout_enabled)) {
998 apply_preset(
"Dungeon Expert");
1000 if (ImGui::MenuItem(
1002 false, layout_enabled)) {
1003 apply_preset(
"Testing");
1005 if (ImGui::MenuItem(
1007 false, layout_enabled)) {
1008 apply_preset(
"Audio");
1011 if (ImGui::MenuItem(
1012 absl::StrFormat(
"%s Manage Presets...",
ICON_MD_TUNE).c_str())) {
1013 OnShowLayoutPresets();
1023void MenuOrchestrator::AddSidebarSubmenu() {
1024 if (!user_settings_) {
1028 if (!ImGui::BeginMenu(
1033 auto persist = [
this]() {
1034 (void)user_settings_->Save();
1036 auto& prefs = user_settings_->prefs();
1038 if (ImGui::MenuItem(
1040 nullptr,
false, !prefs.sidebar_order.empty())) {
1041 prefs.sidebar_order.clear();
1044 if (ImGui::MenuItem(
1046 nullptr,
false, !prefs.sidebar_hidden.empty())) {
1047 prefs.sidebar_hidden.clear();
1053 const size_t session_id = session_coordinator_.GetActiveSessionId();
1054 std::vector<std::string> categories;
1055 if (window_manager_) {
1056 categories = window_manager_->GetAllCategories(session_id);
1060 std::vector<std::string> pinned_list;
1061 std::vector<std::string> hidden_list;
1062 for (
const auto& cat : categories) {
1063 if (cat == WorkspaceWindowManager::kDashboardCategory)
1065 if (prefs.sidebar_pinned.count(cat))
1066 pinned_list.push_back(cat);
1067 if (prefs.sidebar_hidden.count(cat))
1068 hidden_list.push_back(cat);
1071 if (ImGui::BeginMenu(
1073 if (pinned_list.empty()) {
1074 ImGui::TextDisabled(
"(none)");
1076 for (
const auto& cat : pinned_list) {
1077 ImGui::PushID(cat.c_str());
1078 if (ImGui::MenuItem(
1079 absl::StrFormat(
"%s Unpin %s",
ICON_MD_CLOSE, cat).c_str())) {
1080 prefs.sidebar_pinned.erase(cat);
1089 if (ImGui::BeginMenu(
1091 if (hidden_list.empty()) {
1092 ImGui::TextDisabled(
"(none)");
1094 for (
const auto& cat : hidden_list) {
1095 ImGui::PushID(cat.c_str());
1096 if (ImGui::MenuItem(
1099 prefs.sidebar_hidden.erase(cat);
1112 if (ImGui::BeginMenu(absl::StrFormat(
"%s Customize",
ICON_MD_TUNE).c_str())) {
1113 if (categories.empty()) {
1114 ImGui::TextDisabled(
"No categories available");
1116 for (
const auto& cat : categories) {
1117 if (cat == WorkspaceWindowManager::kDashboardCategory)
1119 ImGui::PushID(cat.c_str());
1120 const bool pinned = prefs.sidebar_pinned.count(cat) > 0;
1121 const bool hidden = prefs.sidebar_hidden.count(cat) > 0;
1122 if (ImGui::BeginMenu(cat.c_str())) {
1123 if (ImGui::MenuItem(pinned ?
"Unpin from Top" :
"Pin to Top",
nullptr,
1126 prefs.sidebar_pinned.erase(cat);
1128 prefs.sidebar_pinned.insert(cat);
1132 if (ImGui::MenuItem(hidden ?
"Show on Sidebar" :
"Hide from Sidebar",
1135 prefs.sidebar_hidden.erase(cat);
1137 prefs.sidebar_hidden.insert(cat);
1152void MenuOrchestrator::BuildHelpMenu() {
1153 menu_builder_.BeginMenu(
"Help");
1155 menu_builder_.EndMenu();
1158void MenuOrchestrator::AddHelpMenuItems() {
1162 [
this]() { OnShowGettingStarted(); })
1164 [
this]() { OnShowComingFromZScream(); })
1168 if (window_manager_) {
1169 window_manager_->TriggerShowShortcuts();
1172 GetShortcutForAction(
"Keyboard Shortcuts").c_str())
1174 [
this]() { OnShowBuildInstructions(); })
1178 [
this]() { OnShowSupportedFeatures(); })
1182 [
this]() { OnShowTroubleshooting(); })
1184 [
this]() { OnShowContributing(); })
1188 GetShortcutForAction(
"Show About").c_str());
1190 menu_builder_.Separator();
1192 for (
const std::string& locale :
1195 locale.c_str(),
nullptr,
1202 menu_builder_.EndMenu();
1206void MenuOrchestrator::ClearMenu() {
1207 menu_builder_.Clear();
1210void MenuOrchestrator::RefreshMenu() {
1211 menu_needs_refresh_ =
true;
1215void MenuOrchestrator::OnOpenRom() {
1217 if (editor_manager_) {
1218 auto status = editor_manager_->LoadRom();
1220 toast_manager_.Show(
1221 absl::StrFormat(
"Failed to load ROM: %s", status.message()),
1227void MenuOrchestrator::OnSaveRom() {
1229 if (editor_manager_) {
1230 auto status = editor_manager_->SaveCurrentSession();
1232 if (absl::IsCancelled(status)) {
1235 toast_manager_.Show(
1236 absl::StrFormat(
"Failed to save: %s", status.message()),
1242void MenuOrchestrator::OnSaveRomAs() {
1243 if (editor_manager_) {
1244 auto status = editor_manager_->SaveCurrentSessionAs();
1245 if (!status.ok() && !absl::IsCancelled(status)) {
1246 toast_manager_.Show(
1247 absl::StrFormat(
"Failed to save a copy: %s", status.message()),
1253void MenuOrchestrator::OnCreateProject() {
1255 if (editor_manager_) {
1256 auto status = editor_manager_->CreateNewProject();
1258 toast_manager_.Show(
1259 absl::StrFormat(
"Failed to create project: %s", status.message()),
1265void MenuOrchestrator::OnOpenProject() {
1267 if (editor_manager_) {
1268 auto status = editor_manager_->OpenProject();
1270 toast_manager_.Show(
1271 absl::StrFormat(
"Failed to open project: %s", status.message()),
1277void MenuOrchestrator::OnCreateLyraTerrain() {
1278 if (editor_manager_) {
1279 auto status = editor_manager_->CreateLyraTerrain();
1281 toast_manager_.Show(absl::StrFormat(
"Failed to create Lyra terrain: %s",
1288void MenuOrchestrator::OnOpenLyraTerrain() {
1289 if (editor_manager_) {
1290 auto status = editor_manager_->OpenLyraTerrain();
1292 toast_manager_.Show(
1293 absl::StrFormat(
"Failed to open Lyra terrain: %s", status.message()),
1299void MenuOrchestrator::OnSaveProject() {
1301 if (editor_manager_) {
1302 auto status = editor_manager_->SaveProject();
1304 toast_manager_.Show(
1305 absl::StrFormat(
"Failed to save project: %s", status.message()),
1308 toast_manager_.Show(
"Project saved successfully", ToastType::kSuccess);
1313void MenuOrchestrator::OnSaveProjectAs() {
1315 if (editor_manager_) {
1316 auto status = editor_manager_->SaveProjectAs();
1318 toast_manager_.Show(
1319 absl::StrFormat(
"Failed to save project as: %s", status.message()),
1325void MenuOrchestrator::OnShowProjectManagement() {
1327 if (editor_manager_) {
1328 editor_manager_->ShowProjectManagement();
1332void MenuOrchestrator::OnShowProjectFileEditor() {
1334 if (editor_manager_) {
1335 editor_manager_->ShowProjectFileEditor();
1339void MenuOrchestrator::OnOpenRecentFile(
const std::string& path) {
1340 if (!editor_manager_) {
1344 auto status = editor_manager_->OpenRomOrProject(path);
1346 toast_manager_.Show(
1347 absl::StrFormat(
"Failed to open %s: %s", path, status.message()),
1352void MenuOrchestrator::OnClearRecentFiles() {
1358void MenuOrchestrator::OnCloseRom() {
1359 if (editor_manager_) {
1360 editor_manager_->CloseRom();
1364void MenuOrchestrator::OnRevertRom() {
1365 if (!editor_manager_) {
1368 auto status = editor_manager_->RevertRomToSaved();
1370 toast_manager_.Show(absl::StrFormat(
"Revert failed: %s", status.message()),
1371 ToastType::kError, 6.0f);
1376void MenuOrchestrator::OnUndo() {
1377 if (editor_manager_) {
1378 auto* current_editor = editor_manager_->GetCurrentEditor();
1379 if (current_editor) {
1382 auto status = current_editor->
Undo();
1384 if (!desc.empty()) {
1385 toast_manager_.Show(absl::StrFormat(
"Undid: %s", desc),
1386 ToastType::kInfo, 2.0f);
1389 toast_manager_.Show(
1390 absl::StrFormat(
"Undo failed: %s", status.message()),
1397void MenuOrchestrator::OnRedo() {
1398 if (editor_manager_) {
1399 auto* current_editor = editor_manager_->GetCurrentEditor();
1400 if (current_editor) {
1403 auto status = current_editor->
Redo();
1405 if (!desc.empty()) {
1406 toast_manager_.Show(absl::StrFormat(
"Redid: %s", desc),
1407 ToastType::kInfo, 2.0f);
1410 toast_manager_.Show(
1411 absl::StrFormat(
"Redo failed: %s", status.message()),
1418void MenuOrchestrator::OnCut() {
1419 if (!editor_manager_) {
1422 auto* current_editor = editor_manager_->GetCurrentEditor();
1423 if (!current_editor) {
1426 auto status = current_editor->
Cut();
1430 if (absl::IsUnimplemented(status)) {
1433 toast_manager_.Show(
"Cut is not available in this editor", ToastType::kInfo,
1437 toast_manager_.Show(absl::StrFormat(
"Cut failed: %s", status.message()),
1441void MenuOrchestrator::OnCopy() {
1442 if (!editor_manager_) {
1445 auto* current_editor = editor_manager_->GetCurrentEditor();
1446 if (!current_editor) {
1449 auto status = current_editor->
Copy();
1453 if (absl::IsUnimplemented(status)) {
1456 toast_manager_.Show(
"Copy is not available in this editor",
1457 ToastType::kInfo, 2.0f);
1460 toast_manager_.Show(absl::StrFormat(
"Copy failed: %s", status.message()),
1464void MenuOrchestrator::OnPaste() {
1465 if (!editor_manager_) {
1468 auto* current_editor = editor_manager_->GetCurrentEditor();
1469 if (!current_editor) {
1472 auto status = current_editor->
Paste();
1476 if (absl::IsUnimplemented(status)) {
1479 toast_manager_.Show(
"Paste is not available in this editor",
1480 ToastType::kInfo, 2.0f);
1483 toast_manager_.Show(absl::StrFormat(
"Paste failed: %s", status.message()),
1487void MenuOrchestrator::OnFind() {
1488 if (!editor_manager_) {
1491 auto* current_editor = editor_manager_->GetCurrentEditor();
1492 if (!current_editor) {
1495 auto status = current_editor->
Find();
1499 if (absl::IsUnimplemented(status)) {
1502 toast_manager_.Show(
"Find is not available in this editor",
1503 ToastType::kInfo, 2.0f);
1506 toast_manager_.Show(absl::StrFormat(
"Find failed: %s", status.message()),
1513 if (editor_manager_) {
1514 editor_manager_->SwitchToEditor(editor_type);
1518void MenuOrchestrator::OnShowEditorSelection() {
1520 if (editor_manager_) {
1521 if (
auto* ui = editor_manager_->ui_coordinator()) {
1522 ui->ShowEditorSelection();
1527void MenuOrchestrator::OnShowDisplaySettings() {
1528 popup_manager_.Show(PopupID::kDisplaySettings);
1535 const std::string& action) {
1536 if (manager ==
nullptr) {
1540 if (shortcut ==
nullptr || !shortcut->
callback) {
1553void MenuOrchestrator::OnZoomIn() {
1554 if (RunShortcutAction(shortcut_manager_,
"ui.font_scale_increase") ||
1559 editor_manager_->user_settings().prefs().font_global_scale +
1561 editor_manager_->SetFontGlobalScale(
1562 std::clamp(scale, kMenuFontScaleMin, kMenuFontScaleMax));
1565void MenuOrchestrator::OnZoomOut() {
1566 if (RunShortcutAction(shortcut_manager_,
"ui.font_scale_decrease") ||
1571 editor_manager_->user_settings().prefs().font_global_scale -
1573 editor_manager_->SetFontGlobalScale(
1574 std::clamp(scale, kMenuFontScaleMin, kMenuFontScaleMax));
1577void MenuOrchestrator::OnZoomReset() {
1578 if (RunShortcutAction(shortcut_manager_,
"ui.font_scale_reset") ||
1582 editor_manager_->SetFontGlobalScale(1.0f);
1585void MenuOrchestrator::OnToggleFullscreen() {
1586 if (!SetMainWindowFullscreen(!IsMainWindowFullscreen())) {
1587 toast_manager_.Show(
"Fullscreen is not available for this window",
1588 ToastType::kWarning);
1592void MenuOrchestrator::OnShowHexEditor() {
1594 if (editor_manager_) {
1595 editor_manager_->window_manager().OpenWindow(
1596 editor_manager_->GetCurrentSessionId(),
"Hex Editor");
1600void MenuOrchestrator::OnShowPanelBrowser() {
1601 if (editor_manager_) {
1602 if (
auto* ui = editor_manager_->ui_coordinator()) {
1603 ui->SetWindowBrowserVisible(
true);
1608void MenuOrchestrator::OnShowPanelFinder() {
1609 if (editor_manager_) {
1610 if (
auto* ui = editor_manager_->ui_coordinator()) {
1611 ui->ShowPanelFinder();
1616void MenuOrchestrator::OnShowWelcomeScreen() {
1617 if (editor_manager_) {
1618 if (
auto* ui = editor_manager_->ui_coordinator()) {
1619 ui->SetWelcomeScreenVisible(
true);
1624#ifdef YAZE_BUILD_AGENT_UI
1625void MenuOrchestrator::OnShowAIAgent() {
1626 if (editor_manager_) {
1627 if (
auto* ui = editor_manager_->ui_coordinator()) {
1628 ui->SetAIAgentVisible(
true);
1633void MenuOrchestrator::OnShowProposalDrawer() {
1634 if (editor_manager_) {
1635 if (
auto* ui = editor_manager_->ui_coordinator()) {
1636 ui->SetProposalDrawerVisible(
true);
1643void MenuOrchestrator::OnCreateNewSession() {
1644 session_coordinator_.CreateNewSession();
1647void MenuOrchestrator::OnDuplicateCurrentSession() {
1648 session_coordinator_.DuplicateCurrentSession();
1651void MenuOrchestrator::OnCloseCurrentSession() {
1652 if (editor_manager_) {
1653 editor_manager_->CloseCurrentSession();
1655 session_coordinator_.CloseCurrentSession();
1659void MenuOrchestrator::OnShowSessionSwitcher() {
1661 if (editor_manager_) {
1662 if (
auto* ui = editor_manager_->ui_coordinator()) {
1663 ui->ShowSessionSwitcher();
1668void MenuOrchestrator::OnShowSessionManager() {
1669 popup_manager_.Show(PopupID::kSessionManager);
1673void MenuOrchestrator::OnShowAllWindows() {
1675 if (editor_manager_) {
1676 if (
auto* ui = editor_manager_->ui_coordinator()) {
1677 ui->ShowAllWindows();
1682void MenuOrchestrator::OnHideAllWindows() {
1684 if (editor_manager_) {
1685 editor_manager_->HideAllWindows();
1689void MenuOrchestrator::OnResetWorkspaceLayout() {
1691 if (editor_manager_) {
1692 editor_manager_->QueueDeferredAction([
this]() {
1693 editor_manager_->ResetWorkspaceLayout();
1694 toast_manager_.Show(
"Layout reset to default", ToastType::kInfo);
1699void MenuOrchestrator::OnSaveWorkspaceLayout() {
1701 if (editor_manager_) {
1702 editor_manager_->SaveWorkspaceLayout();
1706void MenuOrchestrator::OnLoadWorkspaceLayout() {
1708 if (editor_manager_) {
1709 editor_manager_->LoadWorkspaceLayout();
1713void MenuOrchestrator::OnShowLayoutPresets() {
1714 popup_manager_.Show(PopupID::kLayoutPresets);
1717void MenuOrchestrator::OnShowLayoutDesigner() {
1719 if (manager ==
nullptr && editor_manager_ !=
nullptr) {
1720 manager = &editor_manager_->window_manager();
1722 if (manager ==
nullptr) {
1723 toast_manager_.Show(
"Layout Designer unavailable: no window manager",
1731 WorkspaceWindowManager::kDashboardCategory) {
1735 const bool already_open =
1736 manager->
IsWindowOpen(session_id, kLayoutDesignerWindowId);
1737 if (!already_open &&
1738 !manager->
OpenWindow(session_id, kLayoutDesignerWindowId)) {
1739 toast_manager_.Show(
1740 "Layout Designer is not registered in the active session",
1751void MenuOrchestrator::OnLoadDeveloperLayout() {
1752 if (editor_manager_) {
1753 editor_manager_->ApplyLayoutPreset(
"Developer");
1757void MenuOrchestrator::OnLoadDesignerLayout() {
1758 if (editor_manager_) {
1759 editor_manager_->ApplyLayoutPreset(
"Designer");
1763void MenuOrchestrator::OnLoadModderLayout() {
1764 if (editor_manager_) {
1765 editor_manager_->ApplyLayoutPreset(
"Modder");
1770void MenuOrchestrator::OnShowGlobalSearch() {
1771 if (editor_manager_) {
1772 if (
auto* ui = editor_manager_->ui_coordinator()) {
1773 ui->ShowGlobalSearch();
1778void MenuOrchestrator::OnShowCommandPalette() {
1779 if (editor_manager_) {
1780 if (
auto* ui = editor_manager_->ui_coordinator()) {
1781 ui->ShowCommandPalette();
1786void MenuOrchestrator::OnShowPerformanceDashboard() {
1787 if (editor_manager_) {
1788 if (
auto* ui = editor_manager_->ui_coordinator()) {
1789 ui->SetPerformanceDashboardVisible(
true);
1794void MenuOrchestrator::OnShowImGuiDemo() {
1795 if (editor_manager_) {
1796 editor_manager_->ShowImGuiDemo();
1800void MenuOrchestrator::OnShowImGuiMetrics() {
1801 if (editor_manager_) {
1802 editor_manager_->ShowImGuiMetrics();
1806void MenuOrchestrator::OnShowMemoryEditor() {
1807 if (editor_manager_) {
1808 editor_manager_->window_manager().OpenWindow(
1809 editor_manager_->GetCurrentSessionId(),
"Memory Editor");
1813void MenuOrchestrator::OnShowResourceLabelManager() {
1814 if (editor_manager_) {
1815 if (
auto* ui = editor_manager_->ui_coordinator()) {
1816 ui->SetResourceLabelManagerVisible(
true);
1821#ifdef YAZE_ENABLE_TESTING
1822void MenuOrchestrator::OnShowTestDashboard() {
1823 if (editor_manager_) {
1824 editor_manager_->ShowTestDashboard();
1831void MenuOrchestrator::OnShowAbout() {
1832 popup_manager_.Show(PopupID::kAbout);
1835void MenuOrchestrator::OnShowGettingStarted() {
1836 popup_manager_.Show(PopupID::kGettingStarted);
1839void MenuOrchestrator::OnShowComingFromZScream() {
1840 popup_manager_.Show(PopupID::kComingFromZScream);
1843void MenuOrchestrator::OnShowBuildInstructions() {
1844 popup_manager_.Show(PopupID::kBuildInstructions);
1847void MenuOrchestrator::OnShowCLIUsage() {
1848 popup_manager_.Show(PopupID::kCLIUsage);
1851void MenuOrchestrator::OnShowTroubleshooting() {
1852 popup_manager_.Show(PopupID::kTroubleshooting);
1855void MenuOrchestrator::OnShowContributing() {
1856 popup_manager_.Show(PopupID::kContributing);
1859void MenuOrchestrator::OnShowWhatsNew() {
1860 popup_manager_.Show(PopupID::kWhatsNew);
1863void MenuOrchestrator::OnShowSupportedFeatures() {
1864 popup_manager_.Show(PopupID::kSupportedFeatures);
1868void MenuOrchestrator::OnShowRomInfo() {
1869 popup_manager_.Show(PopupID::kRomInfo);
1872void MenuOrchestrator::OnCreateBackup() {
1873 if (editor_manager_) {
1874 auto status = rom_manager_.CreateBackup(editor_manager_->GetCurrentRom());
1876 toast_manager_.Show(
"Backup created successfully", ToastType::kSuccess);
1878 toast_manager_.Show(
1879 absl::StrFormat(
"Backup failed: %s", status.message()),
1885void MenuOrchestrator::OnValidateRom() {
1886 if (editor_manager_) {
1887 auto status = rom_manager_.ValidateRom(editor_manager_->GetCurrentRom());
1889 toast_manager_.Show(
"ROM validation passed", ToastType::kSuccess);
1891 toast_manager_.Show(
1892 absl::StrFormat(
"ROM validation failed: %s", status.message()),
1898void MenuOrchestrator::OnShowSettings() {
1900 if (editor_manager_) {
1901 editor_manager_->SwitchToEditor(EditorType::kSettings);
1905void MenuOrchestrator::OnQuit() {
1906 if (editor_manager_) {
1907 editor_manager_->Quit();
1912bool MenuOrchestrator::CanSaveRom()
const {
1913 auto* rom = editor_manager_ ? editor_manager_->GetCurrentRom() :
nullptr;
1914 return rom ? rom_manager_.IsRomLoaded(rom) :
false;
1917bool MenuOrchestrator::CanSaveProject()
const {
1919 return HasProjectFile() &&
1920 editor_manager_->IsCurrentProjectContextOwnedBySession(
1921 editor_manager_->GetCurrentSessionId());
1924bool MenuOrchestrator::HasActiveRom()
const {
1925 auto* rom = editor_manager_ ? editor_manager_->GetCurrentRom() :
nullptr;
1926 return rom ? rom_manager_.IsRomLoaded(rom) :
false;
1929bool MenuOrchestrator::HasActiveProject()
const {
1930 return project_manager_.HasActiveProject();
1933bool MenuOrchestrator::HasProjectFile()
const {
1936 const auto* project =
1937 editor_manager_ ? editor_manager_->GetCurrentProject() :
nullptr;
1938 return project && !project->filepath.empty();
1941bool MenuOrchestrator::HasCurrentEditor()
const {
1942 return editor_manager_ && editor_manager_->GetCurrentEditor() !=
nullptr;
1945bool MenuOrchestrator::HasMultipleSessions()
const {
1946 return session_coordinator_.HasMultipleSessions();
1949bool MenuOrchestrator::CurrentEditorCanUndo()
const {
1951 editor_manager_ ? editor_manager_->GetCurrentEditor() :
nullptr;
1952 return editor !=
nullptr && editor->
CanUndo();
1955bool MenuOrchestrator::CurrentEditorCanRedo()
const {
1957 editor_manager_ ? editor_manager_->GetCurrentEditor() :
nullptr;
1958 return editor !=
nullptr && editor->
CanRedo();
1961bool MenuOrchestrator::CurrentEditorCanCut()
const {
1963 editor_manager_ ? editor_manager_->GetCurrentEditor() :
nullptr;
1964 return editor !=
nullptr && editor->
CanCut();
1967bool MenuOrchestrator::CurrentEditorCanCopy()
const {
1969 editor_manager_ ? editor_manager_->GetCurrentEditor() :
nullptr;
1970 return editor !=
nullptr && editor->
CanCopy();
1973bool MenuOrchestrator::CurrentEditorCanPaste()
const {
1975 editor_manager_ ? editor_manager_->GetCurrentEditor() :
nullptr;
1976 return editor !=
nullptr && editor->
CanPaste();
1979bool MenuOrchestrator::CurrentEditorCanFind()
const {
1981 editor_manager_ ? editor_manager_->GetCurrentEditor() :
nullptr;
1982 return editor !=
nullptr && editor->
CanFind();
1986std::string MenuOrchestrator::GetRomFilename()
const {
1987 auto* rom = editor_manager_ ? editor_manager_->GetCurrentRom() :
nullptr;
1988 return rom ? rom_manager_.GetRomFilename(rom) :
"";
1991std::string MenuOrchestrator::GetProjectName()
const {
1992 return project_manager_.GetProjectName();
1995std::string MenuOrchestrator::GetCurrentEditorName()
const {
1997 return "Unknown Editor";
2001std::string MenuOrchestrator::GetShortcutForAction(
2002 const std::string& action)
const {
2010void MenuOrchestrator::OnCheckRomVersion() {
2011 if (!editor_manager_)
2013 auto* rom = editor_manager_->GetCurrentRom();
2019 std::string version_str =
2020 (version == 0xFF) ?
"Vanilla" : absl::StrFormat(
"v%d", version);
2022 toast_manager_.Show(
2023 absl::StrFormat(
"ROM: %s | ZSCustomOverworld: %s", rom->
title().c_str(),
2024 version_str.c_str()),
2025 ToastType::kInfo, 5.0f);
2028void MenuOrchestrator::OnOpenOverworldForUpgrade() {
2031 if (!editor_manager_ || !HasActiveRom())
2033 editor_manager_->SwitchToEditor(EditorType::kOverworld);
2034 toast_manager_.Show(
2035 "Use the Upgrade button in the Overworld toolbar to apply "
2036 "ZSCustomOverworld",
2037 ToastType::kInfo, 4.0f);
2040void MenuOrchestrator::OnToggleCustomLoading() {
2042 flags.overworld.kLoadCustomOverworld = !flags.overworld.kLoadCustomOverworld;
2044 toast_manager_.Show(
2046 "Custom Overworld Loading: %s",
2047 flags.overworld.kLoadCustomOverworld ?
"Enabled" :
"Disabled"),
2051void MenuOrchestrator::OnToggleAsarPatch() {
2052 if (!editor_manager_)
2054 auto* rom = editor_manager_->GetCurrentRom();
2059 flags.overworld.kApplyZSCustomOverworldASM =
2060 !flags.overworld.kApplyZSCustomOverworldASM;
2062 toast_manager_.Show(
2064 "ZSCustomOverworld ASM Application: %s",
2065 flags.overworld.kApplyZSCustomOverworldASM ?
"Enabled" :
"Disabled"),
2069void MenuOrchestrator::OnShowAssemblyEditor() {
2070 if (editor_manager_) {
2071 editor_manager_->SwitchToEditor(EditorType::kAssembly);
2075void MenuOrchestrator::OnExportBpsPatch() {
2076 if (!editor_manager_)
2078 auto* rom = editor_manager_->GetCurrentRom();
2084 std::string original_path =
2086 if (original_path.empty()) {
2092 auto load_status = original_rom.
LoadFromFile(original_path);
2093 if (!load_status.ok()) {
2094 toast_manager_.Show(absl::StrFormat(
"Failed to load original ROM: %s",
2095 load_status.message()),
2101 std::vector<uint8_t> patch_data;
2102 auto create_status =
2104 if (!create_status.ok()) {
2105 toast_manager_.Show(absl::StrFormat(
"Failed to create BPS patch: %s",
2106 create_status.message()),
2112 std::string default_name = rom->
short_name() +
".bps";
2113 std::string save_path =
2115 if (save_path.empty()) {
2120 if (save_path.size() < 4 ||
2121 save_path.substr(save_path.size() - 4) !=
".bps") {
2122 save_path +=
".bps";
2126 std::ofstream file(save_path, std::ios::binary);
2127 if (!file.is_open()) {
2128 toast_manager_.Show(
2129 absl::StrFormat(
"Failed to open file for writing: %s", save_path),
2134 file.write(
reinterpret_cast<const char*
>(patch_data.data()),
2139 toast_manager_.Show(
2140 absl::StrFormat(
"Failed to write patch file: %s", save_path),
2145 toast_manager_.Show(absl::StrFormat(
"BPS patch exported: %s (%zu bytes)",
2146 save_path, patch_data.size()),
2147 ToastType::kSuccess);
2150void MenuOrchestrator::OnApplyBpsPatch() {
2151 if (!editor_manager_)
2153 auto* rom = editor_manager_->GetCurrentRom();
2159 options.
filters.push_back({
"BPS Patch",
"bps"});
2161 if (patch_path.empty()) {
2166 std::ifstream file(patch_path, std::ios::binary);
2167 if (!file.is_open()) {
2168 toast_manager_.Show(
2169 absl::StrFormat(
"Failed to open patch file: %s", patch_path),
2174 std::vector<uint8_t> patch_data((std::istreambuf_iterator<char>(file)),
2175 std::istreambuf_iterator<char>());
2178 if (patch_data.empty()) {
2179 toast_manager_.Show(
"Patch file is empty", ToastType::kError);
2184 std::vector<uint8_t> patched_rom;
2187 if (!apply_status.ok()) {
2188 toast_manager_.Show(absl::StrFormat(
"Failed to apply BPS patch: %s",
2189 apply_status.message()),
2196 if (!load_status.ok()) {
2197 toast_manager_.Show(absl::StrFormat(
"Failed to load patched ROM data: %s",
2198 load_status.message()),
2204 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()
auto GetCurrentEditor() const -> Editor *override
FeatureHintManager * feature_hint_manager()
project::YazeProject * GetCurrentProject()
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 void DrawEditorMenu()
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_MORE_HORIZ
#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