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// Third-party library headers
21#define IMGUI_DEFINE_MATH_OPERATORS
22#include "absl/status/status.h"
23#include "absl/status/statusor.h"
24#include "absl/strings/ascii.h"
25#include "absl/strings/match.h"
26#include "absl/strings/str_format.h"
27#include "absl/strings/str_split.h"
28#include "absl/strings/strip.h"
29#include "imgui/imgui.h"
30#include "imgui/imgui_internal.h"
31
32// Project headers
33#include "app/application.h"
36#include "app/editor/editor.h"
55#include "app/emu/emulator.h"
59#include "app/gui/core/icons.h"
61#include "app/platform/timing.h"
64#include "core/features.h"
65#include "core/project.h"
70#include "rom/rom.h"
71#include "util/file_util.h"
72#include "util/log.h"
73#include "util/macro.h"
74#include "yaze_config.h"
75#include "zelda3/game_data.h"
79
80// Conditional platform headers
81#ifdef __EMSCRIPTEN__
85#endif
86
87// Conditional test headers
88#ifdef YAZE_ENABLE_TESTING
93#endif
94#ifdef YAZE_ENABLE_GTEST
96#endif
97#ifdef YAZE_WITH_GRPC
99#endif
100
101namespace yaze {
102namespace editor {
103
104namespace {
105// TODO: Move to EditorRegistry
106std::string GetEditorName(EditorType type) {
107 return kEditorNames[static_cast<int>(type)];
108}
109
110std::optional<EditorType> ParseEditorTypeFromString(
111 absl::string_view name) {
112 const std::string lower = absl::AsciiStrToLower(std::string(name));
113 for (int i = 0; i < static_cast<int>(EditorType::kSettings) + 1; ++i) {
114 const std::string candidate = absl::AsciiStrToLower(
115 std::string(GetEditorName(static_cast<EditorType>(i))));
116 if (candidate == lower) {
117 return static_cast<EditorType>(i);
118 }
119 }
120 return std::nullopt;
121}
122
123std::string StripSessionPrefix(absl::string_view panel_id) {
124 if (panel_id.size() > 2 && panel_id[0] == 's' &&
125 absl::ascii_isdigit(panel_id[1])) {
126 const size_t dot = panel_id.find('.');
127 if (dot != absl::string_view::npos) {
128 return std::string(panel_id.substr(dot + 1));
129 }
130 }
131 return std::string(panel_id);
132}
133
134} // namespace
135
136// Static registry of editors that use the card-based layout system
137// These editors register their cards with EditorPanelManager and manage their
138// own windows They do NOT need the traditional ImGui::Begin/End wrapper - they
139// create cards internally
143
145 if (!current_editor_) {
146 return;
147 }
148
149 // Using PanelManager directly
150 std::string category =
153}
154
158
159void EditorManager::ApplyLayoutPreset(const std::string& preset_name) {
161}
162
171
172#ifdef YAZE_BUILD_AGENT_UI
173void EditorManager::ShowAIAgent() {
174 // Apply saved agent settings from the current project when opening the Agent
175 // UI to respect the user's preferred provider/model.
176 // TODO: Implement LoadAgentSettingsFromProject in AgentChat or AgentEditor
178}
179
180void EditorManager::ShowChatHistory() {
182}
183#endif
184
186 : project_manager_(&toast_manager_), rom_file_manager_(&toast_manager_) {
187 std::stringstream ss;
188 ss << YAZE_VERSION_MAJOR << "." << YAZE_VERSION_MINOR << "."
189 << YAZE_VERSION_PATCH;
190 ss >> version_;
191
192 // Initialize Core Context
193 editor_context_ = std::make_unique<GlobalEditorContext>(event_bus_);
195
196 // ============================================================================
197 // DELEGATION INFRASTRUCTURE INITIALIZATION
198 // ============================================================================
199 // EditorManager delegates responsibilities to specialized components:
200 // - SessionCoordinator: Multi-session UI and lifecycle management
201 // - MenuOrchestrator: Menu building and action routing
202 // - UICoordinator: UI drawing and state management
203 // - RomFileManager: ROM file I/O operations
204 // - ProjectManager: Project file operations
205 // - PanelManager: Panel-based editor UI management
206 // - ShortcutConfigurator: Keyboard shortcut registration
207 // - WindowDelegate: Window layout operations
208 // - PopupManager: Modal popup/dialog management
209 //
210 // EditorManager retains:
211 // - Session storage (sessions_) and current pointers (current_rom_, etc.)
212 // - Main update loop (iterates sessions, calls editor updates)
213 // - Asset loading (Initialize/Load on all editors)
214 // - Session ID tracking (current_session_id_)
215 //
216 // INITIALIZATION ORDER (CRITICAL):
217 // 1. PopupManager - MUST be first, MenuOrchestrator/UICoordinator take ref to
218 // it
219 // 2. SessionCoordinator - Independent, can be early
220 // 3. MenuOrchestrator - Depends on PopupManager, SessionCoordinator
221 // 4. UICoordinator - Depends on PopupManager, SessionCoordinator
222 // 5. ShortcutConfigurator - Created in Initialize(), depends on all above
223 //
224 // If this order is violated, you will get SIGSEGV crashes when menu callbacks
225 // try to call popup_manager_.Show() with an uninitialized PopupManager!
226 // ============================================================================
227
228 // STEP 1: Initialize PopupManager FIRST
229 popup_manager_ = std::make_unique<PopupManager>(this);
230 popup_manager_->Initialize(); // Registers all popups with PopupID constants
231
232 // STEP 2: Initialize SessionCoordinator (independent of popups)
233 session_coordinator_ = std::make_unique<SessionCoordinator>(
235
236 // STEP 3: Initialize MenuOrchestrator (depends on popup_manager_,
237 // session_coordinator_)
238 menu_orchestrator_ = std::make_unique<MenuOrchestrator>(
241
242 // Wire up card registry for Panels submenu in View menu
243 menu_orchestrator_->SetPanelManager(&panel_manager_);
244 menu_orchestrator_->SetStatusBar(&status_bar_);
245 menu_orchestrator_->SetUserSettings(&user_settings_);
246
247 session_coordinator_->SetEditorManager(this);
248 session_coordinator_->AddObserver(this); // Register for session lifecycle events
249
250 // STEP 4: Initialize UICoordinator (depends on popup_manager_,
251 // session_coordinator_, panel_manager_)
252 ui_coordinator_ = std::make_unique<UICoordinator>(
256
257 // STEP 4.5: Initialize LayoutManager (DockBuilder layouts for editors)
258 layout_manager_ = std::make_unique<LayoutManager>();
259
260 // STEP 4.6: Initialize RightPanelManager (right-side sliding panels)
261 right_panel_manager_ = std::make_unique<RightPanelManager>();
262 right_panel_manager_->SetToastManager(&toast_manager_);
263 right_panel_manager_->SetProposalDrawer(&proposal_drawer_);
265
266 // Initialize ProjectManagementPanel for project/version management
267 project_management_panel_ = std::make_unique<ProjectManagementPanel>();
268 project_management_panel_->SetToastManager(&toast_manager_);
269 project_management_panel_->SetSwapRomCallback([this]() {
270 // Prompt user to select a new ROM for the project
272 if (!rom_path.empty()) {
274 auto status = current_project_.Save();
275 if (status.ok()) {
276 toast_manager_.Show("Project ROM updated. Reload to apply changes.",
278 } else {
279 toast_manager_.Show("Failed to update project ROM", ToastType::kError);
280 }
281 }
282 });
283 project_management_panel_->SetReloadRomCallback([this]() {
286 auto status = LoadProjectWithRom();
287 if (!status.ok()) {
289 absl::StrFormat("Failed to reload ROM: %s", status.message()),
291 }
292 }
293 });
294 project_management_panel_->SetSaveProjectCallback([this]() {
295 auto status = SaveProject();
296 if (status.ok()) {
297 toast_manager_.Show("Project saved", ToastType::kSuccess);
298 } else {
300 absl::StrFormat("Failed to save project: %s", status.message()),
302 }
303 });
304 project_management_panel_->SetBrowseFolderCallback(
305 [this](const std::string& type) {
307 if (!folder_path.empty()) {
308 if (type == "code") {
309 current_project_.code_folder = folder_path;
310 // Update assembly editor path
311 if (auto* editor_set = GetCurrentEditorSet()) {
312 editor_set->GetAssemblyEditor()->OpenFolder(folder_path);
313 panel_manager_.SetFileBrowserPath("Assembly", folder_path);
314 }
315 } else if (type == "assets") {
316 current_project_.assets_folder = folder_path;
317 }
319 absl::StrFormat("%s folder set: %s", type.c_str(),
320 folder_path.c_str()),
322 }
323 });
324 right_panel_manager_->SetProjectManagementPanel(project_management_panel_.get());
325
326 // STEP 4.6.1: Initialize LayoutCoordinator (facade for layout operations)
328 layout_deps.layout_manager = layout_manager_.get();
329 layout_deps.panel_manager = &panel_manager_;
330 layout_deps.ui_coordinator = ui_coordinator_.get();
331 layout_deps.toast_manager = &toast_manager_;
332 layout_deps.status_bar = &status_bar_;
333 layout_deps.right_panel_manager = right_panel_manager_.get();
334 layout_coordinator_.Initialize(layout_deps);
335
336 // STEP 4.6.2: Initialize EditorActivator (editor switching and jump navigation)
337 EditorActivator::Dependencies activator_deps;
338 activator_deps.panel_manager = &panel_manager_;
339 activator_deps.layout_manager = layout_manager_.get();
340 activator_deps.ui_coordinator = ui_coordinator_.get();
341 activator_deps.right_panel_manager = right_panel_manager_.get();
342 activator_deps.toast_manager = &toast_manager_;
343 activator_deps.get_current_editor_set = [this]() { return GetCurrentEditorSet(); };
344 activator_deps.get_current_session_id = [this]() { return GetCurrentSessionId(); };
345 activator_deps.queue_deferred_action = [this](std::function<void()> action) {
346 QueueDeferredAction(std::move(action));
347 };
348 editor_activator_.Initialize(activator_deps);
349
350 // STEP 4.7: Initialize ActivityBar
351 activity_bar_ = std::make_unique<ActivityBar>(panel_manager_);
352
353 // Wire up PanelManager callbacks for ActivityBar buttons
357 }
358 });
361 right_panel_manager_->TogglePanel(
363 }
364 });
365
366 // STEP 4.8: Initialize DashboardPanel
367 dashboard_panel_ = std::make_unique<DashboardPanel>(this);
369 {.card_id = "dashboard.main",
370 .display_name = "Dashboard",
371 .window_title = " Dashboard",
372 .icon = ICON_MD_DASHBOARD,
373 .category = "Dashboard",
374 .shortcut_hint = "F1",
375 .visibility_flag = dashboard_panel_->visibility_flag(),
376 .priority = 0});
377
378 // STEP 5: ShortcutConfigurator created later in Initialize() method
379 // It depends on all above coordinators being available
380}
381
383 // Unregister as observer before destruction
385 session_coordinator_->RemoveObserver(this);
386 }
387}
388
389// ============================================================================
390// SessionObserver Implementation
391// ============================================================================
392
393void EditorManager::OnSessionSwitched(size_t new_index, RomSession* session) {
394 // Update RightPanelManager with the new session's settings editor
395 if (right_panel_manager_ && session) {
396 right_panel_manager_->SetSettingsPanel(session->editors.GetSettingsPanel());
397 // Set up StatusBar reference for live toggling
398 if (auto* settings = session->editors.GetSettingsPanel()) {
399 settings->SetStatusBar(&status_bar_);
400 }
401 }
402
403 // Update properties panel with new ROM
404 if (session) {
406 }
407
408#ifdef YAZE_ENABLE_TESTING
409 test::TestManager::Get().SetCurrentRom(session ? &session->rom : nullptr);
410#endif
411
412 LOG_DEBUG("EditorManager", "Session switched to %zu via observer", new_index);
413}
414
415void EditorManager::OnSessionCreated(size_t index, RomSession* session) {
416 LOG_INFO("EditorManager", "Session %zu created via observer", index);
417}
418
420#ifdef YAZE_ENABLE_TESTING
421 // Update test manager - it will get the new current ROM on next switch
423#endif
424
425 LOG_INFO("EditorManager", "Session %zu closed via observer", index);
426}
427
428void EditorManager::OnSessionRomLoaded(size_t index, RomSession* session) {
429#ifdef YAZE_ENABLE_TESTING
430 if (session) {
432 }
433#endif
434
435 LOG_INFO("EditorManager", "ROM loaded in session %zu via observer", index);
436}
437
439 auto& test_manager = test::TestManager::Get();
440
441#ifdef YAZE_ENABLE_TESTING
442 // Register comprehensive test suites
443 test_manager.RegisterTestSuite(std::make_unique<test::IntegratedTestSuite>());
444 test_manager.RegisterTestSuite(
445 std::make_unique<test::PerformanceTestSuite>());
446 test_manager.RegisterTestSuite(std::make_unique<test::UITestSuite>());
447 test_manager.RegisterTestSuite(
448 std::make_unique<test::RomDependentTestSuite>());
449
450 // Register new E2E and ZSCustomOverworld test suites
451 test_manager.RegisterTestSuite(std::make_unique<test::E2ETestSuite>());
452 test_manager.RegisterTestSuite(
453 std::make_unique<test::ZSCustomOverworldTestSuite>());
454#endif
455
456 // Register Google Test suite if available
457#ifdef YAZE_ENABLE_GTEST
458 test_manager.RegisterTestSuite(std::make_unique<test::UnitTestSuite>());
459#endif
460
461 // Register z3ed AI Agent test suites (requires gRPC)
462#ifdef YAZE_WITH_GRPC
464#endif
465
466 // Update resource monitoring to track Arena state
467 test_manager.UpdateResourceStats();
468}
469
471 const std::string& filename) {
472 renderer_ = renderer;
473
474 // Inject card_registry into emulator and workspace_manager
477
478 // Initialize layout designer with panel + layout managers
480
481 // Point to a blank editor set when no ROM is loaded
482 // current_editor_set_ = &blank_editor_set_;
483
484 if (!filename.empty()) {
486 }
487
488 // Note: PopupManager is now initialized in constructor before
489 // MenuOrchestrator This ensures all menu callbacks can safely call
490 // popup_manager_.Show()
491
492 // Register emulator cards early (emulator Initialize might not be called)
493 // Using PanelManager directly
494 panel_manager_.RegisterPanel({.card_id = "emulator.cpu_debugger",
495 .display_name = "CPU Debugger",
496 .window_title = " CPU Debugger",
497 .icon = ICON_MD_BUG_REPORT,
498 .category = "Emulator",
499 .priority = 10});
500 panel_manager_.RegisterPanel({.card_id = "emulator.ppu_viewer",
501 .display_name = "PPU Viewer",
502 .window_title = " PPU Viewer",
504 .category = "Emulator",
505 .priority = 20});
506 panel_manager_.RegisterPanel({.card_id = "emulator.memory_viewer",
507 .display_name = "Memory Viewer",
508 .window_title = " Memory Viewer",
509 .icon = ICON_MD_MEMORY,
510 .category = "Emulator",
511 .priority = 30});
512 panel_manager_.RegisterPanel({.card_id = "emulator.breakpoints",
513 .display_name = "Breakpoints",
514 .window_title = " Breakpoints",
515 .icon = ICON_MD_STOP,
516 .category = "Emulator",
517 .priority = 40});
518 panel_manager_.RegisterPanel({.card_id = "emulator.performance",
519 .display_name = "Performance",
520 .window_title = " Performance",
521 .icon = ICON_MD_SPEED,
522 .category = "Emulator",
523 .priority = 50});
524 panel_manager_.RegisterPanel({.card_id = "emulator.ai_agent",
525 .display_name = "AI Agent",
526 .window_title = " AI Agent",
527 .icon = ICON_MD_SMART_TOY,
528 .category = "Emulator",
529 .priority = 60});
530 panel_manager_.RegisterPanel({.card_id = "emulator.save_states",
531 .display_name = "Save States",
532 .window_title = " Save States",
533 .icon = ICON_MD_SAVE,
534 .category = "Emulator",
535 .priority = 70});
536 panel_manager_.RegisterPanel({.card_id = "emulator.keyboard_config",
537 .display_name = "Keyboard Config",
538 .window_title = " Keyboard Config",
539 .icon = ICON_MD_KEYBOARD,
540 .category = "Emulator",
541 .priority = 80});
542 panel_manager_.RegisterPanel({.card_id = "emulator.virtual_controller",
543 .display_name = "Virtual Controller",
544 .window_title = " Virtual Controller",
546 .category = "Emulator",
547 .priority = 85});
548 panel_manager_.RegisterPanel({.card_id = "emulator.apu_debugger",
549 .display_name = "APU Debugger",
550 .window_title = " APU Debugger",
551 .icon = ICON_MD_AUDIOTRACK,
552 .category = "Emulator",
553 .priority = 90});
554 panel_manager_.RegisterPanel({.card_id = "emulator.audio_mixer",
555 .display_name = "Audio Mixer",
556 .window_title = " Audio Mixer",
557 .icon = ICON_MD_AUDIO_FILE,
558 .category = "Emulator",
559 .priority = 100});
560
561 // Register memory/hex editor card
562 panel_manager_.RegisterPanel({.card_id = "memory.hex_editor",
563 .display_name = "Hex Editor",
564 .window_title = ICON_MD_MEMORY " Hex Editor",
565 .icon = ICON_MD_MEMORY,
566 .category = "Memory",
567 .priority = 10});
568
569 // Initialize project file editor
571
572 // Initialize agent UI (no-op when agent UI is disabled)
575
576 // Load critical user settings first
578 if (!status_.ok()) {
579 LOG_WARN("EditorManager", "Failed to load user settings: %s",
580 status_.ToString().c_str());
581 }
582 // Apply sprite naming preference globally.
585
586 // Initialize WASM control and session APIs for browser/agent integration
587#ifdef __EMSCRIPTEN__
590 LOG_INFO("EditorManager", "WASM Control and Session APIs initialized");
591#endif
592
593 // Initialize ROM load options dialog callbacks
595 [this](const RomLoadOptionsDialog::LoadOptions& options) {
596 // Apply feature flags from dialog
597 auto& flags = core::FeatureFlags::get();
598 flags.overworld.kSaveOverworldMaps = options.save_overworld_maps;
599 flags.overworld.kSaveOverworldEntrances =
601 flags.overworld.kSaveOverworldExits = options.save_overworld_exits;
602 flags.overworld.kSaveOverworldItems = options.save_overworld_items;
603 flags.overworld.kLoadCustomOverworld = options.enable_custom_overworld;
604 flags.kSaveDungeonMaps = options.save_dungeon_maps;
605 flags.kSaveAllPalettes = options.save_all_palettes;
606 flags.kSaveGfxGroups = options.save_gfx_groups;
607
608 // Create project if requested
609 if (options.create_project && !options.project_name.empty()) {
612 options.project_name, options.project_path);
613 if (!status.ok()) {
614 toast_manager_.Show("Failed to create project", ToastType::kError);
615 } else {
616 toast_manager_.Show("Project created: " + options.project_name,
618 }
619 }
620
621 // Close dialog and show editor selection
623 if (ui_coordinator_) {
624 // dashboard_panel_->ClearRecentEditors();
625 ui_coordinator_->SetEditorSelectionVisible(true);
626 }
627
628 LOG_INFO("EditorManager", "ROM load options applied: preset=%s",
629 options.selected_preset.c_str());
630 });
631
632 // Initialize welcome screen callbacks
634 status_ = LoadRom();
635 // LoadRom() already handles closing welcome screen and showing editor
636 // selection
637 });
638
641 if (status_.ok() && ui_coordinator_) {
642 ui_coordinator_->SetWelcomeScreenVisible(false);
643 ui_coordinator_->SetWelcomeScreenManuallyClosed(true);
644 }
645 });
646
648 [this](const std::string& template_name) {
649 // Set the template for the ROM load options dialog
650 status_ = CreateNewProject(template_name);
651 if (status_.ok() && ui_coordinator_) {
652 ui_coordinator_->SetWelcomeScreenVisible(false);
653 ui_coordinator_->SetWelcomeScreenManuallyClosed(true);
654 }
655 });
656
657 welcome_screen_.SetOpenProjectCallback([this](const std::string& filepath) {
658 status_ = OpenRomOrProject(filepath);
659 if (status_.ok() && ui_coordinator_) {
660 ui_coordinator_->SetWelcomeScreenVisible(false);
661 ui_coordinator_->SetWelcomeScreenManuallyClosed(true);
662 }
663 });
664
666#ifdef YAZE_BUILD_AGENT_UI
667 ShowAIAgent();
668#endif
669 // Keep welcome screen visible - user may want to do other things
670 });
671
672 // Initialize editor selection dialog callback
673 // editor_selection_dialog_.SetSelectionCallback([this](EditorType type) {
674 // editor_selection_dialog_.MarkRecentlyUsed(type);
675 // // Pass true for from_dialog so the dialog isn't automatically dismissed
676 // SwitchToEditor(type, /*force_visible=*/false, /*from_dialog=*/true);
677 // });
678
679 // Load user settings - this must happen after context is initialized
680 // Apply font scale after loading
681 ImGui::GetIO().FontGlobalScale = user_settings_.prefs().font_global_scale;
682
683 // Apply welcome screen preference
685 ui_coordinator_->SetWelcomeScreenVisible(false);
686 ui_coordinator_->SetWelcomeScreenManuallyClosed(true);
687 }
688
689 // Defer workspace presets loading to avoid initialization crashes
690 // This will be called lazily when workspace features are accessed
691
692 // Set up sidebar utility icon callbacks
694 if (ui_coordinator_) {
695 ui_coordinator_->SetEmulatorVisible(true);
696 }
697 });
701 if (ui_coordinator_) {
702 ui_coordinator_->ShowPanelBrowser();
703 }
704 });
705
706 // Set up sidebar action button callbacks
708 if (GetCurrentRom() && GetCurrentRom()->is_loaded()) {
709 auto status = SaveRom();
710 if (status.ok()) {
711 toast_manager_.Show("ROM saved successfully", ToastType::kSuccess);
712 } else {
714 absl::StrFormat("Failed to save ROM: %s", status.message()),
716 }
717 }
718 });
719
721 if (auto* current_editor = GetCurrentEditor()) {
722 auto status = current_editor->Undo();
723 if (!status.ok()) {
725 absl::StrFormat("Undo failed: %s", status.message()),
727 }
728 }
729 });
730
732 if (auto* current_editor = GetCurrentEditor()) {
733 auto status = current_editor->Redo();
734 if (!status.ok()) {
736 absl::StrFormat("Redo failed: %s", status.message()),
738 }
739 }
740 });
741
743 if (ui_coordinator_) {
744 ui_coordinator_->ShowGlobalSearch();
745 }
746 });
747
749 if (ui_coordinator_) {
750 // Shortcut configuration is part of Settings
752 }
753 });
754
756 if (ui_coordinator_) {
757 ui_coordinator_->ShowCommandPalette();
758 }
759 });
760
762 if (popup_manager_) {
764 }
765 });
766
767 // Set up sidebar state change callbacks for persistence
768 // IMPORTANT: Register callbacks BEFORE applying state to avoid triggering Save() during init
770 [this](bool visible, bool expanded) {
774 });
775
777 [this](const std::string& category) {
780 });
781
782 panel_manager_.SetOnPanelClickedCallback([this](const std::string& category) {
784 // Switch to the editor associated with this card's category
785 // This ensures clicking a card opens/focuses the parent editor
786 if (type != EditorType::kSettings && type != EditorType::kUnknown) {
787 SwitchToEditor(type, true);
788 }
789 });
790
791 // Handle Activity Bar category selection - dismisses dashboard
792 panel_manager_.SetOnCategorySelectedCallback([this](const std::string& category) {
793 // Transition startup surface to Editor state (dismisses dashboard)
794 if (ui_coordinator_) {
795 ui_coordinator_->SetStartupSurface(StartupSurface::kEditor);
796 }
797 });
798
799 // Enable file browser for Assembly category
801
802 // Set up file clicked callback to open files in Assembly editor
804 [this](const std::string& category, const std::string& path) {
805 if (category == "Assembly") {
806 // Open the file in the Assembly editor
807 if (auto* editor_set = GetCurrentEditorSet()) {
808 editor_set->GetAssemblyEditor()->ChangeActiveFile(path);
809 // Make sure Assembly editor is active
811 }
812 }
813 });
814
815 // Apply sidebar state from settings AFTER registering callbacks
816 // This triggers the callbacks but they should be safe now
823 }
824
825 // Initialize testing system only when tests are enabled
826#ifdef YAZE_ENABLE_TESTING
828#endif
829
830 // TestManager will be updated when ROMs are loaded via SetCurrentRom calls
831
832 ShortcutDependencies shortcut_deps;
833 shortcut_deps.editor_manager = this;
834 shortcut_deps.editor_registry = &editor_registry_;
835 shortcut_deps.menu_orchestrator = menu_orchestrator_.get();
836 shortcut_deps.rom_file_manager = &rom_file_manager_;
837 shortcut_deps.project_manager = &project_manager_;
838 shortcut_deps.session_coordinator = session_coordinator_.get();
839 shortcut_deps.ui_coordinator = ui_coordinator_.get();
840 shortcut_deps.workspace_manager = &workspace_manager_;
841 shortcut_deps.popup_manager = popup_manager_.get();
842 shortcut_deps.toast_manager = &toast_manager_;
843 shortcut_deps.panel_manager = &panel_manager_;
844
847}
848
850 const std::string& editor_name, const std::string& panels_str) {
851 const bool has_editor = !editor_name.empty();
852 const bool has_panels = !panels_str.empty();
853
854 if (!has_editor && !has_panels) {
855 return;
856 }
857
858 LOG_INFO("EditorManager",
859 "Processing startup flags: editor='%s', panels='%s'",
860 editor_name.c_str(), panels_str.c_str());
861
862 std::optional<EditorType> editor_type_to_open =
863 has_editor ? ParseEditorTypeFromString(editor_name) : std::nullopt;
864 if (has_editor && !editor_type_to_open.has_value()) {
865 LOG_WARN("EditorManager", "Unknown editor specified via flag: %s",
866 editor_name.c_str());
867 } else if (editor_type_to_open.has_value()) {
868 // Use EditorActivator to ensure layouts and default panels are initialized
869 SwitchToEditor(*editor_type_to_open, true, /*from_dialog=*/true);
870 }
871
872 // Open panels via PanelManager - works for any editor type
873 if (!has_panels) {
874 return;
875 }
876
877 const size_t session_id = GetCurrentSessionId();
878 std::string last_known_category = panel_manager_.GetActiveCategory();
879 bool applied_category_from_panel = false;
880
881 for (absl::string_view token :
882 absl::StrSplit(panels_str, ',', absl::SkipWhitespace())) {
883 if (token.empty()) {
884 continue;
885 }
886 std::string panel_name = std::string(absl::StripAsciiWhitespace(token));
887 LOG_DEBUG("EditorManager", "Attempting to open panel: '%s'",
888 panel_name.c_str());
889
890 const std::string lower_name = absl::AsciiStrToLower(panel_name);
891 if (lower_name == "welcome" || lower_name == "welcome_screen") {
892 if (ui_coordinator_) {
893 ui_coordinator_->SetWelcomeScreenBehavior(StartupVisibility::kShow);
894 }
895 continue;
896 }
897 if (lower_name == "dashboard" || lower_name == "dashboard.main" ||
898 lower_name == "editor_selection") {
899 if (dashboard_panel_) {
900 dashboard_panel_->Show();
901 }
902 if (ui_coordinator_) {
903 ui_coordinator_->SetDashboardBehavior(StartupVisibility::kShow);
904 }
906 continue;
907 }
908
909 // Special case: "Room <id>" opens a dungeon room
910 if (absl::StartsWith(panel_name, "Room ")) {
911 if (auto* editor_set = GetCurrentEditorSet()) {
912 try {
913 int room_id = std::stoi(panel_name.substr(5));
914 editor_set->GetDungeonEditor()->add_room(room_id);
915 } catch (const std::exception& e) {
916 LOG_WARN("EditorManager", "Invalid room ID format: %s",
917 panel_name.c_str());
918 }
919 }
920 continue;
921 }
922
923 std::optional<std::string> resolved_panel;
924 if (panel_manager_.GetPanelDescriptor(session_id, panel_name)) {
925 resolved_panel = panel_name;
926 } else {
927 for (const auto& [prefixed_id, descriptor] :
929 const std::string base_id = StripSessionPrefix(prefixed_id);
930 const std::string card_lower = absl::AsciiStrToLower(base_id);
931 const std::string display_lower =
932 absl::AsciiStrToLower(descriptor.display_name);
933
934 if (card_lower == lower_name || display_lower == lower_name) {
935 resolved_panel = base_id;
936 break;
937 }
938 }
939 }
940
941 if (!resolved_panel.has_value()) {
942 LOG_WARN("EditorManager",
943 "Unknown panel '%s' from --open_panels (known count: %zu)",
944 panel_name.c_str(),
946 continue;
947 }
948
949 if (panel_manager_.ShowPanel(session_id, *resolved_panel)) {
950 const auto* descriptor =
951 panel_manager_.GetPanelDescriptor(session_id, *resolved_panel);
952 if (descriptor && !applied_category_from_panel &&
953 descriptor->category != PanelManager::kDashboardCategory) {
954 panel_manager_.SetActiveCategory(descriptor->category);
955 applied_category_from_panel = true;
956 } else if (!applied_category_from_panel &&
957 descriptor && descriptor->category.empty() &&
958 !last_known_category.empty()) {
959 panel_manager_.SetActiveCategory(last_known_category);
960 }
961 } else {
962 LOG_WARN("EditorManager", "Failed to show panel '%s'",
963 resolved_panel->c_str());
964 }
965 }
966}
967
974
976 if (ui_coordinator_) {
977 ui_coordinator_->SetWelcomeScreenBehavior(welcome_mode_override_);
978 ui_coordinator_->SetDashboardBehavior(dashboard_mode_override_);
979 }
980
982 const bool sidebar_visible =
984 panel_manager_.SetSidebarVisible(sidebar_visible);
985 if (ui_coordinator_) {
986 ui_coordinator_->SetPanelSidebarVisible(sidebar_visible);
987 }
988 }
989
990 // Force sidebar panel to collapse if Welcome Screen or Dashboard is explicitly shown
991 // This prevents visual overlap/clutter on startup
995 }
996
997 if (dashboard_panel_) {
999 dashboard_panel_->Hide();
1001 dashboard_panel_->Show();
1002 }
1003 }
1004}
1005
1007 ApplyStartupVisibility(config);
1008 // Handle startup editor and panels
1009 std::string panels_str;
1010 for (size_t i = 0; i < config.open_panels.size(); ++i) {
1011 if (i > 0)
1012 panels_str += ",";
1013 panels_str += config.open_panels[i];
1014 }
1015 OpenEditorAndPanelsFromFlags(config.startup_editor, panels_str);
1016
1017 // Handle jump targets
1018 if (config.jump_to_room >= 0) {
1020 }
1021 if (config.jump_to_map >= 0) {
1023 }
1024}
1025
1043 // Process deferred actions from previous frame (both EditorManager and LayoutCoordinator)
1044 // This ensures actions that modify ImGui state (like layout resets)
1045 // are executed safely outside of menu/popup rendering contexts
1047 if (!deferred_actions_.empty()) {
1048 std::vector<std::function<void()>> actions_to_execute;
1049 actions_to_execute.swap(deferred_actions_);
1050 for (auto& action : actions_to_execute) {
1051 action();
1052 }
1053 }
1054
1055 // Update timing manager for accurate delta time across the application
1056 // This fixes animation timing issues that occur when mouse isn't moving
1057 // or window loses focus
1059
1060 // Check for layout rebuild requests and execute if needed (delegated to LayoutCoordinator)
1061 bool is_emulator_visible = ui_coordinator_ && ui_coordinator_->IsEmulatorVisible();
1063 layout_coordinator_.ProcessLayoutRebuild(current_type, is_emulator_visible);
1064
1065 // Execute keyboard shortcuts (registered via ShortcutConfigurator)
1067
1068 // Draw editor selection dialog (managed by UICoordinator)
1069 if (ui_coordinator_ && ui_coordinator_->IsEditorSelectionVisible()) {
1070 dashboard_panel_->Show();
1071 dashboard_panel_->Draw();
1072 if (!dashboard_panel_->IsVisible()) {
1073 ui_coordinator_->SetEditorSelectionVisible(false);
1074 }
1075 }
1076
1077 // Draw ROM load options dialog (ZSCustomOverworld, feature flags, project)
1080 }
1081
1082 // Draw panel browser (managed by UICoordinator)
1083 if (ui_coordinator_ && ui_coordinator_->IsPanelBrowserVisible()) {
1084 bool show = true;
1085 if (activity_bar_) {
1086 activity_bar_->DrawPanelBrowser(GetCurrentSessionId(), &show);
1087 }
1088 if (!show) {
1089 ui_coordinator_->SetPanelBrowserVisible(false);
1090 }
1091 }
1092
1093 // Update agent editor dashboard (chat drawn via RightPanelManager)
1095
1096 // Draw background grid effects for the entire viewport
1097 if (ui_coordinator_) {
1098 ui_coordinator_->DrawBackground();
1099 }
1100
1101 // Ensure TestManager always has the current ROM
1102 static Rom* last_test_rom = nullptr;
1103 auto* current_rom = GetCurrentRom();
1104 if (last_test_rom != current_rom) {
1105 LOG_DEBUG(
1106 "EditorManager",
1107 "EditorManager::Update - ROM changed, updating TestManager: %p -> "
1108 "%p",
1109 (void*)last_test_rom, (void*)current_rom);
1111 last_test_rom = current_rom;
1112 }
1113
1114 // CRITICAL: Draw UICoordinator UI components FIRST (before ROM checks)
1115 // This ensures Welcome Screen, Command Palette, etc. work even without ROM
1116 // loaded
1117 if (ui_coordinator_) {
1118 ui_coordinator_->DrawAllUI();
1119 }
1120
1121 // Get current editor set for sidebar/panel logic (needed before early return)
1122 auto* current_editor_set = GetCurrentEditorSet();
1123
1124 // Draw sidebar BEFORE early return so it appears even when no ROM is loaded
1125 // This fixes the issue where sidebar/panel drawing was unreachable without ROM
1126 if (ui_coordinator_ && ui_coordinator_->IsPanelSidebarVisible()) {
1127 // Get ALL editor categories (static list, always shown)
1128 auto all_categories = EditorRegistry::GetAllEditorCategories();
1129
1130 // Track which editors are currently active (for visual highlighting)
1131 std::unordered_set<std::string> active_editor_categories;
1132
1133 if (current_editor_set && session_coordinator_) {
1134 // ROM is loaded - collect active editors for highlighting
1135 for (size_t session_idx = 0;
1136 session_idx < session_coordinator_->GetTotalSessionCount();
1137 ++session_idx) {
1138 auto* session = static_cast<RomSession*>(
1139 session_coordinator_->GetSession(session_idx));
1140 if (!session || !session->rom.is_loaded()) {
1141 continue;
1142 }
1143
1144 for (auto* editor : session->editors.active_editors_) {
1145 if (*editor->active() && IsPanelBasedEditor(editor->type())) {
1146 std::string category =
1147 EditorRegistry::GetEditorCategory(editor->type());
1148 active_editor_categories.insert(category);
1149 }
1150 }
1151 }
1152
1153 // Add Emulator to active categories when it's visible
1154 if (ui_coordinator_->IsEmulatorVisible()) {
1155 active_editor_categories.insert("Emulator");
1156 }
1157 }
1158
1159 // Determine which category to show in sidebar
1160 std::string sidebar_category = panel_manager_.GetActiveCategory();
1161
1162 // If no active category, default to first in list
1163 if (sidebar_category.empty() && !all_categories.empty()) {
1164 sidebar_category = all_categories[0];
1165 panel_manager_.SetActiveCategory(sidebar_category);
1166 }
1167
1168 // Callback to check if ROM is loaded (for category enabled state)
1169 auto has_rom_callback = [this]() -> bool {
1170 auto* rom = GetCurrentRom();
1171 return rom && rom->is_loaded();
1172 };
1173
1174 // Draw VSCode-style sidebar with ALL categories (highlighting active ones)
1175 // Activity Bar is hidden until ROM is loaded (per startup flow design)
1176 if (activity_bar_ && ui_coordinator_->ShouldShowActivityBar()) {
1177 activity_bar_->Render(GetCurrentSessionId(), sidebar_category,
1178 all_categories, active_editor_categories,
1179 has_rom_callback);
1180 }
1181 }
1182
1183 // Draw right panel BEFORE early return (agent chat, proposals, settings)
1186 right_panel_manager_->Draw();
1187 }
1188
1189 // Update and draw status bar
1193 session_coordinator_->GetActiveSessionCount());
1194 }
1195 status_bar_.Draw();
1196
1197 // Autosave timer
1198 if (user_settings_.prefs().autosave_enabled && current_rom &&
1199 current_rom->dirty()) {
1200 autosave_timer_ += ImGui::GetIO().DeltaTime;
1202 autosave_timer_ = 0.0f;
1204 s.backup = true;
1205 s.save_new = false;
1206 auto st = current_rom->SaveToFile(s);
1207 if (st.ok()) {
1208 toast_manager_.Show("Autosave completed", editor::ToastType::kSuccess);
1209 } else {
1210 toast_manager_.Show(std::string(st.message()),
1212 }
1213 }
1214 } else {
1215 autosave_timer_ = 0.0f;
1216 }
1217
1218 // Check if ROM is loaded before allowing editor updates
1219 if (!current_editor_set) {
1220 // No ROM loaded - welcome screen shown by UICoordinator above
1221 // Sidebar and right panel have already been drawn above
1222 return absl::OkStatus();
1223 }
1224
1225 // Check if current ROM is valid
1226 if (!current_rom) {
1227 // No ROM loaded - welcome screen shown by UICoordinator above
1228 return absl::OkStatus();
1229 }
1230
1231 // ROM is loaded and valid - don't auto-show welcome screen
1232 // Welcome screen should only be shown manually at this point
1233
1234 // Delegate session updates to SessionCoordinator
1236 session_coordinator_->UpdateSessions();
1237 }
1238
1239 // Central panel drawing - once per frame for all EditorPanel instances
1240 // This draws panels based on active category, respecting pinned and persistent panels
1242
1243 if (ui_coordinator_ && ui_coordinator_->IsPerformanceDashboardVisible()) {
1245 }
1246
1247 // Proposal drawer is now drawn through RightPanelManager
1248 // Removed duplicate direct call - DrawProposalsPanel() in RightPanelManager handles it
1249
1250 // Update ROM context for agent UI
1251 if (current_rom && current_rom->is_loaded()) {
1252 agent_ui_.SetRomContext(current_rom);
1254 // Pass AsarWrapper instance from AssemblyEditor
1255 if (auto* editor_set = GetCurrentEditorSet()) {
1256 agent_ui_.SetAsarWrapperContext(editor_set->GetAssemblyEditor()->asar_wrapper());
1257 }
1258 }
1259
1260 // Draw SessionCoordinator UI components
1262 session_coordinator_->DrawSessionSwitcher();
1263 session_coordinator_->DrawSessionManager();
1264 session_coordinator_->DrawSessionRenameDialog();
1265 }
1266
1267 // Draw Layout Designer if open
1268 if (layout_designer_.IsOpen()) {
1270 }
1271
1272 return absl::OkStatus();
1273}
1274
1275// DrawContextSensitivePanelControl removed - card control is now in the sidebar
1276
1292 static bool show_display_settings = false;
1293
1294 if (ImGui::BeginMenuBar()) {
1295 // Sidebar toggle - Activity Bar visibility
1296 // Consistent button styling with other menubar buttons
1297 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
1298 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
1300 ImGui::PushStyleColor(ImGuiCol_ButtonActive,
1302
1303 // Highlight when active/visible
1305 ImGui::PushStyleColor(ImGuiCol_Text, gui::GetPrimaryVec4());
1306 } else {
1307 ImGui::PushStyleColor(ImGuiCol_Text, gui::GetTextSecondaryVec4());
1308 }
1309
1310 if (ui_coordinator_ && ui_coordinator_->IsPanelSidebarVisible()) {
1311 if (ImGui::SmallButton(ICON_MD_MENU)) {
1313 }
1314 } else {
1315 if (ImGui::SmallButton(ICON_MD_MENU_OPEN)) {
1317 }
1318 }
1319
1320 ImGui::PopStyleColor(4);
1321
1322 if (ImGui::IsItemHovered()) {
1323 const char* tooltip = panel_manager_.IsSidebarVisible()
1324 ? "Hide Activity Bar (Ctrl+B)"
1325 : "Show Activity Bar (Ctrl+B)";
1326 ImGui::SetTooltip("%s", tooltip);
1327 }
1328
1329 // Delegate menu building to MenuOrchestrator
1330 if (menu_orchestrator_) {
1331 menu_orchestrator_->BuildMainMenu();
1332 }
1333
1334 // Delegate menu bar extras to UICoordinator (status cluster on right)
1335 if (ui_coordinator_) {
1336 ui_coordinator_->DrawMenuBarExtras();
1337 }
1338
1339 ImGui::EndMenuBar();
1340 }
1341
1342 if (show_display_settings) {
1343 // Use the popup manager instead of a separate window
1345 show_display_settings = false; // Close the old-style window
1346 }
1347
1348 // ImGui debug windows (delegated to UICoordinator for visibility state)
1349 if (ui_coordinator_ && ui_coordinator_->IsImGuiDemoVisible()) {
1350 bool visible = true;
1351 ImGui::ShowDemoWindow(&visible);
1352 if (!visible) {
1353 ui_coordinator_->SetImGuiDemoVisible(false);
1354 }
1355 }
1356
1357 if (ui_coordinator_ && ui_coordinator_->IsImGuiMetricsVisible()) {
1358 bool visible = true;
1359 ImGui::ShowMetricsWindow(&visible);
1360 if (!visible) {
1361 ui_coordinator_->SetImGuiMetricsVisible(false);
1362 }
1363 }
1364
1365 // Using PanelManager directly
1366 if (auto* editor_set = GetCurrentEditorSet()) {
1367 // Pass the actual visibility flag pointer so the X button works
1368 bool* hex_visibility =
1369 panel_manager_.GetVisibilityFlag("memory.hex_editor");
1370 if (hex_visibility && *hex_visibility) {
1371 editor_set->GetMemoryEditor()->Update(*hex_visibility);
1372 }
1373
1374 if (ui_coordinator_ && ui_coordinator_->IsAsmEditorVisible()) {
1375 bool visible = true;
1376 editor_set->GetAssemblyEditor()->Update(visible);
1377 if (!visible) {
1378 ui_coordinator_->SetAsmEditorVisible(false);
1379 }
1380 }
1381 }
1382
1383 // Project file editor
1385 if (ui_coordinator_ && ui_coordinator_->IsPerformanceDashboardVisible()) {
1389 if (!gfx::PerformanceDashboard::Get().IsVisible()) {
1390 ui_coordinator_->SetPerformanceDashboardVisible(false);
1391 }
1392 }
1393
1394 // Testing interface (only when tests are enabled)
1395#ifdef YAZE_ENABLE_TESTING
1396 if (show_test_dashboard_) {
1397 auto& test_manager = test::TestManager::Get();
1398 test_manager.UpdateResourceStats(); // Update monitoring data
1399 test_manager.DrawTestDashboard(&show_test_dashboard_);
1400 }
1401#endif
1402
1403 // Update proposal drawer ROM context (drawing handled by RightPanelManager)
1405
1406 // Agent chat history popup (left side)
1408
1409 // Welcome screen is now drawn by UICoordinator::DrawAllUI()
1410 // Removed duplicate call to avoid showing welcome screen twice
1411
1412 // Emulator handling - run with UI when visible, or headless when running in background
1413 if (auto* current_rom = GetCurrentRom()) {
1414 if (ui_coordinator_ && ui_coordinator_->IsEmulatorVisible()) {
1415 // Full emulator with UI
1416 emulator_.Run(current_rom);
1417 } else if (emulator_.running() && emulator_.is_snes_initialized()) {
1418 // Emulator running in background (e.g., for music editor playback)
1419 // Use audio-focused mode when available for lower overhead and authentic sound
1422 } else {
1424 }
1425 }
1426 }
1427
1428 // Enhanced Global Search UI (managed by UICoordinator)
1429 // No longer here - handled by ui_coordinator_->DrawAllUI()
1430
1431 // NOTE: Editor updates are handled by SessionCoordinator::UpdateSessions()
1432 // which is called in EditorManager::Update(). Removed duplicate update loop
1433 // here that was causing EditorPanel::Begin() to be called twice per frame,
1434 // resulting in duplicate rendering detection logs.
1435
1436 if (ui_coordinator_ && ui_coordinator_->IsResourceLabelManagerVisible() &&
1437 GetCurrentRom()) {
1438 bool visible = true;
1439 GetCurrentRom()->resource_label()->DisplayLabels(&visible);
1443 GetCurrentRom()->resource_label()->filename_;
1444 }
1445 if (!visible) {
1446 ui_coordinator_->SetResourceLabelManagerVisible(false);
1447 }
1448 }
1449
1450 // Workspace preset dialogs are now in UICoordinator
1451
1452 // Layout presets UI (session dialogs are drawn by SessionCoordinator at lines
1453 // 907-915)
1454 if (ui_coordinator_) {
1455 ui_coordinator_->DrawLayoutPresets();
1456 }
1457}
1458
1483 if (file_name.empty()) {
1484 return absl::OkStatus();
1485 }
1486
1487 // Check if this is a project file - route to project loading
1488 if (absl::StrContains(file_name, ".yaze")) {
1489 return OpenRomOrProject(file_name);
1490 }
1491
1492 if (session_coordinator_->HasDuplicateSession(file_name)) {
1493 toast_manager_.Show("ROM already open in another session",
1495 return absl::OkStatus();
1496 }
1497
1498 // Delegate ROM loading to RomFileManager
1499 Rom temp_rom;
1500 RETURN_IF_ERROR(rom_file_manager_.LoadRom(&temp_rom, file_name));
1501
1502 auto session_or = session_coordinator_->CreateSessionFromRom(
1503 std::move(temp_rom), file_name);
1504 if (!session_or.ok()) {
1505 return session_or.status();
1506 }
1507
1510
1511 // Initialize resource labels for LoadRom() - use defaults with current project settings
1512 auto& label_provider = zelda3::GetResourceLabels();
1513 label_provider.SetProjectLabels(&current_project_.resource_labels);
1514 label_provider.SetPreferHMagicNames(
1516 LOG_INFO("EditorManager", "Initialized ResourceLabelProvider for LoadRom");
1517
1518#ifdef YAZE_ENABLE_TESTING
1520#endif
1521
1523 manager.AddFile(file_name);
1524 manager.Save();
1525
1527
1528 if (ui_coordinator_) {
1529 ui_coordinator_->SetWelcomeScreenVisible(false);
1530
1531 // Show ROM load options dialog for ZSCustomOverworld and feature settings
1534 }
1535
1536 return absl::OkStatus();
1537}
1538
1539absl::Status EditorManager::LoadAssets(uint64_t passed_handle) {
1540 auto* current_rom = GetCurrentRom();
1541 auto* current_editor_set = GetCurrentEditorSet();
1542 if (!current_rom || !current_editor_set) {
1543 return absl::FailedPreconditionError("No ROM or editor set loaded");
1544 }
1545
1546 auto start_time = std::chrono::steady_clock::now();
1547
1548#ifdef __EMSCRIPTEN__
1549 // Use passed handle if provided, otherwise create new one
1550 auto loading_handle =
1551 passed_handle != 0
1552 ? static_cast<app::platform::WasmLoadingManager::LoadingHandle>(
1553 passed_handle)
1554 : app::platform::WasmLoadingManager::BeginLoading(
1555 "Loading Editor Assets");
1556
1557 // Progress starts at 10% (ROM already loaded), goes to 100%
1558 constexpr float kStartProgress = 0.10f;
1559 constexpr float kEndProgress = 1.0f;
1560 constexpr int kTotalSteps = 11; // Graphics + 8 editors + profiler + finish
1561 int current_step = 0;
1562 auto update_progress = [&](const std::string& message) {
1563 current_step++;
1564 float progress =
1565 kStartProgress + (kEndProgress - kStartProgress) *
1566 (static_cast<float>(current_step) / kTotalSteps);
1567 app::platform::WasmLoadingManager::UpdateProgress(loading_handle, progress);
1568 app::platform::WasmLoadingManager::UpdateMessage(loading_handle, message);
1569 };
1570 // RAII guard to ensure loading indicator is closed even on early return
1571 auto cleanup_loading = [&]() {
1572 app::platform::WasmLoadingManager::EndLoading(loading_handle);
1573 };
1574 struct LoadingGuard {
1575 std::function<void()> cleanup;
1576 bool dismissed = false;
1577 ~LoadingGuard() {
1578 if (!dismissed)
1579 cleanup();
1580 }
1581 void dismiss() { dismissed = true; }
1582 } loading_guard{cleanup_loading};
1583#else
1584 (void)passed_handle; // Unused on non-WASM
1585#endif
1586
1587 // Set renderer for emulator (lazy initialization happens in Run())
1588 if (renderer_) {
1590 }
1591
1592 // Initialize all editors - this registers their cards with PanelManager
1593 // and sets up any editor-specific resources. Must be called before Load().
1594 current_editor_set->GetOverworldEditor()->Initialize();
1595 current_editor_set->GetMessageEditor()->Initialize();
1596 current_editor_set->GetGraphicsEditor()->Initialize();
1597 current_editor_set->GetScreenEditor()->Initialize();
1598 current_editor_set->GetSpriteEditor()->Initialize();
1599 current_editor_set->GetPaletteEditor()->Initialize();
1600 current_editor_set->GetAssemblyEditor()->Initialize();
1601 current_editor_set->GetMusicEditor()->Initialize();
1602
1603 // Initialize the dungeon editor with the renderer
1604 current_editor_set->GetDungeonEditor()->Initialize(renderer_, current_rom);
1605
1606#ifdef __EMSCRIPTEN__
1607 update_progress("Loading graphics sheets...");
1608#endif
1609 // Get current session's GameData and load Zelda3-specific game data
1610 auto* current_session = session_coordinator_->GetActiveRomSession();
1611 if (!current_session) {
1612 return absl::FailedPreconditionError("No active ROM session");
1613 }
1614
1615 // Load all Zelda3-specific data (metadata, palettes, gfx groups, graphics)
1617 zelda3::LoadGameData(*current_rom, current_session->game_data));
1618
1619 // Copy loaded graphics to Arena for global access
1621 current_session->game_data.gfx_bitmaps;
1622
1623 // Propagate GameData to all editors that need it
1624 auto* game_data = &current_session->game_data;
1625 current_editor_set->GetDungeonEditor()->SetGameData(game_data);
1626 current_editor_set->GetOverworldEditor()->SetGameData(game_data);
1627 current_editor_set->GetGraphicsEditor()->SetGameData(game_data);
1628 current_editor_set->GetScreenEditor()->SetGameData(game_data);
1629 current_editor_set->GetPaletteEditor()->SetGameData(game_data);
1630 current_editor_set->GetSpriteEditor()->SetGameData(game_data);
1631 current_editor_set->GetMessageEditor()->SetGameData(game_data);
1632
1633#ifdef __EMSCRIPTEN__
1634 update_progress("Loading overworld...");
1635#endif
1636 RETURN_IF_ERROR(current_editor_set->GetOverworldEditor()->Load());
1637
1638#ifdef __EMSCRIPTEN__
1639 update_progress("Loading dungeons...");
1640#endif
1641 RETURN_IF_ERROR(current_editor_set->GetDungeonEditor()->Load());
1642
1643#ifdef __EMSCRIPTEN__
1644 update_progress("Loading screen editor...");
1645#endif
1646 RETURN_IF_ERROR(current_editor_set->GetScreenEditor()->Load());
1647
1648#ifdef __EMSCRIPTEN__
1649 update_progress("Loading graphics editor...");
1650#endif
1651 RETURN_IF_ERROR(current_editor_set->GetGraphicsEditor()->Load());
1652
1653#ifdef __EMSCRIPTEN__
1654 update_progress("Loading settings...");
1655#endif
1656 // Settings panel doesn't need Load()
1657 // RETURN_IF_ERROR(current_editor_set->settings_editor_.Load());
1658
1659#ifdef __EMSCRIPTEN__
1660 update_progress("Loading sprites...");
1661#endif
1662 RETURN_IF_ERROR(current_editor_set->GetSpriteEditor()->Load());
1663
1664#ifdef __EMSCRIPTEN__
1665 update_progress("Loading messages...");
1666#endif
1667 RETURN_IF_ERROR(current_editor_set->GetMessageEditor()->Load());
1668
1669#ifdef __EMSCRIPTEN__
1670 update_progress("Loading music...");
1671#endif
1672 RETURN_IF_ERROR(current_editor_set->GetMusicEditor()->Load());
1673
1674#ifdef __EMSCRIPTEN__
1675 update_progress("Loading palettes...");
1676#endif
1677 RETURN_IF_ERROR(current_editor_set->GetPaletteEditor()->Load());
1678
1679#ifdef __EMSCRIPTEN__
1680 update_progress("Finishing up...");
1681#endif
1682
1683 // Set up RightPanelManager with session's settings editor
1685 auto* settings = current_editor_set->GetSettingsPanel();
1686 right_panel_manager_->SetSettingsPanel(settings);
1687 // Also update project context for settings panel
1688 if (settings) {
1689 settings->SetProject(&current_project_);
1690 }
1691 }
1692
1693 // Set up StatusBar reference on settings panel for live toggling
1694 if (auto* settings = current_editor_set->GetSettingsPanel()) {
1695 settings->SetStatusBar(&status_bar_);
1696 }
1697
1698 // Apply user preferences to status bar
1700
1702
1703#ifdef __EMSCRIPTEN__
1704 // Dismiss the guard and manually close - we completed successfully
1705 loading_guard.dismiss();
1706 app::platform::WasmLoadingManager::EndLoading(loading_handle);
1707#endif
1708
1709 auto end_time = std::chrono::steady_clock::now();
1710 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
1711 end_time - start_time);
1712 LOG_DEBUG("EditorManager", "ROM assets loaded in %lld ms", duration.count());
1713
1714 return absl::OkStatus();
1715}
1716
1733 auto* current_rom = GetCurrentRom();
1734 auto* current_editor_set = GetCurrentEditorSet();
1735 if (!current_rom || !current_editor_set) {
1736 return absl::FailedPreconditionError("No ROM or editor set loaded");
1737 }
1738
1739 // Save editor-specific data first
1740 if (core::FeatureFlags::get().kSaveDungeonMaps) {
1742 *current_rom, current_editor_set->GetScreenEditor()->dungeon_maps_));
1743 }
1744
1745 RETURN_IF_ERROR(current_editor_set->GetOverworldEditor()->Save());
1746
1747 if (core::FeatureFlags::get().kSaveGraphicsSheet)
1749 *current_rom, gfx::Arena::Get().gfx_sheets()));
1750
1751 // Delegate final ROM file writing to RomFileManager
1752 return rom_file_manager_.SaveRom(current_rom);
1753}
1754
1755absl::Status EditorManager::SaveRomAs(const std::string& filename) {
1756 auto* current_rom = GetCurrentRom();
1757 if (!current_rom) {
1758 return absl::FailedPreconditionError("No ROM loaded");
1759 }
1760
1761 // Reuse SaveRom() logic for editor-specific data saving
1763
1764 // Now save to the new filename
1765 auto save_status = rom_file_manager_.SaveRomAs(current_rom, filename);
1766 if (save_status.ok()) {
1767 // Update session filepath
1769 auto* session = session_coordinator_->GetActiveRomSession();
1770 if (session) {
1771 session->filepath = filename;
1772 }
1773 }
1774
1775 // Add to recent files
1777 manager.AddFile(filename);
1778 manager.Save();
1779 }
1780
1781 return save_status;
1782}
1783
1784absl::Status EditorManager::OpenRomOrProject(const std::string& filename) {
1785 LOG_INFO("EditorManager", "OpenRomOrProject called with: '%s'",
1786 filename.c_str());
1787 if (filename.empty()) {
1788 LOG_INFO("EditorManager", "Empty filename provided, skipping load.");
1789 return absl::OkStatus();
1790 }
1791
1792#ifdef __EMSCRIPTEN__
1793 // Start loading indicator early for WASM builds
1794 auto loading_handle =
1795 app::platform::WasmLoadingManager::BeginLoading("Loading ROM");
1796 app::platform::WasmLoadingManager::UpdateMessage(loading_handle,
1797 "Reading ROM file...");
1798 // RAII guard to ensure loading indicator is closed even on early return
1799 struct LoadingGuard {
1800 app::platform::WasmLoadingManager::LoadingHandle handle;
1801 bool dismissed = false;
1802 ~LoadingGuard() {
1803 if (!dismissed)
1804 app::platform::WasmLoadingManager::EndLoading(handle);
1805 }
1806 void dismiss() { dismissed = true; }
1807 } loading_guard{loading_handle};
1808#endif
1809
1810 if (absl::StrContains(filename, ".yaze")) {
1811 // Open the project file
1813
1814 // Initialize VersionManager for the project
1815 version_manager_ = std::make_unique<core::VersionManager>(&current_project_);
1816 version_manager_->InitializeGit(); // Try to init git if configured
1817
1818 // Load ROM directly from project - don't prompt user
1819 return LoadProjectWithRom();
1820 } else {
1821#ifdef __EMSCRIPTEN__
1822 app::platform::WasmLoadingManager::UpdateProgress(loading_handle, 0.05f);
1823 app::platform::WasmLoadingManager::UpdateMessage(loading_handle,
1824 "Loading ROM data...");
1825#endif
1826 Rom temp_rom;
1827 RETURN_IF_ERROR(rom_file_manager_.LoadRom(&temp_rom, filename));
1828
1829 auto session_or = session_coordinator_->CreateSessionFromRom(
1830 std::move(temp_rom), filename);
1831 if (!session_or.ok()) {
1832 return session_or.status();
1833 }
1834 RomSession* session = *session_or;
1835
1838
1839 // Apply project feature flags to both session and global singleton
1842
1843 // Initialize resource labels for ROM-only loading (use defaults)
1844 // This ensures labels are available before any editors access them
1845 auto& label_provider = zelda3::GetResourceLabels();
1846 label_provider.SetProjectLabels(&current_project_.resource_labels);
1847 label_provider.SetPreferHMagicNames(
1849 LOG_INFO("EditorManager", "Initialized ResourceLabelProvider for ROM-only load");
1850
1851 // Update test manager with current ROM for ROM-dependent tests (only when
1852 // tests are enabled)
1853#ifdef YAZE_ENABLE_TESTING
1854 LOG_DEBUG("EditorManager", "Setting ROM in TestManager - %p ('%s')",
1855 (void*)GetCurrentRom(),
1856 GetCurrentRom() ? GetCurrentRom()->title().c_str() : "null");
1858#endif
1859
1860 if (auto* editor_set = GetCurrentEditorSet();
1861 editor_set && !current_project_.code_folder.empty()) {
1862 editor_set->GetAssemblyEditor()->OpenFolder(current_project_.code_folder);
1863 // Also set the sidebar file browser path
1866 }
1867
1868#ifdef __EMSCRIPTEN__
1869 app::platform::WasmLoadingManager::UpdateProgress(loading_handle, 0.10f);
1870 app::platform::WasmLoadingManager::UpdateMessage(loading_handle,
1871 "Initializing editors...");
1872 // Pass the loading handle to LoadAssets and dismiss our guard
1873 // LoadAssets will manage closing the indicator when done
1874 loading_guard.dismiss();
1875 RETURN_IF_ERROR(LoadAssets(loading_handle));
1876#else
1878#endif
1879
1880 // Hide welcome screen and show editor selection when ROM is loaded
1881 ui_coordinator_->SetWelcomeScreenVisible(false);
1882 // dashboard_panel_->ClearRecentEditors();
1883 ui_coordinator_->SetEditorSelectionVisible(true);
1884
1885 // Set Dashboard category to suppress panel drawing until user selects an editor
1887 }
1888 return absl::OkStatus();
1889}
1890
1891absl::Status EditorManager::CreateNewProject(const std::string& template_name) {
1892 // Delegate to ProjectManager
1893 auto status = project_manager_.CreateNewProject(template_name);
1894 if (status.ok()) {
1896
1897 // Trigger ROM selection dialog - projects need a ROM to be useful
1898 // LoadRom() opens file dialog and shows ROM load options when ROM is loaded
1899 status = LoadRom();
1900 if (status.ok() && ui_coordinator_) {
1901 ui_coordinator_->SetWelcomeScreenVisible(false);
1902 ui_coordinator_->SetWelcomeScreenManuallyClosed(true);
1903 }
1904 }
1905 return status;
1906}
1907
1910 if (file_path.empty()) {
1911 return absl::OkStatus();
1912 }
1913
1914 project::YazeProject new_project;
1915 RETURN_IF_ERROR(new_project.Open(file_path));
1916
1917 // Validate project
1918 auto validation_status = new_project.Validate();
1919 if (!validation_status.ok()) {
1920 toast_manager_.Show(absl::StrFormat("Project validation failed: %s",
1921 validation_status.message()),
1923
1924 // Ask user if they want to repair
1925 popup_manager_->Show("Project Repair");
1926 }
1927
1928 current_project_ = std::move(new_project);
1929
1930 // Initialize VersionManager for the project
1931 version_manager_ = std::make_unique<core::VersionManager>(&current_project_);
1932 version_manager_->InitializeGit();
1933
1934 return LoadProjectWithRom();
1935}
1936
1938 // Check if project has a ROM file specified
1939 if (current_project_.rom_filename.empty()) {
1940 // No ROM specified - prompt user to select one
1941 toast_manager_.Show("Project has no ROM file configured. Please select a ROM.",
1944 if (rom_path.empty()) {
1945 return absl::OkStatus();
1946 }
1947 current_project_.rom_filename = rom_path;
1948 // Save updated project
1950 }
1951
1952 // Load ROM from project
1953 Rom temp_rom;
1954 auto load_status = rom_file_manager_.LoadRom(&temp_rom, current_project_.rom_filename);
1955 if (!load_status.ok()) {
1956 // ROM file not found or invalid - prompt user to select new ROM
1958 absl::StrFormat("Could not load ROM '%s': %s. Please select a new ROM.",
1959 current_project_.rom_filename, load_status.message()),
1961
1963 if (rom_path.empty()) {
1964 return absl::OkStatus();
1965 }
1966 current_project_.rom_filename = rom_path;
1968 RETURN_IF_ERROR(rom_file_manager_.LoadRom(&temp_rom, rom_path));
1969 }
1970
1971 auto session_or = session_coordinator_->CreateSessionFromRom(
1972 std::move(temp_rom), current_project_.rom_filename);
1973 if (!session_or.ok()) {
1974 return session_or.status();
1975 }
1976 RomSession* session = *session_or;
1977
1980
1981 // Apply project feature flags to both session and global singleton
1984
1985 // Update test manager with current ROM for ROM-dependent tests (only when
1986 // tests are enabled)
1987#ifdef YAZE_ENABLE_TESTING
1988 LOG_DEBUG("EditorManager", "Setting ROM in TestManager - %p ('%s')",
1989 (void*)GetCurrentRom(),
1990 GetCurrentRom() ? GetCurrentRom()->title().c_str() : "null");
1992#endif
1993
1994 if (auto* editor_set = GetCurrentEditorSet();
1995 editor_set && !current_project_.code_folder.empty()) {
1996 editor_set->GetAssemblyEditor()->OpenFolder(current_project_.code_folder);
1997 // Also set the sidebar file browser path
2000 }
2001
2003
2004 // Hide welcome screen and show editor selection when project ROM is loaded
2005 if (ui_coordinator_) {
2006 ui_coordinator_->SetWelcomeScreenVisible(false);
2007 ui_coordinator_->SetEditorSelectionVisible(true);
2008 }
2009
2010 // Set Dashboard category to suppress panel drawing until user selects an editor
2012
2013 // Apply workspace settings
2020 ImGui::GetIO().FontGlobalScale = user_settings_.prefs().font_global_scale;
2021
2022 // Initialize resource labels early - before any editors access them
2024 LOG_INFO("EditorManager", "Initialized ResourceLabelProvider with project labels");
2025
2026 // Add to recent files
2028 manager.AddFile(current_project_.filepath);
2029 manager.Save();
2030
2031 // Update project management panel with loaded project
2034 project_management_panel_->SetVersionManager(version_manager_.get());
2036 }
2037
2038 toast_manager_.Show(absl::StrFormat("Project '%s' loaded successfully",
2041
2042 return absl::OkStatus();
2043}
2044
2047 return CreateNewProject();
2048 }
2049
2050 // Update project with current settings
2053 auto* session = session_coordinator_->GetActiveRomSession();
2054 if (session) {
2055 current_project_.feature_flags = session->feature_flags;
2056 }
2057 }
2058
2065
2066 // Save recent files
2069 for (const auto& file : manager.GetRecentFiles()) {
2071 }
2072 }
2073
2074 return current_project_.Save();
2075}
2076
2078 // Get current project name for default filename
2079 std::string default_name = current_project_.project_opened()
2081 : "untitled_project";
2082
2083 auto file_path =
2084 util::FileDialogWrapper::ShowSaveFileDialog(default_name, "yaze");
2085 if (file_path.empty()) {
2086 return absl::OkStatus();
2087 }
2088
2089 // Ensure .yaze extension
2090 if (file_path.find(".yaze") == std::string::npos) {
2091 file_path += ".yaze";
2092 }
2093
2094 // Update project filepath and save
2095 std::string old_filepath = current_project_.filepath;
2096 current_project_.filepath = file_path;
2097
2098 auto save_status = current_project_.Save();
2099 if (save_status.ok()) {
2100 // Add to recent files
2102 manager.AddFile(file_path);
2103 manager.Save();
2104
2105 toast_manager_.Show(absl::StrFormat("Project saved as: %s", file_path),
2107 } else {
2108 // Restore old filepath on failure
2109 current_project_.filepath = old_filepath;
2111 absl::StrFormat("Failed to save project: %s", save_status.message()),
2113 }
2114
2115 return save_status;
2116}
2117
2118absl::Status EditorManager::ImportProject(const std::string& project_path) {
2119 // Delegate to ProjectManager for import logic
2121 // Sync local project reference
2123 return absl::OkStatus();
2124}
2125
2128 return absl::FailedPreconditionError("No project is currently open");
2129 }
2130
2132 toast_manager_.Show("Project repaired successfully",
2134
2135 return absl::OkStatus();
2136}
2137
2139 if (!rom) {
2140 return absl::InvalidArgumentError("Invalid ROM pointer");
2141 }
2142
2143 // We need to find the session that owns this ROM.
2144 // This is inefficient but SetCurrentRom is rare.
2146 for (size_t i = 0; i < session_coordinator_->GetTotalSessionCount(); ++i) {
2147 auto* session =
2148 static_cast<RomSession*>(session_coordinator_->GetSession(i));
2149 if (session && &session->rom == rom) {
2150 session_coordinator_->SwitchToSession(i);
2151 // Update test manager with current ROM for ROM-dependent tests
2153 return absl::OkStatus();
2154 }
2155 }
2156 }
2157 // If ROM wasn't found in existing sessions, treat as new session.
2158 // Copying an external ROM object is avoided; instead, fail.
2159 return absl::NotFoundError("ROM not found in existing sessions");
2160}
2161
2164 session_coordinator_->CreateNewSession();
2165 // Toast messages are now shown by SessionCoordinator
2166 }
2167}
2168
2171 session_coordinator_->DuplicateCurrentSession();
2172 }
2173}
2174
2177 session_coordinator_->CloseCurrentSession();
2178 }
2179}
2180
2183 session_coordinator_->RemoveSession(index);
2184 }
2185}
2186
2189 // Delegate to SessionCoordinator - cross-cutting concerns
2190 // are handled by OnSessionSwitched() observer callback
2191 session_coordinator_->SwitchToSession(index);
2192 }
2193}
2194
2196 return session_coordinator_ ? session_coordinator_->GetActiveSessionIndex()
2197 : 0;
2198}
2199
2201 return session_coordinator_ ? session_coordinator_->GetActiveSessionCount()
2202 : 0;
2203}
2204
2206 EditorType type, size_t session_index) const {
2207 const char* base_name = kEditorNames[static_cast<int>(type)];
2209 ? session_coordinator_->GenerateUniqueEditorTitle(base_name,
2210 session_index)
2211 : std::string(base_name);
2212}
2213
2214// ============================================================================
2215// Jump-to Functionality for Cross-Editor Navigation
2216// ============================================================================
2217
2221
2225
2226void EditorManager::SwitchToEditor(EditorType editor_type, bool force_visible,
2227 bool from_dialog) {
2228 // Special case: Agent editor requires EditorManager-specific handling
2229#ifdef YAZE_BUILD_AGENT_UI
2230 if (editor_type == EditorType::kAgent) {
2231 ShowAIAgent();
2232 return;
2233 }
2234#endif
2235
2236 // Delegate all other editor switching to EditorActivator
2237 editor_activator_.SwitchToEditor(editor_type, force_visible, from_dialog);
2238}
2239
2241 if (!session)
2242 return;
2244 ConfigureEditorDependencies(&session->editors, &session->rom,
2245 session->editors.session_id());
2246}
2247
2248// SessionScope implementation
2250 size_t session_id)
2251 : manager_(manager),
2252 prev_rom_(manager->GetCurrentRom()),
2253 prev_editor_set_(manager->GetCurrentEditorSet()),
2254 prev_session_id_(manager->GetCurrentSessionId()) {
2255 // Set new session context
2256 manager_->session_coordinator_->SwitchToSession(session_id);
2257}
2258
2260 // Restore previous context
2261 manager_->session_coordinator_->SwitchToSession(prev_session_id_);
2262}
2263
2264bool EditorManager::HasDuplicateSession(const std::string& filepath) {
2265 return session_coordinator_ &&
2266 session_coordinator_->HasDuplicateSession(filepath);
2267}
2268
2295 // Update project panel context before showing
2298 project_management_panel_->SetVersionManager(version_manager_.get());
2300 }
2302 }
2303}
2304
2306 // Load the current project file into the editor
2307 if (!current_project_.filepath.empty()) {
2309 if (!status.ok()) {
2311 absl::StrFormat("Failed to load project file: %s", status.message()),
2313 return;
2314 }
2315 }
2316 // Set the project pointer for label import functionality
2318 // Activate the editor window
2320}
2321
2323 size_t session_id) {
2324 if (!editor_set) {
2325 return;
2326 }
2327
2328 EditorDependencies deps;
2329 deps.rom = rom;
2330 deps.session_id = session_id;
2333 deps.popup_manager = popup_manager_.get();
2337 deps.project = &current_project_;
2338 deps.version_manager = version_manager_.get();
2339 deps.renderer = renderer_;
2340 deps.emulator = &emulator_;
2341
2342 editor_set->ApplyDependencies(deps);
2343
2344 // If configuring the active session, update the properties panel
2345 if (session_id == GetCurrentSessionId()) {
2347 }
2348}
2349
2350} // namespace editor
2351} // namespace yaze
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")
layout_designer::LayoutDesignerWindow layout_designer_
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)
void Draw()
Draw the designer window (call every frame)
bool IsOpen() const
Check if designer window is open.
void Initialize(PanelManager *panel_manager, yaze::editor::LayoutManager *layout_manager=nullptr, yaze::editor::EditorManager *editor_manager=nullptr)
Initialize the designer with manager references.
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:406
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:127
static Arena & Get()
Definition arena.cc:19
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:310
void SetCurrentRom(Rom *rom)
static TestManager & Get()
static std::string ShowSaveFileDialog(const std::string &default_name="", const std::string &default_extension="")
ShowSaveFileDialog opens a save file dialog and returns the selected filepath. Uses global feature fl...
static std::string ShowOpenFileDialog()
ShowOpenFileDialog opens a file dialog and returns the selected filepath. Uses global feature flag to...
static std::string ShowOpenFolderDialog()
ShowOpenFolderDialog opens a file dialog and returns the selected folder path. Uses global feature fl...
#define 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)
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()
absl::Status LoadGameData(Rom &rom, GameData &data, const LoadOptions &options)
Loads all Zelda3-specific game data from a generic ROM.
Definition game_data.cc:80
absl::Status SaveAllGraphicsData(Rom &rom, const std::array< gfx::Bitmap, kNumGfxSheets > &sheets)
Saves all graphics sheets back to ROM.
Definition game_data.cc:516
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:23
std::string startup_editor
Definition application.h:34
StartupVisibility welcome_mode
Definition application.h:29
std::vector< std::string > open_panels
Definition application.h:35
StartupVisibility sidebar_mode
Definition application.h:31
StartupVisibility dashboard_mode
Definition application.h:30
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:225
void InitializeResourceLabelProvider()
Initialize the global ResourceLabelProvider with this project's labels.
Definition project.cc:1451
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:108
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:106
absl::Status Open(const std::string &project_path)
Definition project.cc:151
std::string code_folder
Definition project.h:97
absl::Status Validate() const
Definition project.cc:738
core::FeatureFlags::Flags feature_flags
Definition project.h:105