yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
editor_manager.cc
Go to the documentation of this file.
1// Related header
2#include "editor_manager.h"
3
4// C system headers
5#include <cstdint>
6#include <cstring>
7
8// C++ standard library headers
9#include <chrono>
10#include <exception>
11#include <functional>
12#include <memory>
13#include <optional>
14#include <sstream>
15#include <string>
16#include <unordered_set>
17#include <utility>
18#include <vector>
19
20#ifdef __APPLE__
21#include <TargetConditionals.h>
22#endif
23
24// Third-party library headers
25#define IMGUI_DEFINE_MATH_OPERATORS
26#include "absl/status/status.h"
27#include "absl/status/statusor.h"
28#include "absl/strings/ascii.h"
29#include "absl/strings/match.h"
30#include "absl/strings/str_format.h"
31#include "absl/strings/str_split.h"
32#include "absl/strings/string_view.h"
33#include "imgui/imgui.h"
34
35// Project headers
36#include "app/application.h"
39#include "app/editor/editor.h"
59#include "app/emu/emulator.h"
63#include "app/gui/core/icons.h"
65#include "app/platform/timing.h"
67#include "core/features.h"
68#include "core/project.h"
75#include "rom/rom.h"
76#include "startup_flags.h"
77#include "util/file_util.h"
78#include "util/log.h"
79#include "util/macro.h"
80#include "yaze_config.h"
81#include "zelda3/game_data.h"
85
86// Conditional platform headers
87#ifdef __EMSCRIPTEN__
91#endif
92
93// Conditional test headers
94#ifdef YAZE_ENABLE_TESTING
99#endif
100#ifdef YAZE_ENABLE_GTEST
102#endif
103#ifdef YAZE_WITH_GRPC
105#endif
106
107namespace yaze::editor {
108
109namespace {
110// TODO: Move to EditorRegistry
111std::string GetEditorName(EditorType type) {
112 return kEditorNames[static_cast<int>(type)];
113}
114
115std::optional<EditorType> ParseEditorTypeFromString(absl::string_view name) {
116 const std::string lower = absl::AsciiStrToLower(std::string(name));
117 for (int i = 0; i < static_cast<int>(EditorType::kSettings) + 1; ++i) {
118 const std::string candidate = absl::AsciiStrToLower(
119 std::string(GetEditorName(static_cast<EditorType>(i))));
120 if (candidate == lower) {
121 return static_cast<EditorType>(i);
122 }
123 }
124 return std::nullopt;
125}
126
127std::string StripSessionPrefix(absl::string_view panel_id) {
128 if (panel_id.size() > 2 && panel_id[0] == 's' &&
129 absl::ascii_isdigit(panel_id[1])) {
130 const size_t dot = panel_id.find('.');
131 if (dot != absl::string_view::npos) {
132 return std::string(panel_id.substr(dot + 1));
133 }
134 }
135 return std::string(panel_id);
136}
137
138} // namespace
139
140// Static registry of editors that use the card-based layout system
141// These editors register their cards with EditorPanelManager and manage their
142// own windows They do NOT need the traditional ImGui::Begin/End wrapper - they
143// create cards internally
147
149 if (!current_editor_) {
150 return;
151 }
152
153 // Using PanelManager directly
154 std::string category =
157}
158
162
163void EditorManager::ApplyLayoutPreset(const std::string& preset_name) {
165}
166
175
176#ifdef YAZE_BUILD_AGENT_UI
177void EditorManager::ShowAIAgent() {
178 // Apply saved agent settings from the current project when opening the Agent
179 // UI to respect the user's preferred provider/model.
180 // TODO: Implement LoadAgentSettingsFromProject in AgentChat or AgentEditor
182}
183
184void EditorManager::ShowChatHistory() {
186}
187#endif
188
190 : project_manager_(&toast_manager_), rom_file_manager_(&toast_manager_) {
191 std::stringstream ss;
192 ss << YAZE_VERSION_MAJOR << "." << YAZE_VERSION_MINOR << "."
193 << YAZE_VERSION_PATCH;
194 ss >> version_;
195
196 // Initialize Core Context
197 editor_context_ = std::make_unique<GlobalEditorContext>(event_bus_);
199
200 // ============================================================================
201 // DELEGATION INFRASTRUCTURE INITIALIZATION
202 // ============================================================================
203 // EditorManager delegates responsibilities to specialized components:
204 // - SessionCoordinator: Multi-session UI and lifecycle management
205 // - MenuOrchestrator: Menu building and action routing
206 // - UICoordinator: UI drawing and state management
207 // - RomFileManager: ROM file I/O operations
208 // - ProjectManager: Project file operations
209 // - PanelManager: Panel-based editor UI management
210 // - ShortcutConfigurator: Keyboard shortcut registration
211 // - WindowDelegate: Window layout operations
212 // - PopupManager: Modal popup/dialog management
213 //
214 // EditorManager retains:
215 // - Session storage (sessions_) and current pointers (current_rom_, etc.)
216 // - Main update loop (iterates sessions, calls editor updates)
217 // - Asset loading (Initialize/Load on all editors)
218 // - Session ID tracking (current_session_id_)
219 //
220 // INITIALIZATION ORDER (CRITICAL):
221 // 1. PopupManager - MUST be first, MenuOrchestrator/UICoordinator take ref to
222 // it
223 // 2. SessionCoordinator - Independent, can be early
224 // 3. MenuOrchestrator - Depends on PopupManager, SessionCoordinator
225 // 4. UICoordinator - Depends on PopupManager, SessionCoordinator
226 // 5. ShortcutConfigurator - Created in Initialize(), depends on all above
227 //
228 // If this order is violated, you will get SIGSEGV crashes when menu callbacks
229 // try to call popup_manager_.Show() with an uninitialized PopupManager!
230 // ============================================================================
231
232 // STEP 1: Initialize PopupManager FIRST
233 popup_manager_ = std::make_unique<PopupManager>(this);
234 popup_manager_->Initialize(); // Registers all popups with PopupID constants
235
236 // STEP 2: Initialize SessionCoordinator (independent of popups)
237 session_coordinator_ = std::make_unique<SessionCoordinator>(
239
240 // STEP 3: Initialize MenuOrchestrator (depends on popup_manager_,
241 // session_coordinator_)
242 menu_orchestrator_ = std::make_unique<MenuOrchestrator>(
245
246 // Wire up card registry for Panels submenu in View menu
247 menu_orchestrator_->SetPanelManager(&panel_manager_);
248 menu_orchestrator_->SetStatusBar(&status_bar_);
249 menu_orchestrator_->SetUserSettings(&user_settings_);
250
251 session_coordinator_->SetEditorManager(this);
252 session_coordinator_->AddObserver(
253 this); // Register for session lifecycle events
254
255 // STEP 4: Initialize UICoordinator (depends on popup_manager_,
256 // session_coordinator_, panel_manager_)
257 ui_coordinator_ = std::make_unique<UICoordinator>(
261
262 // STEP 4.5: Initialize LayoutManager (DockBuilder layouts for editors)
263 layout_manager_ = std::make_unique<LayoutManager>();
264
265 // STEP 4.6: Initialize RightPanelManager (right-side sliding panels)
266 right_panel_manager_ = std::make_unique<RightPanelManager>();
267 right_panel_manager_->SetToastManager(&toast_manager_);
268 right_panel_manager_->SetProposalDrawer(&proposal_drawer_);
270
271 // Initialize ProjectManagementPanel for project/version management
272 project_management_panel_ = std::make_unique<ProjectManagementPanel>();
273 project_management_panel_->SetToastManager(&toast_manager_);
274 project_management_panel_->SetSwapRomCallback([this]() {
275 // Prompt user to select a new ROM for the project
278 if (!rom_path.empty()) {
280 auto status = current_project_.Save();
281 if (status.ok()) {
282 toast_manager_.Show("Project ROM updated. Reload to apply changes.",
284 } else {
285 toast_manager_.Show("Failed to update project ROM", ToastType::kError);
286 }
287 }
288 });
289 project_management_panel_->SetReloadRomCallback([this]() {
292 auto status = LoadProjectWithRom();
293 if (!status.ok()) {
295 absl::StrFormat("Failed to reload ROM: %s", status.message()),
297 }
298 }
299 });
300 project_management_panel_->SetSaveProjectCallback([this]() {
301 auto status = SaveProject();
302 if (status.ok()) {
303 toast_manager_.Show("Project saved", ToastType::kSuccess);
304 } else {
306 absl::StrFormat("Failed to save project: %s", status.message()),
308 }
309 });
310 project_management_panel_->SetBrowseFolderCallback(
311 [this](const std::string& type) {
313 if (!folder_path.empty()) {
314 if (type == "code") {
315 current_project_.code_folder = folder_path;
316 // Update assembly editor path
317 if (auto* editor_set = GetCurrentEditorSet()) {
318 editor_set->GetAssemblyEditor()->OpenFolder(folder_path);
319 panel_manager_.SetFileBrowserPath("Assembly", folder_path);
320 }
321 } else if (type == "assets") {
322 current_project_.assets_folder = folder_path;
323 }
324 toast_manager_.Show(absl::StrFormat("%s folder set: %s", type.c_str(),
325 folder_path.c_str()),
327 }
328 });
329 right_panel_manager_->SetProjectManagementPanel(
331
332 // STEP 4.6.1: Initialize LayoutCoordinator (facade for layout operations)
334 layout_deps.layout_manager = layout_manager_.get();
335 layout_deps.panel_manager = &panel_manager_;
336 layout_deps.ui_coordinator = ui_coordinator_.get();
337 layout_deps.toast_manager = &toast_manager_;
338 layout_deps.status_bar = &status_bar_;
339 layout_deps.right_panel_manager = right_panel_manager_.get();
340 layout_coordinator_.Initialize(layout_deps);
341
342 // STEP 4.6.2: Initialize EditorActivator (editor switching and jump navigation)
343 EditorActivator::Dependencies activator_deps;
344 activator_deps.panel_manager = &panel_manager_;
345 activator_deps.layout_manager = layout_manager_.get();
346 activator_deps.ui_coordinator = ui_coordinator_.get();
347 activator_deps.right_panel_manager = right_panel_manager_.get();
348 activator_deps.toast_manager = &toast_manager_;
349 activator_deps.get_current_editor_set = [this]() {
350 return GetCurrentEditorSet();
351 };
352 activator_deps.get_current_session_id = [this]() {
353 return GetCurrentSessionId();
354 };
355 activator_deps.queue_deferred_action = [this](std::function<void()> action) {
356 QueueDeferredAction(std::move(action));
357 };
358 editor_activator_.Initialize(activator_deps);
359
360 // STEP 4.7: Initialize ActivityBar
361 activity_bar_ = std::make_unique<ActivityBar>(panel_manager_);
362
363 // Wire up PanelManager callbacks for ActivityBar buttons
367 }
368 });
371 right_panel_manager_->TogglePanel(
373 }
374 });
375
376 // STEP 4.8: Initialize DashboardPanel
377 dashboard_panel_ = std::make_unique<DashboardPanel>(this);
379 {.card_id = "dashboard.main",
380 .display_name = "Dashboard",
381 .window_title = " Dashboard",
382 .icon = ICON_MD_DASHBOARD,
383 .category = "Dashboard",
384 .shortcut_hint = "F1",
385 .visibility_flag = dashboard_panel_->visibility_flag(),
386 .priority = 0});
387
388 // STEP 5: ShortcutConfigurator created later in Initialize() method
389 // It depends on all above coordinators being available
390}
391
393 // Unregister as observer before destruction
395 session_coordinator_->RemoveObserver(this);
396 }
397}
398
399// ============================================================================
400// SessionObserver Implementation
401// ============================================================================
402
403void EditorManager::OnSessionSwitched(size_t new_index, RomSession* session) {
404 // Update RightPanelManager with the new session's settings editor
405 if (right_panel_manager_ && session) {
406 right_panel_manager_->SetSettingsPanel(session->editors.GetSettingsPanel());
407 // Set up StatusBar reference for live toggling
408 if (auto* settings = session->editors.GetSettingsPanel()) {
409 settings->SetStatusBar(&status_bar_);
410 }
411 }
412
413 // Update properties panel with new ROM
414 if (session) {
416 }
417
418#ifdef YAZE_ENABLE_TESTING
419 test::TestManager::Get().SetCurrentRom(session ? &session->rom : nullptr);
420#endif
421
422 LOG_DEBUG("EditorManager", "Session switched to %zu via observer", new_index);
423}
424
425void EditorManager::OnSessionCreated(size_t index, RomSession* session) {
426 LOG_INFO("EditorManager", "Session %zu created via observer", index);
427}
428
430#ifdef YAZE_ENABLE_TESTING
431 // Update test manager - it will get the new current ROM on next switch
433#endif
434
435 LOG_INFO("EditorManager", "Session %zu closed via observer", index);
436}
437
438void EditorManager::OnSessionRomLoaded(size_t index, RomSession* session) {
439#ifdef YAZE_ENABLE_TESTING
440 if (session) {
442 }
443#endif
444
445 LOG_INFO("EditorManager", "ROM loaded in session %zu via observer", index);
446}
447
449 auto& test_manager = test::TestManager::Get();
450
451#ifdef YAZE_ENABLE_TESTING
452 // Register comprehensive test suites
453 test_manager.RegisterTestSuite(std::make_unique<test::IntegratedTestSuite>());
454 test_manager.RegisterTestSuite(
455 std::make_unique<test::PerformanceTestSuite>());
456 test_manager.RegisterTestSuite(std::make_unique<test::UITestSuite>());
457 test_manager.RegisterTestSuite(
458 std::make_unique<test::RomDependentTestSuite>());
459
460 // Register new E2E and ZSCustomOverworld test suites
461 test_manager.RegisterTestSuite(std::make_unique<test::E2ETestSuite>());
462 test_manager.RegisterTestSuite(
463 std::make_unique<test::ZSCustomOverworldTestSuite>());
464#endif
465
466 // Register Google Test suite if available
467#ifdef YAZE_ENABLE_GTEST
468 test_manager.RegisterTestSuite(std::make_unique<test::UnitTestSuite>());
469#endif
470
471 // Register z3ed AI Agent test suites (requires gRPC)
472#ifdef YAZE_WITH_GRPC
474#endif
475
476 // Update resource monitoring to track Arena state
477 test_manager.UpdateResourceStats();
478}
479
481 const std::string& filename) {
482 renderer_ = renderer;
483
484 // Inject card_registry into emulator and workspace_manager
487
488 // Point to a blank editor set when no ROM is loaded
489 // current_editor_set_ = &blank_editor_set_;
490
491 if (!filename.empty()) {
493 }
494
495 // Note: PopupManager is now initialized in constructor before
496 // MenuOrchestrator This ensures all menu callbacks can safely call
497 // popup_manager_.Show()
498
499 // Register emulator cards early (emulator Initialize might not be called)
500 // Using PanelManager directly
501 panel_manager_.RegisterPanel({.card_id = "emulator.cpu_debugger",
502 .display_name = "CPU Debugger",
503 .window_title = " CPU Debugger",
504 .icon = ICON_MD_BUG_REPORT,
505 .category = "Emulator",
506 .priority = 10});
507 panel_manager_.RegisterPanel({.card_id = "emulator.ppu_viewer",
508 .display_name = "PPU Viewer",
509 .window_title = " PPU Viewer",
511 .category = "Emulator",
512 .priority = 20});
513 panel_manager_.RegisterPanel({.card_id = "emulator.memory_viewer",
514 .display_name = "Memory Viewer",
515 .window_title = " Memory Viewer",
516 .icon = ICON_MD_MEMORY,
517 .category = "Emulator",
518 .priority = 30});
519 panel_manager_.RegisterPanel({.card_id = "emulator.breakpoints",
520 .display_name = "Breakpoints",
521 .window_title = " Breakpoints",
522 .icon = ICON_MD_STOP,
523 .category = "Emulator",
524 .priority = 40});
525 panel_manager_.RegisterPanel({.card_id = "emulator.performance",
526 .display_name = "Performance",
527 .window_title = " Performance",
528 .icon = ICON_MD_SPEED,
529 .category = "Emulator",
530 .priority = 50});
531 panel_manager_.RegisterPanel({.card_id = "emulator.ai_agent",
532 .display_name = "AI Agent",
533 .window_title = " AI Agent",
534 .icon = ICON_MD_SMART_TOY,
535 .category = "Emulator",
536 .priority = 60});
537 panel_manager_.RegisterPanel({.card_id = "emulator.save_states",
538 .display_name = "Save States",
539 .window_title = " Save States",
540 .icon = ICON_MD_SAVE,
541 .category = "Emulator",
542 .priority = 70});
543 panel_manager_.RegisterPanel({.card_id = "emulator.keyboard_config",
544 .display_name = "Keyboard Config",
545 .window_title = " Keyboard Config",
546 .icon = ICON_MD_KEYBOARD,
547 .category = "Emulator",
548 .priority = 80});
549 panel_manager_.RegisterPanel({.card_id = "emulator.virtual_controller",
550 .display_name = "Virtual Controller",
551 .window_title = " Virtual Controller",
553 .category = "Emulator",
554 .priority = 85});
555 panel_manager_.RegisterPanel({.card_id = "emulator.apu_debugger",
556 .display_name = "APU Debugger",
557 .window_title = " APU Debugger",
558 .icon = ICON_MD_AUDIOTRACK,
559 .category = "Emulator",
560 .priority = 90});
561 panel_manager_.RegisterPanel({.card_id = "emulator.audio_mixer",
562 .display_name = "Audio Mixer",
563 .window_title = " Audio Mixer",
564 .icon = ICON_MD_AUDIO_FILE,
565 .category = "Emulator",
566 .priority = 100});
567
568 // Register memory/hex editor card
569 panel_manager_.RegisterPanel({.card_id = "memory.hex_editor",
570 .display_name = "Hex Editor",
571 .window_title = ICON_MD_MEMORY " Hex Editor",
572 .icon = ICON_MD_MEMORY,
573 .category = "Memory",
574 .priority = 10});
575
576 // Initialize project file editor
578
579 // Initialize agent UI (no-op when agent UI is disabled)
582
583 // Note: AgentControlServer is started from Application::Initialize()
584 // after gRPC infrastructure is properly set up
585
586 // Load critical user settings first
588 if (!status_.ok()) {
589 LOG_WARN("EditorManager", "Failed to load user settings: %s",
590 status_.ToString().c_str());
591 }
592 // Apply sprite naming preference globally.
595
596 // Initialize WASM control and session APIs for browser/agent integration
597#ifdef __EMSCRIPTEN__
600 LOG_INFO("EditorManager", "WASM Control and Session APIs initialized");
601#endif
602
603 // Initialize ROM load options dialog callbacks
605 [this](const RomLoadOptionsDialog::LoadOptions& options) {
606 // Apply feature flags from dialog
607 auto& flags = core::FeatureFlags::get();
608 flags.overworld.kSaveOverworldMaps = options.save_overworld_maps;
609 flags.overworld.kSaveOverworldEntrances =
611 flags.overworld.kSaveOverworldExits = options.save_overworld_exits;
612 flags.overworld.kSaveOverworldItems = options.save_overworld_items;
613 flags.overworld.kLoadCustomOverworld = options.enable_custom_overworld;
614 flags.kSaveDungeonMaps = options.save_dungeon_maps;
615 flags.kSaveAllPalettes = options.save_all_palettes;
616 flags.kSaveGfxGroups = options.save_gfx_groups;
617
618 // Create project if requested
619 if (options.create_project && !options.project_name.empty()) {
622 options.project_name, options.project_path);
623 if (!status.ok()) {
624 toast_manager_.Show("Failed to create project", ToastType::kError);
625 } else {
626 toast_manager_.Show("Project created: " + options.project_name,
628 }
629 }
630
631 // Close dialog and show editor selection
633 if (ui_coordinator_) {
634 // dashboard_panel_->ClearRecentEditors();
635 ui_coordinator_->SetEditorSelectionVisible(true);
636 }
637
638 LOG_INFO("EditorManager", "ROM load options applied: preset=%s",
639 options.selected_preset.c_str());
640 });
641
642 // Initialize welcome screen callbacks
644 status_ = LoadRom();
645 // LoadRom() already handles closing welcome screen and showing editor
646 // selection
647 });
648
651 if (status_.ok() && ui_coordinator_) {
652 ui_coordinator_->SetWelcomeScreenVisible(false);
653 ui_coordinator_->SetWelcomeScreenManuallyClosed(true);
654 }
655 });
656
658 [this](const std::string& template_name) {
659 // Set the template for the ROM load options dialog
660 status_ = CreateNewProject(template_name);
661 if (status_.ok() && ui_coordinator_) {
662 ui_coordinator_->SetWelcomeScreenVisible(false);
663 ui_coordinator_->SetWelcomeScreenManuallyClosed(true);
664 }
665 });
666
667 welcome_screen_.SetOpenProjectCallback([this](const std::string& filepath) {
668 status_ = OpenRomOrProject(filepath);
669 if (status_.ok() && ui_coordinator_) {
670 ui_coordinator_->SetWelcomeScreenVisible(false);
671 ui_coordinator_->SetWelcomeScreenManuallyClosed(true);
672 }
673 });
674
676#ifdef YAZE_BUILD_AGENT_UI
677 ShowAIAgent();
678#endif
679 // Keep welcome screen visible - user may want to do other things
680 });
681
682 // Initialize editor selection dialog callback
683 // editor_selection_dialog_.SetSelectionCallback([this](EditorType type) {
684 // editor_selection_dialog_.MarkRecentlyUsed(type);
685 // // Pass true for from_dialog so the dialog isn't automatically dismissed
686 // SwitchToEditor(type, /*force_visible=*/false, /*from_dialog=*/true);
687 // });
688
689 // Load user settings - this must happen after context is initialized
690 // Apply font scale after loading
691 ImGui::GetIO().FontGlobalScale = user_settings_.prefs().font_global_scale;
692
693 // Apply welcome screen preference
695 ui_coordinator_->SetWelcomeScreenVisible(false);
696 ui_coordinator_->SetWelcomeScreenManuallyClosed(true);
697 }
698
699 // Defer workspace presets loading to avoid initialization crashes
700 // This will be called lazily when workspace features are accessed
701
702 // Set up sidebar utility icon callbacks
704 if (ui_coordinator_) {
705 ui_coordinator_->SetEmulatorVisible(true);
706 }
707 });
711 if (ui_coordinator_) {
712 ui_coordinator_->ShowPanelBrowser();
713 }
714 });
715
716 // Set up sidebar action button callbacks
718 if (GetCurrentRom() && GetCurrentRom()->is_loaded()) {
719 auto status = SaveRom();
720 if (status.ok()) {
721 toast_manager_.Show("ROM saved successfully", ToastType::kSuccess);
722 } else {
724 absl::StrFormat("Failed to save ROM: %s", status.message()),
726 }
727 }
728 });
729
731 if (auto* current_editor = GetCurrentEditor()) {
732 auto status = current_editor->Undo();
733 if (!status.ok()) {
735 absl::StrFormat("Undo failed: %s", status.message()),
737 }
738 }
739 });
740
742 if (auto* current_editor = GetCurrentEditor()) {
743 auto status = current_editor->Redo();
744 if (!status.ok()) {
746 absl::StrFormat("Redo failed: %s", status.message()),
748 }
749 }
750 });
751
753 if (ui_coordinator_) {
754 ui_coordinator_->ShowGlobalSearch();
755 }
756 });
757
759 if (ui_coordinator_) {
760 // Shortcut configuration is part of Settings
762 }
763 });
764
766 if (ui_coordinator_) {
767 ui_coordinator_->ShowCommandPalette();
768 }
769 });
770
772 if (popup_manager_) {
774 }
775 });
776
777 // Set up sidebar state change callbacks for persistence
778 // IMPORTANT: Register callbacks BEFORE applying state to avoid triggering Save() during init
780 [this](bool visible, bool expanded) {
784 });
785
787 [this](const std::string& category) {
790 });
791
792 panel_manager_.SetOnPanelClickedCallback([this](const std::string& category) {
794 // Switch to the editor associated with this card's category
795 // This ensures clicking a card opens/focuses the parent editor
796 if (type != EditorType::kSettings && type != EditorType::kUnknown) {
797 SwitchToEditor(type, true);
798 }
799 });
800
801 // Handle Activity Bar category selection - dismisses dashboard
803 [this](const std::string& category) {
804 // Transition startup surface to Editor state (dismisses dashboard)
805 if (ui_coordinator_) {
806 ui_coordinator_->SetStartupSurface(StartupSurface::kEditor);
807 }
808 });
809
810 // Enable file browser for Assembly category
812
813 // Set up file clicked callback to open files in Assembly editor
815 [this](const std::string& category, const std::string& path) {
816 if (category == "Assembly") {
817 // Open the file in the Assembly editor
818 if (auto* editor_set = GetCurrentEditorSet()) {
819 editor_set->GetAssemblyEditor()->ChangeActiveFile(path);
820 // Make sure Assembly editor is active
822 }
823 }
824 });
825
826 // Apply sidebar state from settings AFTER registering callbacks
827 // This triggers the callbacks but they should be safe now
834 }
835
836 // Initialize testing system only when tests are enabled
837#ifdef YAZE_ENABLE_TESTING
839#endif
840
841 // TestManager will be updated when ROMs are loaded via SetCurrentRom calls
842
843 ShortcutDependencies shortcut_deps;
844 shortcut_deps.editor_manager = this;
845 shortcut_deps.editor_registry = &editor_registry_;
846 shortcut_deps.menu_orchestrator = menu_orchestrator_.get();
847 shortcut_deps.rom_file_manager = &rom_file_manager_;
848 shortcut_deps.project_manager = &project_manager_;
849 shortcut_deps.session_coordinator = session_coordinator_.get();
850 shortcut_deps.ui_coordinator = ui_coordinator_.get();
851 shortcut_deps.workspace_manager = &workspace_manager_;
852 shortcut_deps.popup_manager = popup_manager_.get();
853 shortcut_deps.toast_manager = &toast_manager_;
854 shortcut_deps.panel_manager = &panel_manager_;
855
858}
859
861 const std::string& editor_name, const std::string& panels_str) {
862 const bool has_editor = !editor_name.empty();
863 const bool has_panels = !panels_str.empty();
864
865 if (!has_editor && !has_panels) {
866 return;
867 }
868
869 LOG_INFO("EditorManager",
870 "Processing startup flags: editor='%s', panels='%s'",
871 editor_name.c_str(), panels_str.c_str());
872
873 std::optional<EditorType> editor_type_to_open =
874 has_editor ? ParseEditorTypeFromString(editor_name) : std::nullopt;
875 if (has_editor && !editor_type_to_open.has_value()) {
876 LOG_WARN("EditorManager", "Unknown editor specified via flag: %s",
877 editor_name.c_str());
878 } else if (editor_type_to_open.has_value()) {
879 // Use EditorActivator to ensure layouts and default panels are initialized
880 SwitchToEditor(*editor_type_to_open, true, /*from_dialog=*/true);
881 }
882
883 // Open panels via PanelManager - works for any editor type
884 if (!has_panels) {
885 return;
886 }
887
888 const size_t session_id = GetCurrentSessionId();
889 std::string last_known_category = panel_manager_.GetActiveCategory();
890 bool applied_category_from_panel = false;
891
892 for (absl::string_view token :
893 absl::StrSplit(panels_str, ',', absl::SkipWhitespace())) {
894 if (token.empty()) {
895 continue;
896 }
897 std::string panel_name = std::string(absl::StripAsciiWhitespace(token));
898 LOG_DEBUG("EditorManager", "Attempting to open panel: '%s'",
899 panel_name.c_str());
900
901 const std::string lower_name = absl::AsciiStrToLower(panel_name);
902 if (lower_name == "welcome" || lower_name == "welcome_screen") {
903 if (ui_coordinator_) {
904 ui_coordinator_->SetWelcomeScreenBehavior(StartupVisibility::kShow);
905 }
906 continue;
907 }
908 if (lower_name == "dashboard" || lower_name == "dashboard.main" ||
909 lower_name == "editor_selection") {
910 if (dashboard_panel_) {
911 dashboard_panel_->Show();
912 }
913 if (ui_coordinator_) {
914 ui_coordinator_->SetDashboardBehavior(StartupVisibility::kShow);
915 }
917 continue;
918 }
919
920 // Special case: "Room <id>" opens a dungeon room
921 if (absl::StartsWith(panel_name, "Room ")) {
922 if (auto* editor_set = GetCurrentEditorSet()) {
923 try {
924 int room_id = std::stoi(panel_name.substr(5));
925 editor_set->GetDungeonEditor()->add_room(room_id);
926 } catch (const std::exception& e) {
927 LOG_WARN("EditorManager", "Invalid room ID format: %s",
928 panel_name.c_str());
929 }
930 }
931 continue;
932 }
933
934 std::optional<std::string> resolved_panel;
935 if (panel_manager_.GetPanelDescriptor(session_id, panel_name)) {
936 resolved_panel = panel_name;
937 } else {
938 for (const auto& [prefixed_id, descriptor] :
940 const std::string base_id = StripSessionPrefix(prefixed_id);
941 const std::string card_lower = absl::AsciiStrToLower(base_id);
942 const std::string display_lower =
943 absl::AsciiStrToLower(descriptor.display_name);
944
945 if (card_lower == lower_name || display_lower == lower_name) {
946 resolved_panel = base_id;
947 break;
948 }
949 }
950 }
951
952 if (!resolved_panel.has_value()) {
953 LOG_WARN("EditorManager",
954 "Unknown panel '%s' from --open_panels (known count: %zu)",
955 panel_name.c_str(),
957 continue;
958 }
959
960 if (panel_manager_.ShowPanel(session_id, *resolved_panel)) {
961 const auto* descriptor =
962 panel_manager_.GetPanelDescriptor(session_id, *resolved_panel);
963 if (descriptor && !applied_category_from_panel &&
964 descriptor->category != PanelManager::kDashboardCategory) {
965 panel_manager_.SetActiveCategory(descriptor->category);
966 applied_category_from_panel = true;
967 } else if (!applied_category_from_panel && descriptor &&
968 descriptor->category.empty() && !last_known_category.empty()) {
969 panel_manager_.SetActiveCategory(last_known_category);
970 }
971 } else {
972 LOG_WARN("EditorManager", "Failed to show panel '%s'",
973 resolved_panel->c_str());
974 }
975 }
976}
977
984
986 if (ui_coordinator_) {
987 ui_coordinator_->SetWelcomeScreenBehavior(welcome_mode_override_);
988 ui_coordinator_->SetDashboardBehavior(dashboard_mode_override_);
989 }
990
992 const bool sidebar_visible =
994 panel_manager_.SetSidebarVisible(sidebar_visible);
995 if (ui_coordinator_) {
996 ui_coordinator_->SetPanelSidebarVisible(sidebar_visible);
997 }
998 }
999
1000 // Force sidebar panel to collapse if Welcome Screen or Dashboard is explicitly shown
1001 // This prevents visual overlap/clutter on startup
1005 }
1006
1007 if (dashboard_panel_) {
1009 dashboard_panel_->Hide();
1011 dashboard_panel_->Show();
1012 }
1013 }
1014}
1015
1017 ApplyStartupVisibility(config);
1018 // Handle startup editor and panels
1019 std::string panels_str;
1020 for (size_t i = 0; i < config.open_panels.size(); ++i) {
1021 if (i > 0)
1022 panels_str += ",";
1023 panels_str += config.open_panels[i];
1024 }
1025 OpenEditorAndPanelsFromFlags(config.startup_editor, panels_str);
1026
1027 // Handle jump targets
1028 if (config.jump_to_room >= 0) {
1030 }
1031 if (config.jump_to_map >= 0) {
1033 }
1034}
1035
1053 // Process deferred actions from previous frame (both EditorManager and LayoutCoordinator)
1054 // This ensures actions that modify ImGui state (like layout resets)
1055 // are executed safely outside of menu/popup rendering contexts
1057 if (!deferred_actions_.empty()) {
1058 std::vector<std::function<void()>> actions_to_execute;
1059 actions_to_execute.swap(deferred_actions_);
1060 for (auto& action : actions_to_execute) {
1061 action();
1062 }
1063 }
1064
1065 // Update timing manager for accurate delta time across the application
1066 // This fixes animation timing issues that occur when mouse isn't moving
1067 // or window loses focus
1069
1070 // Check for layout rebuild requests and execute if needed (delegated to LayoutCoordinator)
1071 bool is_emulator_visible =
1072 ui_coordinator_ && ui_coordinator_->IsEmulatorVisible();
1073 EditorType current_type =
1075 layout_coordinator_.ProcessLayoutRebuild(current_type, is_emulator_visible);
1076
1077 // Execute keyboard shortcuts (registered via ShortcutConfigurator)
1079
1080 // Draw editor selection dialog (managed by UICoordinator)
1081 if (ui_coordinator_ && ui_coordinator_->IsEditorSelectionVisible()) {
1082 dashboard_panel_->Show();
1083 dashboard_panel_->Draw();
1084 if (!dashboard_panel_->IsVisible()) {
1085 ui_coordinator_->SetEditorSelectionVisible(false);
1086 }
1087 }
1088
1089 // Draw ROM load options dialog (ZSCustomOverworld, feature flags, project)
1092 }
1093
1094 // Draw panel browser (managed by UICoordinator)
1095 if (ui_coordinator_ && ui_coordinator_->IsPanelBrowserVisible()) {
1096 bool show = true;
1097 if (activity_bar_) {
1098 activity_bar_->DrawPanelBrowser(GetCurrentSessionId(), &show);
1099 }
1100 if (!show) {
1101 ui_coordinator_->SetPanelBrowserVisible(false);
1102 }
1103 }
1104
1105 // Update agent editor dashboard (chat drawn via RightPanelManager)
1107
1108 // Draw background grid effects for the entire viewport
1109 if (ui_coordinator_) {
1110 ui_coordinator_->DrawBackground();
1111 }
1112
1113 // Ensure TestManager always has the current ROM
1114 static Rom* last_test_rom = nullptr;
1115 auto* current_rom = GetCurrentRom();
1116 if (last_test_rom != current_rom) {
1117 LOG_DEBUG(
1118 "EditorManager",
1119 "EditorManager::Update - ROM changed, updating TestManager: %p -> "
1120 "%p",
1121 (void*)last_test_rom, (void*)current_rom);
1123 last_test_rom = current_rom;
1124 }
1125
1126 // CRITICAL: Draw UICoordinator UI components FIRST (before ROM checks)
1127 // This ensures Welcome Screen, Command Palette, etc. work even without ROM
1128 // loaded
1129 if (ui_coordinator_) {
1130 ui_coordinator_->DrawAllUI();
1131 }
1132
1133 // Get current editor set for sidebar/panel logic (needed before early return)
1134 auto* current_editor_set = GetCurrentEditorSet();
1135
1136 // Draw sidebar BEFORE early return so it appears even when no ROM is loaded
1137 // This fixes the issue where sidebar/panel drawing was unreachable without ROM
1138 if (ui_coordinator_ && ui_coordinator_->IsPanelSidebarVisible()) {
1139 // Get ALL editor categories (static list, always shown)
1140 auto all_categories = EditorRegistry::GetAllEditorCategories();
1141
1142 // Track which editors are currently active (for visual highlighting)
1143 std::unordered_set<std::string> active_editor_categories;
1144
1145 if (current_editor_set && session_coordinator_) {
1146 // ROM is loaded - collect active editors for highlighting
1147 for (size_t session_idx = 0;
1148 session_idx < session_coordinator_->GetTotalSessionCount();
1149 ++session_idx) {
1150 auto* session = static_cast<RomSession*>(
1151 session_coordinator_->GetSession(session_idx));
1152 if (!session || !session->rom.is_loaded()) {
1153 continue;
1154 }
1155
1156 for (auto* editor : session->editors.active_editors_) {
1157 if (*editor->active() && IsPanelBasedEditor(editor->type())) {
1158 std::string category =
1159 EditorRegistry::GetEditorCategory(editor->type());
1160 active_editor_categories.insert(category);
1161 }
1162 }
1163 }
1164
1165 // Add Emulator to active categories when it's visible
1166 if (ui_coordinator_->IsEmulatorVisible()) {
1167 active_editor_categories.insert("Emulator");
1168 }
1169 }
1170
1171 // Determine which category to show in sidebar
1172 std::string sidebar_category = panel_manager_.GetActiveCategory();
1173
1174 // If no active category, default to first in list
1175 if (sidebar_category.empty() && !all_categories.empty()) {
1176 sidebar_category = all_categories[0];
1177 panel_manager_.SetActiveCategory(sidebar_category);
1178 }
1179
1180 // Callback to check if ROM is loaded (for category enabled state)
1181 auto has_rom_callback = [this]() -> bool {
1182 auto* rom = GetCurrentRom();
1183 return rom && rom->is_loaded();
1184 };
1185
1186 // Draw VSCode-style sidebar with ALL categories (highlighting active ones)
1187 // Activity Bar is hidden until ROM is loaded (per startup flow design)
1188 if (activity_bar_ && ui_coordinator_->ShouldShowActivityBar()) {
1189 activity_bar_->Render(GetCurrentSessionId(), sidebar_category,
1190 all_categories, active_editor_categories,
1191 has_rom_callback);
1192 }
1193 }
1194
1195 // Draw right panel BEFORE early return (agent chat, proposals, settings)
1198 right_panel_manager_->Draw();
1199 }
1200
1201 // Update and draw status bar
1205 session_coordinator_->GetActiveSessionCount());
1206 }
1207 status_bar_.Draw();
1208
1209 // Autosave timer
1210 if (user_settings_.prefs().autosave_enabled && current_rom &&
1211 current_rom->dirty()) {
1212 autosave_timer_ += ImGui::GetIO().DeltaTime;
1214 autosave_timer_ = 0.0f;
1216 s.backup = true;
1217 s.save_new = false;
1218 auto st = current_rom->SaveToFile(s);
1219 if (st.ok()) {
1220 toast_manager_.Show("Autosave completed", editor::ToastType::kSuccess);
1221 } else {
1222 toast_manager_.Show(std::string(st.message()),
1224 }
1225 }
1226 } else {
1227 autosave_timer_ = 0.0f;
1228 }
1229
1230 // Check if ROM is loaded before allowing editor updates
1231 if (!current_editor_set) {
1232 // No ROM loaded - welcome screen shown by UICoordinator above
1233 // Sidebar and right panel have already been drawn above
1234 return absl::OkStatus();
1235 }
1236
1237 // Check if current ROM is valid
1238 if (!current_rom) {
1239 // No ROM loaded - welcome screen shown by UICoordinator above
1240 return absl::OkStatus();
1241 }
1242
1243 // ROM is loaded and valid - don't auto-show welcome screen
1244 // Welcome screen should only be shown manually at this point
1245
1246 // Delegate session updates to SessionCoordinator
1248 session_coordinator_->UpdateSessions();
1249 }
1250
1251 // Central panel drawing - once per frame for all EditorPanel instances
1252 // This draws panels based on active category, respecting pinned and persistent panels
1254
1255 if (ui_coordinator_ && ui_coordinator_->IsPerformanceDashboardVisible()) {
1257 }
1258
1259 // Proposal drawer is now drawn through RightPanelManager
1260 // Removed duplicate direct call - DrawProposalsPanel() in RightPanelManager handles it
1261
1262 // Update ROM context for agent UI
1263 if (current_rom && current_rom->is_loaded()) {
1264 agent_ui_.SetRomContext(current_rom);
1266 // Pass AsarWrapper instance from AssemblyEditor
1267 if (auto* editor_set = GetCurrentEditorSet()) {
1269 editor_set->GetAssemblyEditor()->asar_wrapper());
1270 }
1271 }
1272
1273 // Draw SessionCoordinator UI components
1275 session_coordinator_->DrawSessionSwitcher();
1276 session_coordinator_->DrawSessionManager();
1277 session_coordinator_->DrawSessionRenameDialog();
1278 }
1279
1280 return absl::OkStatus();
1281}
1282
1283// DrawContextSensitivePanelControl removed - card control is now in the sidebar
1284
1300 static bool show_display_settings = false;
1301
1302 if (ImGui::BeginMenuBar()) {
1303 // Sidebar toggle - Activity Bar visibility
1304 // Consistent button styling with other menubar buttons
1305 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
1306 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
1308 ImGui::PushStyleColor(ImGuiCol_ButtonActive,
1310
1311 // Highlight when active/visible
1313 ImGui::PushStyleColor(ImGuiCol_Text, gui::GetPrimaryVec4());
1314 } else {
1315 ImGui::PushStyleColor(ImGuiCol_Text, gui::GetTextSecondaryVec4());
1316 }
1317
1318 if (ui_coordinator_ && ui_coordinator_->IsPanelSidebarVisible()) {
1319 if (ImGui::SmallButton(ICON_MD_MENU)) {
1321 }
1322 } else {
1323 if (ImGui::SmallButton(ICON_MD_MENU_OPEN)) {
1325 }
1326 }
1327
1328 ImGui::PopStyleColor(4);
1329
1330 if (ImGui::IsItemHovered()) {
1331 const char* tooltip = panel_manager_.IsSidebarVisible()
1332 ? "Hide Activity Bar (Ctrl+B)"
1333 : "Show Activity Bar (Ctrl+B)";
1334 ImGui::SetTooltip("%s", tooltip);
1335 }
1336
1337 // Delegate menu building to MenuOrchestrator
1338 if (menu_orchestrator_) {
1339 menu_orchestrator_->BuildMainMenu();
1340 }
1341
1342 // Delegate menu bar extras to UICoordinator (status cluster on right)
1343 if (ui_coordinator_) {
1344 ui_coordinator_->DrawMenuBarExtras();
1345 }
1346
1347 ImGui::EndMenuBar();
1348 }
1349
1350 if (show_display_settings) {
1351 // Use the popup manager instead of a separate window
1353 show_display_settings = false; // Close the old-style window
1354 }
1355
1356 // ImGui debug windows (delegated to UICoordinator for visibility state)
1357 if (ui_coordinator_ && ui_coordinator_->IsImGuiDemoVisible()) {
1358 bool visible = true;
1359 ImGui::ShowDemoWindow(&visible);
1360 if (!visible) {
1361 ui_coordinator_->SetImGuiDemoVisible(false);
1362 }
1363 }
1364
1365 if (ui_coordinator_ && ui_coordinator_->IsImGuiMetricsVisible()) {
1366 bool visible = true;
1367 ImGui::ShowMetricsWindow(&visible);
1368 if (!visible) {
1369 ui_coordinator_->SetImGuiMetricsVisible(false);
1370 }
1371 }
1372
1373 // Using PanelManager directly
1374 if (auto* editor_set = GetCurrentEditorSet()) {
1375 // Pass the actual visibility flag pointer so the X button works
1376 bool* hex_visibility =
1377 panel_manager_.GetVisibilityFlag("memory.hex_editor");
1378 if (hex_visibility && *hex_visibility) {
1379 editor_set->GetMemoryEditor()->Update(*hex_visibility);
1380 }
1381
1382 if (ui_coordinator_ && ui_coordinator_->IsAsmEditorVisible()) {
1383 bool visible = true;
1384 editor_set->GetAssemblyEditor()->Update(visible);
1385 if (!visible) {
1386 ui_coordinator_->SetAsmEditorVisible(false);
1387 }
1388 }
1389 }
1390
1391 // Project file editor
1393 if (ui_coordinator_ && ui_coordinator_->IsPerformanceDashboardVisible()) {
1397 if (!gfx::PerformanceDashboard::Get().IsVisible()) {
1398 ui_coordinator_->SetPerformanceDashboardVisible(false);
1399 }
1400 }
1401
1402 // Testing interface (only when tests are enabled)
1403#ifdef YAZE_ENABLE_TESTING
1404 if (show_test_dashboard_) {
1405 auto& test_manager = test::TestManager::Get();
1406 test_manager.UpdateResourceStats(); // Update monitoring data
1407 test_manager.DrawTestDashboard(&show_test_dashboard_);
1408 }
1409#endif
1410
1411 // Update proposal drawer ROM context (drawing handled by RightPanelManager)
1413
1414 // Agent chat history popup (left side)
1416
1417 // Welcome screen is now drawn by UICoordinator::DrawAllUI()
1418 // Removed duplicate call to avoid showing welcome screen twice
1419
1420 // Emulator handling - run with UI when visible, or headless when running in background
1421 if (auto* current_rom = GetCurrentRom()) {
1422 if (ui_coordinator_ && ui_coordinator_->IsEmulatorVisible()) {
1423 // Full emulator with UI
1424 emulator_.Run(current_rom);
1425 } else if (emulator_.running() && emulator_.is_snes_initialized()) {
1426 // Emulator running in background (e.g., for music editor playback)
1427 // Use audio-focused mode when available for lower overhead and authentic sound
1430 } else {
1432 }
1433 }
1434 }
1435
1436 // Enhanced Global Search UI (managed by UICoordinator)
1437 // No longer here - handled by ui_coordinator_->DrawAllUI()
1438
1439 // NOTE: Editor updates are handled by SessionCoordinator::UpdateSessions()
1440 // which is called in EditorManager::Update(). Removed duplicate update loop
1441 // here that was causing EditorPanel::Begin() to be called twice per frame,
1442 // resulting in duplicate rendering detection logs.
1443
1444 if (ui_coordinator_ && ui_coordinator_->IsResourceLabelManagerVisible() &&
1445 GetCurrentRom()) {
1446 bool visible = true;
1447 GetCurrentRom()->resource_label()->DisplayLabels(&visible);
1451 GetCurrentRom()->resource_label()->filename_;
1452 }
1453 if (!visible) {
1454 ui_coordinator_->SetResourceLabelManagerVisible(false);
1455 }
1456 }
1457
1458 // Workspace preset dialogs are now in UICoordinator
1459
1460 // Layout presets UI (session dialogs are drawn by SessionCoordinator at lines
1461 // 907-915)
1462 if (ui_coordinator_) {
1463 ui_coordinator_->DrawLayoutPresets();
1464 }
1465}
1466
1490 auto load_from_path = [this](const std::string& file_name) -> absl::Status {
1491 if (file_name.empty()) {
1492 return absl::OkStatus();
1493 }
1494
1495 // Check if this is a project file - route to project loading
1496 if (absl::StrContains(file_name, ".yaze")) {
1497 return OpenRomOrProject(file_name);
1498 }
1499
1500 if (session_coordinator_->HasDuplicateSession(file_name)) {
1501 toast_manager_.Show("ROM already open in another session",
1503 return absl::OkStatus();
1504 }
1505
1506 // Delegate ROM loading to RomFileManager
1507 Rom temp_rom;
1508 RETURN_IF_ERROR(rom_file_manager_.LoadRom(&temp_rom, file_name));
1509
1510 auto session_or = session_coordinator_->CreateSessionFromRom(
1511 std::move(temp_rom), file_name);
1512 if (!session_or.ok()) {
1513 return session_or.status();
1514 }
1515
1518
1519 // Initialize resource labels for LoadRom() - use defaults with current project settings
1520 auto& label_provider = zelda3::GetResourceLabels();
1521 label_provider.SetProjectLabels(&current_project_.resource_labels);
1522 label_provider.SetPreferHMagicNames(
1524 LOG_INFO("EditorManager", "Initialized ResourceLabelProvider for LoadRom");
1525
1526#ifdef YAZE_ENABLE_TESTING
1528#endif
1529
1531 manager.AddFile(file_name);
1532 manager.Save();
1533
1535
1536 if (ui_coordinator_) {
1537 ui_coordinator_->SetWelcomeScreenVisible(false);
1538
1539 // Show ROM load options dialog for ZSCustomOverworld and feature settings
1542 }
1543
1544 return absl::OkStatus();
1545 };
1546
1547#if defined(__APPLE__) && TARGET_OS_IOS == 1
1550 [this, load_from_path](const std::string& file_name) {
1551 auto status = load_from_path(file_name);
1552 if (!status.ok()) {
1554 absl::StrFormat("Failed to load ROM: %s", status.message()),
1556 }
1557 });
1558 return absl::OkStatus();
1559#else
1562 return load_from_path(file_name);
1563#endif
1564}
1565
1566absl::Status EditorManager::LoadAssets(uint64_t passed_handle) {
1567 auto* current_rom = GetCurrentRom();
1568 auto* current_editor_set = GetCurrentEditorSet();
1569 if (!current_rom || !current_editor_set) {
1570 return absl::FailedPreconditionError("No ROM or editor set loaded");
1571 }
1572
1573 auto start_time = std::chrono::steady_clock::now();
1574
1575#ifdef __EMSCRIPTEN__
1576 // Use passed handle if provided, otherwise create new one
1577 auto loading_handle =
1578 passed_handle != 0
1579 ? static_cast<app::platform::WasmLoadingManager::LoadingHandle>(
1580 passed_handle)
1581 : app::platform::WasmLoadingManager::BeginLoading(
1582 "Loading Editor Assets");
1583
1584 // Progress starts at 10% (ROM already loaded), goes to 100%
1585 constexpr float kStartProgress = 0.10f;
1586 constexpr float kEndProgress = 1.0f;
1587 constexpr int kTotalSteps = 11; // Graphics + 8 editors + profiler + finish
1588 int current_step = 0;
1589 auto update_progress = [&](const std::string& message) {
1590 current_step++;
1591 float progress =
1592 kStartProgress + (kEndProgress - kStartProgress) *
1593 (static_cast<float>(current_step) / kTotalSteps);
1594 app::platform::WasmLoadingManager::UpdateProgress(loading_handle, progress);
1595 app::platform::WasmLoadingManager::UpdateMessage(loading_handle, message);
1596 };
1597 // RAII guard to ensure loading indicator is closed even on early return
1598 auto cleanup_loading = [&]() {
1599 app::platform::WasmLoadingManager::EndLoading(loading_handle);
1600 };
1601 struct LoadingGuard {
1602 std::function<void()> cleanup;
1603 bool dismissed = false;
1604 ~LoadingGuard() {
1605 if (!dismissed)
1606 cleanup();
1607 }
1608 void dismiss() { dismissed = true; }
1609 } loading_guard{cleanup_loading};
1610#else
1611 (void)passed_handle; // Unused on non-WASM
1612#endif
1613
1614 // Set renderer for emulator (lazy initialization happens in Run())
1615 if (renderer_) {
1617 }
1618
1619 // Initialize all editors - this registers their cards with PanelManager
1620 // and sets up any editor-specific resources. Must be called before Load().
1621 current_editor_set->GetOverworldEditor()->Initialize();
1622 current_editor_set->GetMessageEditor()->Initialize();
1623 current_editor_set->GetGraphicsEditor()->Initialize();
1624 current_editor_set->GetScreenEditor()->Initialize();
1625 current_editor_set->GetSpriteEditor()->Initialize();
1626 current_editor_set->GetPaletteEditor()->Initialize();
1627 current_editor_set->GetAssemblyEditor()->Initialize();
1628 current_editor_set->GetMusicEditor()->Initialize();
1629
1630 // Initialize the dungeon editor with the renderer
1631 current_editor_set->GetDungeonEditor()->Initialize(renderer_, current_rom);
1632
1633#ifdef __EMSCRIPTEN__
1634 update_progress("Loading graphics sheets...");
1635#endif
1636 // Get current session's GameData and load Zelda3-specific game data
1637 auto* current_session = session_coordinator_->GetActiveRomSession();
1638 if (!current_session) {
1639 return absl::FailedPreconditionError("No active ROM session");
1640 }
1641
1642 // Load all Zelda3-specific data (metadata, palettes, gfx groups, graphics)
1644 zelda3::LoadGameData(*current_rom, current_session->game_data));
1645
1646 // Copy loaded graphics to Arena for global access
1648 current_session->game_data.gfx_bitmaps;
1649
1650 // Propagate GameData to all editors that need it
1651 auto* game_data = &current_session->game_data;
1652 current_editor_set->GetDungeonEditor()->SetGameData(game_data);
1653 current_editor_set->GetOverworldEditor()->SetGameData(game_data);
1654 current_editor_set->GetGraphicsEditor()->SetGameData(game_data);
1655 current_editor_set->GetScreenEditor()->SetGameData(game_data);
1656 current_editor_set->GetPaletteEditor()->SetGameData(game_data);
1657 current_editor_set->GetSpriteEditor()->SetGameData(game_data);
1658 current_editor_set->GetMessageEditor()->SetGameData(game_data);
1659
1660#ifdef __EMSCRIPTEN__
1661 update_progress("Loading overworld...");
1662#endif
1663 RETURN_IF_ERROR(current_editor_set->GetOverworldEditor()->Load());
1664
1665#ifdef __EMSCRIPTEN__
1666 update_progress("Loading dungeons...");
1667#endif
1668 RETURN_IF_ERROR(current_editor_set->GetDungeonEditor()->Load());
1669
1670#ifdef __EMSCRIPTEN__
1671 update_progress("Loading screen editor...");
1672#endif
1673 RETURN_IF_ERROR(current_editor_set->GetScreenEditor()->Load());
1674
1675#ifdef __EMSCRIPTEN__
1676 update_progress("Loading graphics editor...");
1677#endif
1678 RETURN_IF_ERROR(current_editor_set->GetGraphicsEditor()->Load());
1679
1680#ifdef __EMSCRIPTEN__
1681 update_progress("Loading settings...");
1682#endif
1683 // Settings panel doesn't need Load()
1684 // RETURN_IF_ERROR(current_editor_set->settings_editor_.Load());
1685
1686#ifdef __EMSCRIPTEN__
1687 update_progress("Loading sprites...");
1688#endif
1689 RETURN_IF_ERROR(current_editor_set->GetSpriteEditor()->Load());
1690
1691#ifdef __EMSCRIPTEN__
1692 update_progress("Loading messages...");
1693#endif
1694 RETURN_IF_ERROR(current_editor_set->GetMessageEditor()->Load());
1695
1696#ifdef __EMSCRIPTEN__
1697 update_progress("Loading music...");
1698#endif
1699 RETURN_IF_ERROR(current_editor_set->GetMusicEditor()->Load());
1700
1701#ifdef __EMSCRIPTEN__
1702 update_progress("Loading palettes...");
1703#endif
1704 RETURN_IF_ERROR(current_editor_set->GetPaletteEditor()->Load());
1705
1706#ifdef __EMSCRIPTEN__
1707 update_progress("Finishing up...");
1708#endif
1709
1710 // Set up RightPanelManager with session's settings editor
1712 auto* settings = current_editor_set->GetSettingsPanel();
1713 right_panel_manager_->SetSettingsPanel(settings);
1714 // Also update project context for settings panel
1715 if (settings) {
1716 settings->SetProject(&current_project_);
1717 }
1718 }
1719
1720 // Set up StatusBar reference on settings panel for live toggling
1721 if (auto* settings = current_editor_set->GetSettingsPanel()) {
1722 settings->SetStatusBar(&status_bar_);
1723 }
1724
1725 // Apply user preferences to status bar
1727
1729
1730#ifdef __EMSCRIPTEN__
1731 // Dismiss the guard and manually close - we completed successfully
1732 loading_guard.dismiss();
1733 app::platform::WasmLoadingManager::EndLoading(loading_handle);
1734#endif
1735
1736 auto end_time = std::chrono::steady_clock::now();
1737 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
1738 end_time - start_time);
1739 LOG_DEBUG("EditorManager", "ROM assets loaded in %lld ms", duration.count());
1740
1741 return absl::OkStatus();
1742}
1743
1760 auto* current_rom = GetCurrentRom();
1761 auto* current_editor_set = GetCurrentEditorSet();
1762 if (!current_rom || !current_editor_set) {
1763 return absl::FailedPreconditionError("No ROM or editor set loaded");
1764 }
1765
1766 // Save editor-specific data first
1767 if (core::FeatureFlags::get().kSaveDungeonMaps) {
1769 *current_rom, current_editor_set->GetScreenEditor()->dungeon_maps_));
1770 }
1771
1772 RETURN_IF_ERROR(current_editor_set->GetOverworldEditor()->Save());
1773
1774 if (core::FeatureFlags::get().kSaveGraphicsSheet)
1776 *current_rom, gfx::Arena::Get().gfx_sheets()));
1777
1778 // Delegate final ROM file writing to RomFileManager
1779 return rom_file_manager_.SaveRom(current_rom);
1780}
1781
1782absl::Status EditorManager::SaveRomAs(const std::string& filename) {
1783 auto* current_rom = GetCurrentRom();
1784 if (!current_rom) {
1785 return absl::FailedPreconditionError("No ROM loaded");
1786 }
1787
1788 // Reuse SaveRom() logic for editor-specific data saving
1790
1791 // Now save to the new filename
1792 auto save_status = rom_file_manager_.SaveRomAs(current_rom, filename);
1793 if (save_status.ok()) {
1794 // Update session filepath
1796 auto* session = session_coordinator_->GetActiveRomSession();
1797 if (session) {
1798 session->filepath = filename;
1799 }
1800 }
1801
1802 // Add to recent files
1804 manager.AddFile(filename);
1805 manager.Save();
1806 }
1807
1808 return save_status;
1809}
1810
1811absl::Status EditorManager::OpenRomOrProject(const std::string& filename) {
1812 LOG_INFO("EditorManager", "OpenRomOrProject called with: '%s'",
1813 filename.c_str());
1814 if (filename.empty()) {
1815 LOG_INFO("EditorManager", "Empty filename provided, skipping load.");
1816 return absl::OkStatus();
1817 }
1818
1819#ifdef __EMSCRIPTEN__
1820 // Start loading indicator early for WASM builds
1821 auto loading_handle =
1822 app::platform::WasmLoadingManager::BeginLoading("Loading ROM");
1823 app::platform::WasmLoadingManager::UpdateMessage(loading_handle,
1824 "Reading ROM file...");
1825 // RAII guard to ensure loading indicator is closed even on early return
1826 struct LoadingGuard {
1827 app::platform::WasmLoadingManager::LoadingHandle handle;
1828 bool dismissed = false;
1829 ~LoadingGuard() {
1830 if (!dismissed)
1831 app::platform::WasmLoadingManager::EndLoading(handle);
1832 }
1833 void dismiss() { dismissed = true; }
1834 } loading_guard{loading_handle};
1835#endif
1836
1837 if (absl::StrContains(filename, ".yaze")) {
1838 // Open the project file
1840
1841 // Initialize VersionManager for the project
1843 std::make_unique<core::VersionManager>(&current_project_);
1844 version_manager_->InitializeGit(); // Try to init git if configured
1845
1846 // Load ROM directly from project - don't prompt user
1847 return LoadProjectWithRom();
1848 } else {
1849#ifdef __EMSCRIPTEN__
1850 app::platform::WasmLoadingManager::UpdateProgress(loading_handle, 0.05f);
1851 app::platform::WasmLoadingManager::UpdateMessage(loading_handle,
1852 "Loading ROM data...");
1853#endif
1854 Rom temp_rom;
1855 RETURN_IF_ERROR(rom_file_manager_.LoadRom(&temp_rom, filename));
1856
1857 auto session_or = session_coordinator_->CreateSessionFromRom(
1858 std::move(temp_rom), filename);
1859 if (!session_or.ok()) {
1860 return session_or.status();
1861 }
1862 RomSession* session = *session_or;
1863
1866
1867 // Apply project feature flags to both session and global singleton
1870
1871 // Initialize resource labels for ROM-only loading (use defaults)
1872 // This ensures labels are available before any editors access them
1873 auto& label_provider = zelda3::GetResourceLabels();
1874 label_provider.SetProjectLabels(&current_project_.resource_labels);
1875 label_provider.SetPreferHMagicNames(
1877 LOG_INFO("EditorManager",
1878 "Initialized ResourceLabelProvider for ROM-only load");
1879
1880 // Update test manager with current ROM for ROM-dependent tests (only when
1881 // tests are enabled)
1882#ifdef YAZE_ENABLE_TESTING
1883 LOG_DEBUG("EditorManager", "Setting ROM in TestManager - %p ('%s')",
1884 (void*)GetCurrentRom(),
1885 GetCurrentRom() ? GetCurrentRom()->title().c_str() : "null");
1887#endif
1888
1889 if (auto* editor_set = GetCurrentEditorSet();
1890 editor_set && !current_project_.code_folder.empty()) {
1891 editor_set->GetAssemblyEditor()->OpenFolder(current_project_.code_folder);
1892 // Also set the sidebar file browser path
1895 }
1896
1897#ifdef __EMSCRIPTEN__
1898 app::platform::WasmLoadingManager::UpdateProgress(loading_handle, 0.10f);
1899 app::platform::WasmLoadingManager::UpdateMessage(loading_handle,
1900 "Initializing editors...");
1901 // Pass the loading handle to LoadAssets and dismiss our guard
1902 // LoadAssets will manage closing the indicator when done
1903 loading_guard.dismiss();
1904 RETURN_IF_ERROR(LoadAssets(loading_handle));
1905#else
1907#endif
1908
1909 // Hide welcome screen and show editor selection when ROM is loaded
1910 ui_coordinator_->SetWelcomeScreenVisible(false);
1911 // dashboard_panel_->ClearRecentEditors();
1912 ui_coordinator_->SetEditorSelectionVisible(true);
1913
1914 // Set Dashboard category to suppress panel drawing until user selects an editor
1916 }
1917 return absl::OkStatus();
1918}
1919
1920absl::Status EditorManager::CreateNewProject(const std::string& template_name) {
1921 // Delegate to ProjectManager
1922 auto status = project_manager_.CreateNewProject(template_name);
1923 if (status.ok()) {
1925
1926 // Trigger ROM selection dialog - projects need a ROM to be useful
1927 // LoadRom() opens file dialog and shows ROM load options when ROM is loaded
1928 status = LoadRom();
1929#if !(defined(__APPLE__) && TARGET_OS_IOS == 1)
1930 if (status.ok() && ui_coordinator_) {
1931 ui_coordinator_->SetWelcomeScreenVisible(false);
1932 ui_coordinator_->SetWelcomeScreenManuallyClosed(true);
1933 }
1934#endif
1935 }
1936 return status;
1937}
1938
1940 auto open_project_from_path =
1941 [this](const std::string& file_path) -> absl::Status {
1942 if (file_path.empty()) {
1943 return absl::OkStatus();
1944 }
1945
1946 project::YazeProject new_project;
1947 RETURN_IF_ERROR(new_project.Open(file_path));
1948
1949 // Validate project
1950 auto validation_status = new_project.Validate();
1951 if (!validation_status.ok()) {
1952 toast_manager_.Show(absl::StrFormat("Project validation failed: %s",
1953 validation_status.message()),
1955
1956 // Ask user if they want to repair
1957 popup_manager_->Show("Project Repair");
1958 }
1959
1960 current_project_ = std::move(new_project);
1961
1962 // Initialize VersionManager for the project
1964 std::make_unique<core::VersionManager>(&current_project_);
1965 version_manager_->InitializeGit();
1966
1967 return LoadProjectWithRom();
1968 };
1969
1970#if defined(__APPLE__) && TARGET_OS_IOS == 1
1973 [this, open_project_from_path](const std::string& file_path) {
1974 auto status = open_project_from_path(file_path);
1975 if (!status.ok()) {
1977 absl::StrFormat("Failed to open project: %s", status.message()),
1979 }
1980 });
1981 return absl::OkStatus();
1982#else
1984 return open_project_from_path(file_path);
1985#endif
1986}
1987
1989 // Check if project has a ROM file specified
1990 if (current_project_.rom_filename.empty()) {
1991 // No ROM specified - prompt user to select one
1993 "Project has no ROM file configured. Please select a ROM.",
1995#if defined(__APPLE__) && TARGET_OS_IOS == 1
1998 [this](const std::string& rom_path) {
1999 if (rom_path.empty()) {
2000 return;
2001 }
2002 current_project_.rom_filename = rom_path;
2003 auto save_status = current_project_.Save();
2004 if (!save_status.ok()) {
2006 absl::StrFormat("Failed to update project ROM: %s",
2007 save_status.message()),
2009 return;
2010 }
2011 auto status = LoadProjectWithRom();
2012 if (!status.ok()) {
2014 absl::StrFormat("Failed to load project ROM: %s",
2015 status.message()),
2017 }
2018 });
2019 return absl::OkStatus();
2020#else
2023 if (rom_path.empty()) {
2024 return absl::OkStatus();
2025 }
2026 current_project_.rom_filename = rom_path;
2027 // Save updated project
2029#endif
2030 }
2031
2032 // Load ROM from project
2033 Rom temp_rom;
2034 auto load_status =
2036 if (!load_status.ok()) {
2037 // ROM file not found or invalid - prompt user to select new ROM
2039 absl::StrFormat("Could not load ROM '%s': %s. Please select a new ROM.",
2040 current_project_.rom_filename, load_status.message()),
2042#if defined(__APPLE__) && TARGET_OS_IOS == 1
2045 [this](const std::string& rom_path) {
2046 if (rom_path.empty()) {
2047 return;
2048 }
2049 current_project_.rom_filename = rom_path;
2050 auto save_status = current_project_.Save();
2051 if (!save_status.ok()) {
2053 absl::StrFormat("Failed to update project ROM: %s",
2054 save_status.message()),
2056 return;
2057 }
2058 auto status = LoadProjectWithRom();
2059 if (!status.ok()) {
2061 absl::StrFormat("Failed to load project ROM: %s",
2062 status.message()),
2064 }
2065 });
2066 return absl::OkStatus();
2067#else
2070 if (rom_path.empty()) {
2071 return absl::OkStatus();
2072 }
2073 current_project_.rom_filename = rom_path;
2075 RETURN_IF_ERROR(rom_file_manager_.LoadRom(&temp_rom, rom_path));
2076#endif
2077 }
2078
2079 auto session_or = session_coordinator_->CreateSessionFromRom(
2080 std::move(temp_rom), current_project_.rom_filename);
2081 if (!session_or.ok()) {
2082 return session_or.status();
2083 }
2084 RomSession* session = *session_or;
2085
2088
2089 // Apply project feature flags to both session and global singleton
2092
2093 // Update test manager with current ROM for ROM-dependent tests (only when
2094 // tests are enabled)
2095#ifdef YAZE_ENABLE_TESTING
2096 LOG_DEBUG("EditorManager", "Setting ROM in TestManager - %p ('%s')",
2097 (void*)GetCurrentRom(),
2098 GetCurrentRom() ? GetCurrentRom()->title().c_str() : "null");
2100#endif
2101
2102 if (auto* editor_set = GetCurrentEditorSet();
2103 editor_set && !current_project_.code_folder.empty()) {
2104 editor_set->GetAssemblyEditor()->OpenFolder(current_project_.code_folder);
2105 // Also set the sidebar file browser path
2107 }
2108
2110
2111 // Hide welcome screen and show editor selection when project ROM is loaded
2112 if (ui_coordinator_) {
2113 ui_coordinator_->SetWelcomeScreenVisible(false);
2114 ui_coordinator_->SetEditorSelectionVisible(true);
2115 }
2116
2117 // Set Dashboard category to suppress panel drawing until user selects an editor
2119
2120 // Apply workspace settings
2127 ImGui::GetIO().FontGlobalScale = user_settings_.prefs().font_global_scale;
2128
2129 // Initialize resource labels early - before any editors access them
2131 LOG_INFO("EditorManager",
2132 "Initialized ResourceLabelProvider with project labels");
2133
2134 // Add to recent files
2136 manager.AddFile(current_project_.filepath);
2137 manager.Save();
2138
2139 // Update project management panel with loaded project
2142 project_management_panel_->SetVersionManager(version_manager_.get());
2144 }
2145
2146 toast_manager_.Show(absl::StrFormat("Project '%s' loaded successfully",
2149
2150 return absl::OkStatus();
2151}
2152
2155 return CreateNewProject();
2156 }
2157
2158 // Update project with current settings
2161 auto* session = session_coordinator_->GetActiveRomSession();
2162 if (session) {
2163 current_project_.feature_flags = session->feature_flags;
2164 }
2165 }
2166
2173
2174 // Save recent files
2177 for (const auto& file : manager.GetRecentFiles()) {
2179 }
2180 }
2181
2182 return current_project_.Save();
2183}
2184
2186 // Get current project name for default filename
2187 std::string default_name = current_project_.project_opened()
2189 : "untitled_project";
2190
2191 auto file_path =
2192 util::FileDialogWrapper::ShowSaveFileDialog(default_name, "yaze");
2193 if (file_path.empty()) {
2194 return absl::OkStatus();
2195 }
2196
2197 // Ensure .yaze extension
2198 if (file_path.find(".yaze") == std::string::npos) {
2199 file_path += ".yaze";
2200 }
2201
2202 // Update project filepath and save
2203 std::string old_filepath = current_project_.filepath;
2204 current_project_.filepath = file_path;
2205
2206 auto save_status = current_project_.Save();
2207 if (save_status.ok()) {
2208 // Add to recent files
2210 manager.AddFile(file_path);
2211 manager.Save();
2212
2213 toast_manager_.Show(absl::StrFormat("Project saved as: %s", file_path),
2215 } else {
2216 // Restore old filepath on failure
2217 current_project_.filepath = old_filepath;
2219 absl::StrFormat("Failed to save project: %s", save_status.message()),
2221 }
2222
2223 return save_status;
2224}
2225
2226absl::Status EditorManager::ImportProject(const std::string& project_path) {
2227 // Delegate to ProjectManager for import logic
2229 // Sync local project reference
2231 return absl::OkStatus();
2232}
2233
2236 return absl::FailedPreconditionError("No project is currently open");
2237 }
2238
2240 toast_manager_.Show("Project repaired successfully",
2242
2243 return absl::OkStatus();
2244}
2245
2247 if (!rom) {
2248 return absl::InvalidArgumentError("Invalid ROM pointer");
2249 }
2250
2251 // We need to find the session that owns this ROM.
2252 // This is inefficient but SetCurrentRom is rare.
2254 for (size_t i = 0; i < session_coordinator_->GetTotalSessionCount(); ++i) {
2255 auto* session =
2256 static_cast<RomSession*>(session_coordinator_->GetSession(i));
2257 if (session && &session->rom == rom) {
2258 session_coordinator_->SwitchToSession(i);
2259 // Update test manager with current ROM for ROM-dependent tests
2261 return absl::OkStatus();
2262 }
2263 }
2264 }
2265 // If ROM wasn't found in existing sessions, treat as new session.
2266 // Copying an external ROM object is avoided; instead, fail.
2267 return absl::NotFoundError("ROM not found in existing sessions");
2268}
2269
2272 session_coordinator_->CreateNewSession();
2273 // Toast messages are now shown by SessionCoordinator
2274 }
2275}
2276
2279 session_coordinator_->DuplicateCurrentSession();
2280 }
2281}
2282
2285 session_coordinator_->CloseCurrentSession();
2286 }
2287}
2288
2291 session_coordinator_->RemoveSession(index);
2292 }
2293}
2294
2297 // Delegate to SessionCoordinator - cross-cutting concerns
2298 // are handled by OnSessionSwitched() observer callback
2299 session_coordinator_->SwitchToSession(index);
2300 }
2301}
2302
2304 return session_coordinator_ ? session_coordinator_->GetActiveSessionIndex()
2305 : 0;
2306}
2307
2309 return session_coordinator_ ? session_coordinator_->GetActiveSessionCount()
2310 : 0;
2311}
2312
2314 EditorType type, size_t session_index) const {
2315 const char* base_name = kEditorNames[static_cast<int>(type)];
2316 return session_coordinator_ ? session_coordinator_->GenerateUniqueEditorTitle(
2317 base_name, session_index)
2318 : std::string(base_name);
2319}
2320
2321// ============================================================================
2322// Jump-to Functionality for Cross-Editor Navigation
2323// ============================================================================
2324
2328
2332
2333void EditorManager::SwitchToEditor(EditorType editor_type, bool force_visible,
2334 bool from_dialog) {
2335 // Special case: Agent editor requires EditorManager-specific handling
2336#ifdef YAZE_BUILD_AGENT_UI
2337 if (editor_type == EditorType::kAgent) {
2338 ShowAIAgent();
2339 return;
2340 }
2341#endif
2342
2343 // Delegate all other editor switching to EditorActivator
2344 editor_activator_.SwitchToEditor(editor_type, force_visible, from_dialog);
2345}
2346
2348 if (!session)
2349 return;
2351 ConfigureEditorDependencies(&session->editors, &session->rom,
2352 session->editors.session_id());
2353}
2354
2355// SessionScope implementation
2357 size_t session_id)
2358 : manager_(manager),
2359 prev_rom_(manager->GetCurrentRom()),
2360 prev_editor_set_(manager->GetCurrentEditorSet()),
2361 prev_session_id_(manager->GetCurrentSessionId()) {
2362 // Set new session context
2363 manager_->session_coordinator_->SwitchToSession(session_id);
2364}
2365
2367 // Restore previous context
2368 manager_->session_coordinator_->SwitchToSession(prev_session_id_);
2369}
2370
2371bool EditorManager::HasDuplicateSession(const std::string& filepath) {
2372 return session_coordinator_ &&
2373 session_coordinator_->HasDuplicateSession(filepath);
2374}
2375
2402 // Update project panel context before showing
2405 project_management_panel_->SetVersionManager(version_manager_.get());
2407 }
2409 }
2410}
2411
2413 // Load the current project file into the editor
2414 if (!current_project_.filepath.empty()) {
2416 if (!status.ok()) {
2418 absl::StrFormat("Failed to load project file: %s", status.message()),
2420 return;
2421 }
2422 }
2423 // Set the project pointer for label import functionality
2425 // Activate the editor window
2427}
2428
2430 size_t session_id) {
2431 if (!editor_set) {
2432 return;
2433 }
2434
2435 EditorDependencies deps;
2436 deps.rom = rom;
2437 deps.session_id = session_id;
2440 deps.popup_manager = popup_manager_.get();
2444 deps.project = &current_project_;
2445 deps.version_manager = version_manager_.get();
2446 deps.renderer = renderer_;
2447 deps.emulator = &emulator_;
2448
2449 editor_set->ApplyDependencies(deps);
2450
2451 // If configuring the active session, update the properties panel
2452 if (session_id == GetCurrentSessionId()) {
2454 }
2455}
2456
2457} // namespace yaze::editor
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
Definition rom.h:24
static TimingManager & Get()
Definition timing.h:20
float Update()
Update the timing manager (call once per frame)
Definition timing.h:29
static void Initialize(editor::EditorManager *)
static void Initialize(editor::EditorManager *)
static Flags & get()
Definition features.h:92
void SetProjectContext(project::YazeProject *project)
void SetAsarWrapperContext(core::AsarWrapper *asar_wrapper)
void Initialize(ToastManager *toast_manager, ProposalDrawer *proposal_drawer, RightPanelManager *right_panel_manager, PanelManager *panel_manager)
void SwitchToEditor(EditorType type, bool force_visible=false, bool from_dialog=false)
Switch to an editor, optionally forcing visibility.
void Initialize(const Dependencies &deps)
void JumpToDungeonRoom(int room_id)
Jump to a specific dungeon room.
void JumpToOverworldMap(int map_id)
Jump to a specific overworld map.
SessionScope(EditorManager *manager, size_t session_id)
The EditorManager controls the main editor window and manages the various editor classes.
std::unique_ptr< SessionCoordinator > session_coordinator_
void OnSessionClosed(size_t index) override
Called when a session is closed.
StartupVisibility welcome_mode_override_
std::unique_ptr< GlobalEditorContext > editor_context_
absl::Status SaveRomAs(const std::string &filename)
project::YazeProject current_project_
void ConfigureSession(RomSession *session)
void JumpToDungeonRoom(int room_id)
void SwitchToSession(size_t index)
bool HasDuplicateSession(const std::string &filepath)
std::unique_ptr< LayoutManager > layout_manager_
std::unique_ptr< DashboardPanel > dashboard_panel_
void ProcessStartupActions(const AppConfig &config)
std::string GenerateUniqueEditorTitle(EditorType type, size_t session_index) const
void DrawMenuBar()
Draw the main menu bar.
void ShowProjectManagement()
Injects dependencies into all editors within an EditorSet.
void Initialize(gfx::IRenderer *renderer, const std::string &filename="")
absl::Status CreateNewProject(const std::string &template_name="Basic ROM Hack")
LayoutCoordinator layout_coordinator_
absl::Status LoadAssets(uint64_t loading_handle=0)
auto GetCurrentEditorSet() const -> EditorSet *
void OnSessionRomLoaded(size_t index, RomSession *session) override
Called when a ROM is loaded into a session.
std::unique_ptr< MenuOrchestrator > menu_orchestrator_
ProjectFileEditor project_file_editor_
void ApplyLayoutPreset(const std::string &preset_name)
void ApplyStartupVisibility(const AppConfig &config)
auto GetCurrentEditor() const -> Editor *
std::unique_ptr< RightPanelManager > right_panel_manager_
std::unique_ptr< core::VersionManager > version_manager_
StartupVisibility sidebar_mode_override_
void JumpToOverworldMap(int map_id)
void OnSessionSwitched(size_t new_index, RomSession *session) override
Called when the active session changes.
RomLoadOptionsDialog rom_load_options_dialog_
absl::Status LoadRom()
Load a ROM file into a new or existing session.
std::vector< std::function< void()> > deferred_actions_
void OpenEditorAndPanelsFromFlags(const std::string &editor_name, const std::string &panels_str)
StartupVisibility dashboard_mode_override_
static bool IsPanelBasedEditor(EditorType type)
absl::Status Update()
Main update loop for the editor application.
absl::Status ImportProject(const std::string &project_path)
void OnSessionCreated(size_t index, RomSession *session) override
Called when a new session is created.
void QueueDeferredAction(std::function< void()> action)
SelectionPropertiesPanel selection_properties_panel_
std::unique_ptr< ActivityBar > activity_bar_
ShortcutManager shortcut_manager_
EditorDependencies::SharedClipboard shared_clipboard_
void SwitchToEditor(EditorType editor_type, bool force_visible=false, bool from_dialog=false)
WorkspaceManager workspace_manager_
auto GetCurrentRom() const -> Rom *
void ConfigureEditorDependencies(EditorSet *editor_set, Rom *rom, size_t session_id)
std::unique_ptr< ProjectManagementPanel > project_management_panel_
absl::Status OpenRomOrProject(const std::string &filename)
void RemoveSession(size_t index)
std::unique_ptr< PopupManager > popup_manager_
std::unique_ptr< UICoordinator > ui_coordinator_
EditorActivator editor_activator_
absl::Status SaveRom()
Save the current ROM file.
absl::Status SetCurrentRom(Rom *rom)
static EditorType GetEditorTypeFromCategory(const std::string &category)
static std::vector< std::string > GetAllEditorCategories()
Get all editor categories in display order for sidebar.
static bool IsPanelBasedEditor(EditorType type)
static std::string GetEditorCategory(EditorType type)
Contains a complete set of editors for a single ROM instance.
size_t session_id() const
void ApplyDependencies(const EditorDependencies &dependencies)
void set_user_settings(UserSettings *settings)
SettingsPanel * GetSettingsPanel() const
EditorType type() const
Definition editor.h:214
void ProcessDeferredActions()
Process all queued deferred actions.
void ResetWorkspaceLayout()
Reset the workspace layout to defaults.
void ProcessLayoutRebuild(EditorType current_editor_type, bool is_emulator_visible)
Process pending layout rebuild requests.
void ResetCurrentEditorLayout(EditorType editor_type, size_t session_id)
Reset current editor layout to its default configuration.
void ApplyLayoutPreset(const std::string &preset_name, size_t session_id)
Apply a named layout preset.
void Initialize(const Dependencies &deps)
Initialize with all dependencies.
void SetUndoCallback(std::function< void()> cb)
void SetShowCommandPaletteCallback(std::function< void()> cb)
void SetOnCategorySelectedCallback(std::function< void(const std::string &)> callback)
void SetSidebarStateChangedCallback(std::function< void(bool, bool)> cb)
void SetFileBrowserPath(const std::string &category, const std::string &path)
void SetShowPanelBrowserCallback(std::function< void()> cb)
void SetShowSearchCallback(std::function< void()> cb)
const PanelDescriptor * GetPanelDescriptor(size_t session_id, const std::string &base_card_id) const
void SetCategoryChangedCallback(std::function< void(const std::string &)> cb)
bool * GetVisibilityFlag(size_t session_id, const std::string &base_card_id)
void SetOnPanelClickedCallback(std::function< void(const std::string &)> callback)
void SetShowShortcutsCallback(std::function< void()> cb)
bool ShowPanel(size_t session_id, const std::string &base_card_id)
void RegisterPanel(size_t session_id, const PanelDescriptor &base_info)
std::string GetActiveCategory() const
void EnableFileBrowser(const std::string &category, const std::string &root_path="")
void SetShowEmulatorCallback(std::function< void()> cb)
void SetPanelExpanded(bool expanded)
void SetShowSettingsCallback(std::function< void()> cb)
void DrawAllVisiblePanels()
Draw all visible EditorPanel instances (central drawing)
void SetRedoCallback(std::function< void()> cb)
static constexpr const char * kDashboardCategory
const std::unordered_map< std::string, PanelDescriptor > & GetAllPanelDescriptors() const
Get all panel descriptors (for layout designer, panel browser, etc.)
void SetSidebarVisible(bool visible)
void SetSaveRomCallback(std::function< void()> cb)
void SetShowHelpCallback(std::function< void()> cb)
void SetActiveCategory(const std::string &category)
void SetFileClickedCallback(std::function< void(const std::string &category, const std::string &path)> callback)
void HideAllPanelsInCategory(size_t session_id, const std::string &category)
absl::Status LoadFile(const std::string &filepath)
Load a project file into the editor.
void SetProject(project::YazeProject *project)
Set the project pointer for label import operations.
void set_active(bool active)
Set whether the editor window is active.
void SetToastManager(ToastManager *toast_manager)
Set toast manager for notifications.
absl::Status FinalizeProjectCreation(const std::string &project_name, const std::string &project_path)
Complete project creation after ROM is loaded.
absl::Status SetProjectRom(const std::string &rom_path)
Set the ROM for the current project.
absl::Status CreateNewProject(const std::string &template_name="")
absl::Status ImportProject(const std::string &project_path)
project::YazeProject & GetCurrentProject()
absl::Status LoadRom(Rom *rom, const std::string &filename)
absl::Status SaveRom(Rom *rom)
absl::Status SaveRomAs(Rom *rom, const std::string &filename)
void Open(Rom *rom, const std::string &rom_filename)
Open the dialog after ROM detection.
void SetConfirmCallback(std::function< void(const LoadOptions &)> callback)
Set callback for when options are confirmed.
void Draw(bool *p_open)
Draw the dialog (wrapper around Show)
void SetSessionInfo(size_t session_id, size_t total_sessions)
Set session information.
Definition status_bar.cc:43
void SetRom(Rom *rom)
Set the current ROM for dirty status and filename display.
Definition status_bar.h:58
void SetEnabled(bool enabled)
Enable or disable the status bar.
Definition status_bar.h:52
void Initialize(GlobalEditorContext *context)
Definition status_bar.cc:13
void Draw()
Draw the status bar.
void Show(const std::string &message, ToastType type=ToastType::kInfo, float ttl_seconds=3.0f)
void SetNewProjectCallback(std::function< void()> callback)
Set callback for creating new project.
void SetOpenAgentCallback(std::function< void()> callback)
Set callback for opening AI Agent.
void SetOpenRomCallback(std::function< void()> callback)
Set callback for opening ROM.
void SetNewProjectWithTemplateCallback(std::function< void(const std::string &)> callback)
Set callback for creating project with template.
void SetOpenProjectCallback(std::function< void(const std::string &)> callback)
Set callback for opening project.
void set_panel_manager(PanelManager *manager)
bool is_audio_focus_mode() const
Definition emulator.h:108
void set_renderer(gfx::IRenderer *renderer)
Definition emulator.h:97
bool is_snes_initialized() const
Definition emulator.h:122
void Run(Rom *rom)
Definition emulator.cc:420
auto running() const -> bool
Definition emulator.h:59
void set_panel_manager(editor::PanelManager *manager)
Definition emulator.h:49
auto mutable_gfx_sheets()
Get mutable reference to all graphics sheets.
Definition arena.h:128
static Arena & Get()
Definition arena.cc:20
Defines an abstract interface for all rendering operations.
Definition irenderer.h:40
static PerformanceDashboard & Get()
void SetVisible(bool visible)
Show/hide the dashboard.
void Update()
Update dashboard with current performance data.
void Render()
Render the performance dashboard UI.
static PerformanceProfiler & Get()
void PrintSummary() const
Print a summary of all operations to console.
static RecentFilesManager & GetInstance()
Definition project.h:312
void SetCurrentRom(Rom *rom)
static TestManager & Get()
static void ShowOpenFileDialogAsync(const FileDialogOptions &options, std::function< void(const std::string &)> callback)
static std::string ShowSaveFileDialog(const std::string &default_name="", const std::string &default_extension="")
ShowSaveFileDialog opens a save file dialog and returns the selected filepath. Uses global feature fl...
static std::string ShowOpenFileDialog()
ShowOpenFileDialog opens a file dialog and returns the selected filepath. Uses global feature flag to...
static std::string ShowOpenFolderDialog()
ShowOpenFolderDialog opens a file dialog and returns the selected folder path. Uses global feature fl...
#define ICON_MD_MEMORY
Definition icons.h:1195
#define ICON_MD_STOP
Definition icons.h:1862
#define ICON_MD_VIDEOGAME_ASSET
Definition icons.h:2076
#define ICON_MD_BUG_REPORT
Definition icons.h:327
#define ICON_MD_SPEED
Definition icons.h:1817
#define ICON_MD_AUDIOTRACK
Definition icons.h:213
#define ICON_MD_KEYBOARD
Definition icons.h:1028
#define ICON_MD_DASHBOARD
Definition icons.h:517
#define ICON_MD_SAVE
Definition icons.h:1644
#define ICON_MD_MENU
Definition icons.h:1196
#define ICON_MD_SPORTS_ESPORTS
Definition icons.h:1826
#define ICON_MD_AUDIO_FILE
Definition icons.h:212
#define ICON_MD_SMART_TOY
Definition icons.h:1781
#define ICON_MD_MENU_OPEN
Definition icons.h:1198
#define LOG_DEBUG(category, format,...)
Definition log.h:103
#define LOG_WARN(category, format,...)
Definition log.h:107
#define LOG_INFO(category, format,...)
Definition log.h:105
#define PRINT_IF_ERROR(expression)
Definition macro.h:28
constexpr const char * kAbout
constexpr const char * kDisplaySettings
std::string StripSessionPrefix(absl::string_view panel_id)
std::optional< EditorType > ParseEditorTypeFromString(absl::string_view name)
Editors are the view controllers for the application.
constexpr std::array< const char *, 14 > kEditorNames
Definition editor.h:167
void ConfigureMenuShortcuts(const ShortcutDependencies &deps, ShortcutManager *shortcut_manager)
void ConfigureEditorShortcuts(const ShortcutDependencies &deps, ShortcutManager *shortcut_manager)
void ExecuteShortcuts(const ShortcutManager &shortcut_manager)
ImVec4 GetSurfaceContainerHighestVec4()
ImVec4 GetPrimaryVec4()
ImVec4 GetTextSecondaryVec4()
ImVec4 GetSurfaceContainerHighVec4()
void RegisterZ3edTestSuites()
FileDialogOptions MakeRomFileDialogOptions(bool include_all_files)
Definition file_util.cc:87
absl::Status LoadGameData(Rom &rom, GameData &data, const LoadOptions &options)
Loads all Zelda3-specific game data from a generic ROM.
Definition game_data.cc:122
absl::Status SaveAllGraphicsData(Rom &rom, const std::array< gfx::Bitmap, kNumGfxSheets > &sheets)
Saves all graphics sheets back to ROM.
Definition game_data.cc:585
void SetPreferHmagicSpriteNames(bool prefer)
Definition sprite.cc:273
absl::Status SaveDungeonMaps(Rom &rom, std::vector< DungeonMap > &dungeon_maps)
Save the dungeon maps to the ROM.
ResourceLabelProvider & GetResourceLabels()
Get the global ResourceLabelProvider instance.
#define RETURN_IF_ERROR(expr)
Definition snes.cc:22
Configuration options for the application startup.
Definition application.h:24
std::string startup_editor
Definition application.h:35
StartupVisibility welcome_mode
Definition application.h:30
std::vector< std::string > open_panels
Definition application.h:36
StartupVisibility sidebar_mode
Definition application.h:32
StartupVisibility dashboard_mode
Definition application.h:31
std::function< EditorSet *()> get_current_editor_set
std::function< void(std::function< void()>)> queue_deferred_action
Unified dependency container for all editor types.
Definition editor.h:111
project::YazeProject * project
Definition editor.h:134
SharedClipboard * shared_clipboard
Definition editor.h:132
gfx::IRenderer * renderer
Definition editor.h:138
ShortcutManager * shortcut_manager
Definition editor.h:131
core::VersionManager * version_manager
Definition editor.h:135
All dependencies required by LayoutCoordinator.
Represents a single session, containing a ROM and its associated editors.
core::FeatureFlags::Flags feature_flags
std::vector< std::string > recent_files
Definition project.h:76
Modern project structure with comprehensive settings consolidation.
Definition project.h:84
absl::Status RepairProject()
Definition project.cc:794
bool project_opened() const
Definition project.h:227
void InitializeResourceLabelProvider()
Initialize the global ResourceLabelProvider with this project's labels.
Definition project.cc:1467
std::string rom_filename
Definition project.h:92
std::unordered_map< std::string, std::unordered_map< std::string, std::string > > resource_labels
Definition project.h:109
std::string assets_folder
Definition project.h:98
std::string labels_filename
Definition project.h:100
std::string GetDisplayName() const
Definition project.cc:828
WorkspaceSettings workspace_settings
Definition project.h:107
absl::Status Open(const std::string &project_path)
Definition project.cc:150
std::string code_folder
Definition project.h:97
absl::Status Validate() const
Definition project.cc:738
core::FeatureFlags::Flags feature_flags
Definition project.h:106