refactor(editor): introduce MenuOrchestrator for menu management

- Added MenuOrchestrator class to handle menu construction and coordination, improving separation of concerns within the editor.
- Refactored EditorManager to delegate menu-related tasks to MenuOrchestrator, enhancing maintainability and clarity.
- Updated CMake configuration to include new source files for the MenuOrchestrator component.

Benefits:
- Streamlines menu management, leading to a more organized and efficient user experience.
- Enhances the overall architecture by clearly defining roles for menu handling and editor operations.
This commit is contained in:
scawful
2025-10-14 22:26:34 -04:00
parent 0b72b56594
commit 2f84879ca9
5 changed files with 679 additions and 515 deletions

View File

@@ -133,6 +133,11 @@ EditorManager::EditorManager()
// Initialize new delegated components
session_coordinator_ = std::make_unique<SessionCoordinator>(
static_cast<void*>(&sessions_), &card_registry_, &toast_manager_);
// Initialize MenuOrchestrator after SessionCoordinator is created
menu_orchestrator_ = std::make_unique<MenuOrchestrator>(
menu_builder_, rom_file_manager_, project_manager_, editor_registry_,
*session_coordinator_, toast_manager_);
}
EditorManager::~EditorManager() = default;
@@ -1093,521 +1098,10 @@ void EditorManager::DrawContextSensitiveCardControl() {
}
void EditorManager::BuildModernMenu() {
menu_builder_.Clear();
// File Menu - enhanced with ROM features
menu_builder_.BeginMenu("File")
.Item(
"Open ROM", ICON_MD_FILE_OPEN, [this]() { status_ = LoadRom(); },
"Ctrl+O")
.Item(
"Save ROM", ICON_MD_SAVE, [this]() { status_ = SaveRom(); }, "Ctrl+S",
[this]() { return current_rom_ && current_rom_->is_loaded(); })
.Item(
"Save As...", ICON_MD_SAVE_AS,
[this]() { popup_manager_->Show("Save As.."); }, nullptr,
[this]() { return current_rom_ && current_rom_->is_loaded(); })
.Separator()
.Item("New Project", ICON_MD_CREATE_NEW_FOLDER,
[this]() { status_ = CreateNewProject(); })
.Item("Open Project", ICON_MD_FOLDER_OPEN,
[this]() { status_ = OpenProject(); })
.Item(
"Save Project", ICON_MD_SAVE, [this]() { status_ = SaveProject(); },
nullptr, [this]() { return !current_project_.filepath.empty(); })
.Item(
"Save Project As...", ICON_MD_SAVE_AS,
[this]() { status_ = SaveProjectAs(); }, nullptr,
[this]() { return !current_project_.filepath.empty(); })
.Separator()
.Item(
"ROM Information", ICON_MD_INFO,
[this]() { popup_manager_->Show("ROM Info"); }, nullptr,
[this]() { return current_rom_ && current_rom_->is_loaded(); })
.Item(
"Create Backup", ICON_MD_BACKUP,
[this]() {
if (current_rom_ && current_rom_->is_loaded()) {
Rom::SaveSettings settings;
settings.backup = true;
settings.filename = current_rom_->filename();
status_ = current_rom_->SaveToFile(settings);
if (status_.ok()) {
toast_manager_.Show("Backup created successfully",
ToastType::kSuccess);
}
}
},
nullptr,
[this]() { return current_rom_ && current_rom_->is_loaded(); })
.Item(
"Validate ROM", ICON_MD_CHECK_CIRCLE,
[this]() {
if (current_rom_ && current_rom_->is_loaded()) {
auto result = current_project_.Validate();
if (result.ok()) {
toast_manager_.Show("ROM validation passed",
ToastType::kSuccess);
} else {
toast_manager_.Show(
"ROM validation failed: " + std::string(result.message()),
ToastType::kError);
}
}
},
nullptr,
[this]() { return current_rom_ && current_rom_->is_loaded(); })
.Separator()
.Item(
"Settings", ICON_MD_SETTINGS,
[this]() { current_editor_set_->settings_editor_.set_active(true); })
.Separator()
.Item(
"Quit", ICON_MD_EXIT_TO_APP, [this]() { quit_ = true; }, "Ctrl+Q")
.EndMenu();
// Edit Menu
menu_builder_.BeginMenu("Edit")
.Item(
"Undo", ICON_MD_UNDO,
[this]() {
if (current_editor_)
status_ = current_editor_->Undo();
},
"Ctrl+Z")
.Item(
"Redo", ICON_MD_REDO,
[this]() {
if (current_editor_)
status_ = current_editor_->Redo();
},
"Ctrl+Y")
.Separator()
.Item(
"Cut", ICON_MD_CONTENT_CUT,
[this]() {
if (current_editor_)
status_ = current_editor_->Cut();
},
"Ctrl+X")
.Item(
"Copy", ICON_MD_CONTENT_COPY,
[this]() {
if (current_editor_)
status_ = current_editor_->Copy();
},
"Ctrl+C")
.Item(
"Paste", ICON_MD_CONTENT_PASTE,
[this]() {
if (current_editor_)
status_ = current_editor_->Paste();
},
"Ctrl+V")
.Separator()
.Item(
"Find", ICON_MD_SEARCH,
[this]() {
if (current_editor_)
status_ = current_editor_->Find();
},
"Ctrl+F")
.Item(
"Find in Files", ICON_MD_SEARCH,
[this]() { show_global_search_ = true; }, "Ctrl+Shift+F")
.EndMenu();
// View Menu - editors and cards
menu_builder_.BeginMenu("View")
.Item(
"Editor Selection", ICON_MD_DASHBOARD,
[this]() { show_editor_selection_ = true; }, "Ctrl+E")
.Separator()
.Item(
"Overworld", ICON_MD_MAP,
[this]() { SwitchToEditor(EditorType::kOverworld); }, "Ctrl+1")
.Item(
"Dungeon", ICON_MD_CASTLE,
[this]() { SwitchToEditor(EditorType::kDungeon); }, "Ctrl+2")
.Item(
"Graphics", ICON_MD_IMAGE,
[this]() { SwitchToEditor(EditorType::kGraphics); }, "Ctrl+3")
.Item(
"Sprites", ICON_MD_TOYS,
[this]() { SwitchToEditor(EditorType::kSprite); }, "Ctrl+4")
.Item(
"Messages", ICON_MD_CHAT_BUBBLE,
[this]() { SwitchToEditor(EditorType::kMessage); }, "Ctrl+5")
.Item(
"Music", ICON_MD_MUSIC_NOTE,
[this]() { SwitchToEditor(EditorType::kMusic); }, "Ctrl+6")
.Item(
"Palettes", ICON_MD_PALETTE,
[this]() { SwitchToEditor(EditorType::kPalette); }, "Ctrl+7")
.Item(
"Screens", ICON_MD_TV,
[this]() { SwitchToEditor(EditorType::kScreen); }, "Ctrl+8")
.Item(
"Hex Editor", ICON_MD_DATA_ARRAY,
[this]() {
gui::EditorCardManager::Get().ShowCard("memory.hex_editor");
},
"Ctrl+0")
#ifdef YAZE_WITH_GRPC
.Item(
"AI Agent", ICON_MD_SMART_TOY,
[this]() { agent_editor_.set_active(true); }, "Ctrl+Shift+A")
.Item(
"Chat History", ICON_MD_CHAT,
[this]() { agent_chat_history_popup_.Toggle(); }, "Ctrl+H")
.Item(
"Proposal Drawer", ICON_MD_PREVIEW,
[this]() { proposal_drawer_.Toggle(); }, "Ctrl+P")
#endif
.Separator();
menu_builder_.Item(
"Card Browser", ICON_MD_DASHBOARD,
[this]() { show_card_browser_ = true; }, "Ctrl+Shift+B")
.Separator()
.Item("Welcome Screen", ICON_MD_HOME,
[this]() { show_welcome_screen_ = true; })
.Item(
"Command Palette", ICON_MD_TERMINAL,
[this]() { show_command_palette_ = true; }, "Ctrl+Shift+P")
.Item(
"Emulator", ICON_MD_GAMEPAD, [this]() { show_emulator_ = true; },
nullptr, nullptr, [this]() { return show_emulator_; })
.EndMenu();
// Window Menu - layout and session management
menu_builder_.BeginMenu("Window")
.BeginSubMenu("Sessions", ICON_MD_TAB)
.Item(
"New Session", ICON_MD_ADD, [this]() { CreateNewSession(); },
"Ctrl+Shift+N")
.Item(
"Duplicate Session", ICON_MD_CONTENT_COPY,
[this]() { DuplicateCurrentSession(); }, nullptr,
[this]() { return current_rom_ != nullptr; })
.Item(
"Close Session", ICON_MD_CLOSE, [this]() { CloseCurrentSession(); },
"Ctrl+Shift+W", [this]() { return sessions_.size() > 1; })
.Separator()
.Item(
"Session Switcher", ICON_MD_SWITCH_ACCOUNT,
[this]() { show_session_switcher_ = true; }, "Ctrl+Tab",
[this]() { return sessions_.size() > 1; })
.Item("Session Manager", ICON_MD_VIEW_LIST,
[this]() { show_session_manager_ = true; })
.EndMenu()
.Separator()
.Item(
"Save Layout", ICON_MD_SAVE, [this]() { SaveWorkspaceLayout(); },
"Ctrl+Shift+S")
.Item(
"Load Layout", ICON_MD_FOLDER_OPEN,
[this]() { LoadWorkspaceLayout(); }, "Ctrl+Shift+O")
.Item("Reset Layout", ICON_MD_RESET_TV,
[this]() { ResetWorkspaceLayout(); })
.Item("Layout Presets", ICON_MD_BOOKMARK,
[this]() { show_layout_presets_ = true; })
.Separator()
.Item("Show All Windows", ICON_MD_VISIBILITY,
[this]() { ShowAllWindows(); })
.Item("Hide All Windows", ICON_MD_VISIBILITY_OFF,
[this]() { HideAllWindows(); })
.Item(
"Maximize Current", ICON_MD_FULLSCREEN,
[this]() { MaximizeCurrentWindow(); }, "F11")
.Item("Restore All", ICON_MD_FULLSCREEN_EXIT,
[this]() { RestoreAllWindows(); })
.Item("Close All Floating", ICON_MD_CLOSE_FULLSCREEN,
[this]() { CloseAllFloatingWindows(); })
.EndMenu();
#ifdef YAZE_WITH_GRPC
// Collaboration Menu - combined Agent + Network features
menu_builder_.BeginMenu("Collaborate")
.Item(
"AI Agent Chat", ICON_MD_SMART_TOY,
[this]() { agent_editor_.SetChatActive(true); }, "Ctrl+Shift+A",
nullptr, [this]() { return agent_editor_.IsChatActive(); })
.Item(
"Proposal Drawer", ICON_MD_RATE_REVIEW,
[this]() { show_proposal_drawer_ = !show_proposal_drawer_; }, nullptr,
nullptr, [this]() { return show_proposal_drawer_; })
.Separator()
.Item("Host Session", ICON_MD_ADD_CIRCLE,
[this]() {
auto result = agent_editor_.HostSession("New Session");
if (result.ok()) {
toast_manager_.Show("Hosted session: " + result->session_name,
ToastType::kSuccess);
} else {
toast_manager_.Show("Failed to host session: " +
std::string(result.status().message()),
ToastType::kError);
}
})
.Item("Join Session", ICON_MD_LOGIN,
[this]() { popup_manager_->Show("Join Collaboration Session"); })
.Item(
"Leave Session", ICON_MD_LOGOUT,
[this]() {
status_ = agent_editor_.LeaveSession();
if (status_.ok()) {
toast_manager_.Show("Left collaboration session",
ToastType::kInfo);
}
},
nullptr, [this]() { return agent_editor_.IsInSession(); })
.Item(
"Refresh Session", ICON_MD_REFRESH,
[this]() {
auto result = agent_editor_.RefreshSession();
if (result.ok()) {
toast_manager_.Show(
"Session refreshed: " +
std::to_string(result->participants.size()) +
" participants",
ToastType::kSuccess);
}
},
nullptr, [this]() { return agent_editor_.IsInSession(); })
.Separator()
.Item("Connect to Server", ICON_MD_CLOUD_UPLOAD,
[this]() { popup_manager_->Show("Connect to Server"); })
.Item(
"Disconnect from Server", ICON_MD_CLOUD_OFF,
[this]() {
agent_editor_.DisconnectFromServer();
toast_manager_.Show("Disconnected from server", ToastType::kInfo);
},
nullptr, [this]() { return agent_editor_.IsConnectedToServer(); })
.Separator()
.Item("Capture Active Editor", ICON_MD_SCREENSHOT,
[this]() {
std::filesystem::path output;
AgentEditor::CaptureConfig config;
config.mode =
AgentEditor::CaptureConfig::CaptureMode::kActiveEditor;
status_ = agent_editor_.CaptureSnapshot(&output, config);
})
.Item("Capture Full Window", ICON_MD_FULLSCREEN,
[this]() {
std::filesystem::path output;
AgentEditor::CaptureConfig config;
config.mode =
AgentEditor::CaptureConfig::CaptureMode::kFullWindow;
status_ = agent_editor_.CaptureSnapshot(&output, config);
})
.Separator()
.Item(
"Local Mode", ICON_MD_FOLDER, [this]() { /* Set local mode */ },
nullptr, nullptr,
[this]() {
return agent_editor_.GetCurrentMode() ==
AgentEditor::CollaborationMode::kLocal;
})
.Item(
"Network Mode", ICON_MD_WIFI, [this]() { /* Set network mode */ },
nullptr, nullptr,
[this]() {
return agent_editor_.GetCurrentMode() ==
AgentEditor::CollaborationMode::kNetwork;
})
.EndMenu();
#endif
// Debug Menu - comprehensive development tools
menu_builder_.BeginMenu("Debug");
#ifdef YAZE_ENABLE_TESTING
// Testing and Validation section
menu_builder_
.Item(
"Test Dashboard", ICON_MD_SCIENCE,
[this]() { show_test_dashboard_ = true; }, "Ctrl+T")
.Item("Run All Tests", ICON_MD_PLAY_ARROW,
[this]() {
[[maybe_unused]] auto status =
test::TestManager::Get().RunAllTests();
})
.Item("Run Unit Tests", ICON_MD_INTEGRATION_INSTRUCTIONS,
[this]() {
[[maybe_unused]] auto status =
test::TestManager::Get().RunTestsByCategory(
test::TestCategory::kUnit);
})
.Item("Run Integration Tests", ICON_MD_MEMORY,
[this]() {
[[maybe_unused]] auto status =
test::TestManager::Get().RunTestsByCategory(
test::TestCategory::kIntegration);
})
.Item("Run UI Tests", ICON_MD_VISIBILITY,
[this]() {
[[maybe_unused]] auto status =
test::TestManager::Get().RunTestsByCategory(
test::TestCategory::kUI);
})
.Item("Run Performance Tests", ICON_MD_SPEED,
[this]() {
[[maybe_unused]] auto status =
test::TestManager::Get().RunTestsByCategory(
test::TestCategory::kPerformance);
})
.Item("Run Memory Tests", ICON_MD_STORAGE,
[this]() {
[[maybe_unused]] auto status =
test::TestManager::Get().RunTestsByCategory(
test::TestCategory::kMemory);
})
.Item("Clear Test Results", ICON_MD_CLEAR_ALL,
[this]() { test::TestManager::Get().ClearResults(); })
.Separator();
#endif
// ROM and ASM Management
menu_builder_.BeginSubMenu("ROM Analysis", ICON_MD_STORAGE)
.Item(
"ROM Information", ICON_MD_INFO,
[this]() { popup_manager_->Show("ROM Information"); }, nullptr,
[this]() { return current_rom_ && current_rom_->is_loaded(); })
#ifdef YAZE_ENABLE_TESTING
.Item(
"Data Integrity Check", ICON_MD_ANALYTICS,
[this]() {
if (current_rom_) {
[[maybe_unused]] auto status =
test::TestManager::Get().RunTestSuite("RomIntegrity");
toast_manager_.Show("Running ROM integrity tests...",
ToastType::kInfo);
}
},
nullptr,
[this]() { return current_rom_ && current_rom_->is_loaded(); })
.Item(
"Test Save/Load", ICON_MD_SAVE_ALT,
[this]() {
if (current_rom_) {
[[maybe_unused]] auto status =
test::TestManager::Get().RunTestSuite("RomSaveLoad");
toast_manager_.Show("Running ROM save/load tests...",
ToastType::kInfo);
}
},
nullptr,
[this]() { return current_rom_ && current_rom_->is_loaded(); })
#endif
.EndMenu()
.BeginSubMenu("ZSCustomOverworld", ICON_MD_CODE)
.Item(
"Check ROM Version", ICON_MD_INFO,
[this]() {
if (current_rom_) {
uint8_t version =
(*current_rom_)[zelda3::OverworldCustomASMHasBeenApplied];
std::string version_str = (version == 0xFF)
? "Vanilla"
: absl::StrFormat("v%d", version);
toast_manager_.Show(
absl::StrFormat("ROM: %s | ZSCustomOverworld: %s",
current_rom_->title().c_str(),
version_str.c_str()),
ToastType::kInfo, 5.0f);
}
},
nullptr,
[this]() { return current_rom_ && current_rom_->is_loaded(); })
.Item(
"Upgrade ROM", ICON_MD_UPGRADE,
[this]() {
if (current_rom_) {
toast_manager_.Show("Use Overworld Editor to upgrade ROM version",
ToastType::kInfo, 4.0f);
}
},
nullptr,
[this]() { return current_rom_ && current_rom_->is_loaded(); })
.Item("Toggle Custom Loading", ICON_MD_SETTINGS,
[this]() {
auto& flags = core::FeatureFlags::get();
flags.overworld.kLoadCustomOverworld =
!flags.overworld.kLoadCustomOverworld;
toast_manager_.Show(
absl::StrFormat("Custom Overworld Loading: %s",
flags.overworld.kLoadCustomOverworld
? "Enabled"
: "Disabled"),
ToastType::kInfo);
})
.EndMenu()
.BeginSubMenu("Asar Integration", ICON_MD_BUILD)
.Item("Asar Status", ICON_MD_INFO,
[this]() { popup_manager_->Show("Asar Integration"); })
.Item(
"Toggle ASM Patch", ICON_MD_CODE,
[this]() {
if (current_rom_) {
auto& flags = core::FeatureFlags::get();
flags.overworld.kApplyZSCustomOverworldASM =
!flags.overworld.kApplyZSCustomOverworldASM;
toast_manager_.Show(
absl::StrFormat("ZSCustomOverworld ASM Application: %s",
flags.overworld.kApplyZSCustomOverworldASM
? "Enabled"
: "Disabled"),
ToastType::kInfo);
}
},
nullptr,
[this]() { return current_rom_ && current_rom_->is_loaded(); })
.Item("Load ASM File", ICON_MD_FOLDER_OPEN,
[this]() {
toast_manager_.Show("ASM file loading not yet implemented",
ToastType::kWarning);
})
.EndMenu()
.Separator()
// Development Tools
.Item("Memory Editor", ICON_MD_MEMORY,
[this]() {
gui::EditorCardManager::Get().ShowCard("memory.hex_editor");
})
.Item("Assembly Editor", ICON_MD_CODE,
[this]() {
gui::EditorCardManager::Get().ShowCard("assembly.editor");
})
.Item("Feature Flags", ICON_MD_FLAG,
[this]() { popup_manager_->Show("Feature Flags"); })
.Separator()
.Item("Performance Dashboard", ICON_MD_SPEED,
[this]() { show_performance_dashboard_ = true; })
#ifdef YAZE_WITH_GRPC
.Item("Agent Proposals", ICON_MD_PREVIEW,
[this]() { proposal_drawer_.Toggle(); })
#endif
.Separator()
.Item(
"ImGui Demo", ICON_MD_HELP, [this]() { show_imgui_demo_ = true; },
nullptr, nullptr, [this]() { return show_imgui_demo_; })
.Item(
"ImGui Metrics", ICON_MD_ANALYTICS,
[this]() { show_imgui_metrics_ = true; }, nullptr, nullptr,
[this]() { return show_imgui_metrics_; })
.EndMenu();
// Help Menu
menu_builder_.BeginMenu("Help")
.Item("Getting Started", ICON_MD_PLAY_ARROW,
[this]() { popup_manager_->Show("Getting Started"); })
.Item(
"About", ICON_MD_INFO, [this]() { popup_manager_->Show("About"); },
"F1")
.EndMenu();
menu_builder_.Draw();
// Delegate to MenuOrchestrator for clean separation of concerns
if (menu_orchestrator_) {
menu_orchestrator_->BuildMainMenu();
}
}
void EditorManager::DrawMenuBarExtras() {