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#include "editor_manager.h"
2
3#include <algorithm>
4#include <chrono>
5#include <cstring>
6
7#define IMGUI_DEFINE_MATH_OPERATORS
8
9#include "absl/status/status.h"
10#include "absl/status/statusor.h"
11#include "absl/strings/match.h"
12#include "absl/strings/str_cat.h"
13#include "app/core/features.h"
14#include "app/core/timing.h"
15#include "util/file_util.h"
17#include "imgui/imgui.h"
18#include "util/log.h"
19#include "util/platform_paths.h"
20#include "app/core/project.h"
30#include "app/emu/emulator.h"
31#include "app/gfx/arena.h"
34#include "app/gui/icons.h"
35#include "app/gui/input.h"
36#include "app/gui/style.h"
38#include "app/rom.h"
41#ifdef YAZE_ENABLE_TESTING
46#endif
47#ifdef YAZE_ENABLE_GTEST
49#endif
50
53#include "app/emu/emulator.h"
55#include "app/editor/editor.h"
56
57#ifdef YAZE_WITH_GRPC
64
65#include "absl/flags/flag.h"
66
67// Declare the agent_control flag (defined in src/cli/flags.cc)
68// ABSL_DECLARE_FLAG(bool, agent_control);
69
71#endif
72
73#include "imgui/imgui.h"
74#include "imgui/misc/cpp/imgui_stdlib.h"
75#include "util/log.h"
76#include "util/macro.h"
77#include "yaze_config.h"
78
79namespace yaze {
80namespace editor {
81
82using namespace ImGui;
83using util::FileDialogWrapper;
84
85namespace {
86
87std::string GetEditorName(EditorType type) {
88 return kEditorNames[static_cast<int>(type)];
89}
90
91} // namespace
92
93EditorManager::EditorManager() : blank_editor_set_(nullptr, &user_settings_) {
94 std::stringstream ss;
95 ss << YAZE_VERSION_MAJOR << "." << YAZE_VERSION_MINOR << "."
96 << YAZE_VERSION_PATCH;
97 ss >> version_;
99}
100
102
104 auto& test_manager = test::TestManager::Get();
105
106#ifdef YAZE_ENABLE_TESTING
107 // Register comprehensive test suites
108 test_manager.RegisterTestSuite(std::make_unique<test::IntegratedTestSuite>());
109 test_manager.RegisterTestSuite(
110 std::make_unique<test::PerformanceTestSuite>());
111 test_manager.RegisterTestSuite(std::make_unique<test::UITestSuite>());
112 test_manager.RegisterTestSuite(
113 std::make_unique<test::RomDependentTestSuite>());
114
115 // Register new E2E and ZSCustomOverworld test suites
116 test_manager.RegisterTestSuite(std::make_unique<test::E2ETestSuite>());
117 test_manager.RegisterTestSuite(
118 std::make_unique<test::ZSCustomOverworldTestSuite>());
119#endif
120
121 // Register Google Test suite if available
122#ifdef YAZE_ENABLE_GTEST
123 test_manager.RegisterTestSuite(std::make_unique<test::UnitTestSuite>());
124#endif
125
126 // Register z3ed AI Agent test suites (requires gRPC)
127#ifdef YAZE_WITH_GRPC
129#endif
130
131 // Update resource monitoring to track Arena state
132 test_manager.UpdateResourceStats();
133}
134
135constexpr const char* kOverworldEditorName = ICON_MD_LAYERS " Overworld Editor";
136constexpr const char* kGraphicsEditorName = ICON_MD_PHOTO " Graphics Editor";
137constexpr const char* kPaletteEditorName = ICON_MD_PALETTE " Palette Editor";
138constexpr const char* kScreenEditorName = ICON_MD_SCREENSHOT " Screen Editor";
139constexpr const char* kSpriteEditorName = ICON_MD_SMART_TOY " Sprite Editor";
140constexpr const char* kMessageEditorName = ICON_MD_MESSAGE " Message Editor";
141constexpr const char* kSettingsEditorName = ICON_MD_SETTINGS " Settings Editor";
142constexpr const char* kAssemblyEditorName = ICON_MD_CODE " Assembly Editor";
143constexpr const char* kDungeonEditorName = ICON_MD_CASTLE " Dungeon Editor";
144constexpr const char* kMusicEditorName = ICON_MD_MUSIC_NOTE " Music Editor";
145
146void EditorManager::Initialize(gfx::IRenderer* renderer, const std::string& filename) {
147 renderer_ = renderer;
148
149 // NOTE: Emulator will be initialized later when a ROM is loaded
150 // We just store the renderer for now
151
152 // Point to a blank editor set when no ROM is loaded
154
155 if (!filename.empty()) {
157 }
158
159 // Initialize popup manager
160 popup_manager_ = std::make_unique<PopupManager>(this);
161 popup_manager_->Initialize();
162
163 // Set the popup manager in the context
165
166 // Initialize project file editor
168
169#ifdef YAZE_WITH_GRPC
170 // Initialize the agent editor as a proper Editor (configuration dashboard)
171 agent_editor_.set_context(&context_);
172 agent_editor_.Initialize();
173 agent_editor_.InitializeWithDependencies(&toast_manager_, &proposal_drawer_, nullptr);
174
175 // Initialize and connect the chat history popup
177 if (agent_editor_.GetChatWidget()) {
178 agent_editor_.GetChatWidget()->SetChatHistoryPopup(&agent_chat_history_popup_);
179 }
180
181 // Set up multimodal (vision) callbacks for Gemini
182 AgentChatWidget::MultimodalCallbacks multimodal_callbacks;
183 multimodal_callbacks.capture_snapshot =
184 [this](std::filesystem::path* output_path) -> absl::Status {
185 using CaptureMode = AgentChatWidget::CaptureMode;
186
187 absl::StatusOr<yaze::test::ScreenshotArtifact> result;
188
189 // Capture based on selected mode
190 switch (agent_editor_.GetChatWidget()->capture_mode()) {
191 case CaptureMode::kFullWindow:
192 result = yaze::test::CaptureHarnessScreenshot("");
193 break;
194
195 case CaptureMode::kActiveEditor:
196 result = yaze::test::CaptureActiveWindow("");
197 if (!result.ok()) {
198 // Fallback to full window if no active window
199 result = yaze::test::CaptureHarnessScreenshot("");
200 }
201 break;
202
203 case CaptureMode::kSpecificWindow: {
204 const char* window_name = agent_editor_.GetChatWidget()->specific_window_name();
205 if (window_name && std::strlen(window_name) > 0) {
206 result = yaze::test::CaptureWindowByName(window_name, "");
207 if (!result.ok()) {
208 // Fallback to active window if specific window not found
209 result = yaze::test::CaptureActiveWindow("");
210 }
211 } else {
212 result = yaze::test::CaptureActiveWindow("");
213 }
214 if (!result.ok()) {
215 result = yaze::test::CaptureHarnessScreenshot("");
216 }
217 break;
218 }
219 }
220
221 if (!result.ok()) {
222 return result.status();
223 }
224 *output_path = result->file_path;
225 return absl::OkStatus();
226 };
227 multimodal_callbacks.send_to_gemini =
228 [this](const std::filesystem::path& image_path,
229 const std::string& prompt) -> absl::Status {
230 // Get Gemini API key from environment
231 const char* api_key = std::getenv("GEMINI_API_KEY");
232 if (!api_key || std::strlen(api_key) == 0) {
233 return absl::FailedPreconditionError(
234 "GEMINI_API_KEY environment variable not set");
235 }
236
237 // Create Gemini service
238 cli::GeminiConfig config;
239 config.api_key = api_key;
240 config.model = "gemini-2.5-flash"; // Use vision-capable model
241 config.verbose = false;
242
243 cli::GeminiAIService gemini_service(config);
244
245 // Generate multimodal response
246 auto response =
247 gemini_service.GenerateMultimodalResponse(image_path.string(), prompt);
248 if (!response.ok()) {
249 return response.status();
250 }
251
252 // Add the response to chat history
253 cli::agent::ChatMessage agent_msg;
255 agent_msg.message = response->text_response;
256 agent_msg.timestamp = absl::Now();
257 agent_editor_.GetChatWidget()->SetRomContext(current_rom_);
258
259 return absl::OkStatus();
260 };
261 agent_editor_.GetChatWidget()->SetMultimodalCallbacks(multimodal_callbacks);
262
263 // Set up Z3ED command callbacks for proposal management
265
266 z3ed_callbacks.accept_proposal = [this](const std::string& proposal_id) -> absl::Status {
267 // Use ProposalDrawer's existing logic
269 proposal_drawer_.FocusProposal(proposal_id);
270
272 absl::StrFormat("%s View proposal %s in drawer to accept", ICON_MD_PREVIEW, proposal_id),
273 ToastType::kInfo, 3.5f);
274
275 return absl::OkStatus();
276 };
277
278 z3ed_callbacks.reject_proposal = [this](const std::string& proposal_id) -> absl::Status {
279 // Use ProposalDrawer's existing logic
281 proposal_drawer_.FocusProposal(proposal_id);
282
284 absl::StrFormat("%s View proposal %s in drawer to reject", ICON_MD_PREVIEW, proposal_id),
285 ToastType::kInfo, 3.0f);
286
287 return absl::OkStatus();
288 };
289
290 z3ed_callbacks.list_proposals = []() -> absl::StatusOr<std::vector<std::string>> {
291 // Return empty for now - ProposalDrawer handles the real list
292 return std::vector<std::string>{};
293 };
294
295 z3ed_callbacks.diff_proposal = [this](const std::string& proposal_id) -> absl::StatusOr<std::string> {
296 // Open drawer to show diff
298 proposal_drawer_.FocusProposal(proposal_id);
299 return "See diff in proposal drawer";
300 };
301
302 agent_editor_.GetChatWidget()->SetZ3EDCommandCallbacks(z3ed_callbacks);
303
304 AgentChatWidget::AutomationCallbacks automation_callbacks;
305 automation_callbacks.open_harness_dashboard = [this]() {
307 };
308 automation_callbacks.show_active_tests = [this]() {
310 };
311 automation_callbacks.replay_last_plan = [this]() {
313 };
314 automation_callbacks.focus_proposal = [this](const std::string& proposal_id) {
316 proposal_drawer_.FocusProposal(proposal_id);
317 };
318 agent_editor_.GetChatWidget()->SetAutomationCallbacks(automation_callbacks);
319
320 harness_telemetry_bridge_.SetChatWidget(agent_editor_.GetChatWidget());
321 test::TestManager::Get().SetHarnessListener(&harness_telemetry_bridge_);
322#endif
323
324 // Load critical user settings first
326 if (!status_.ok()) {
327 LOG_WARN("EditorManager", "Failed to load user settings: %s", status_.ToString().c_str());
328 }
329
330 // Initialize welcome screen callbacks
332 status_ = LoadRom();
333 // LoadRom() already handles closing welcome screen and showing editor selection
334 });
335
338 if (status_.ok()) {
339 show_welcome_screen_ = false;
341 }
342 });
343
344 welcome_screen_.SetOpenProjectCallback([this](const std::string& filepath) {
345 status_ = OpenRomOrProject(filepath);
346 if (status_.ok()) {
347 show_welcome_screen_ = false;
349 }
350 });
351
352 // Initialize editor selection dialog callback
355
356 // Handle agent editor separately (doesn't require ROM)
357 if (type == EditorType::kAgent) {
358#ifdef YAZE_WITH_GRPC
359 agent_editor_.set_active(true);
360#endif
361 return;
362 }
363
364 if (!current_editor_set_) return;
365
366 switch (type) {
369 break;
372 break;
375 break;
378 break;
381 break;
384 break;
387 break;
390 break;
392 show_asm_editor_ = true;
393 break;
395 show_emulator_ = true;
396 break;
399 break;
400 default:
401 break;
402 }
403 });
404
405 // Load user settings - this must happen after context is initialized
407
408 // Defer workspace presets loading to avoid initialization crashes
409 // This will be called lazily when workspace features are accessed
410
411 // Initialize testing system only when tests are enabled
412#ifdef YAZE_ENABLE_TESTING
414#endif
415
416 // TestManager will be updated when ROMs are loaded via SetCurrentRom calls
417
419 "Open", {ImGuiKey_O, ImGuiMod_Ctrl}, [this]() { status_ = LoadRom(); });
421 "Save", {ImGuiKey_S, ImGuiMod_Ctrl}, [this]() { status_ = SaveRom(); });
423 "Close", {ImGuiKey_W, ImGuiMod_Ctrl}, [this]() {
424 if (current_rom_)
426 });
428 "Quit", {ImGuiKey_Q, ImGuiMod_Ctrl}, [this]() { quit_ = true; });
429
431 "Undo", {ImGuiKey_Z, ImGuiMod_Ctrl}, [this]() {
432 if (current_editor_)
434 });
436 "Redo", {ImGuiKey_Y, ImGuiMod_Ctrl}, [this]() {
437 if (current_editor_)
439 });
441 "Cut", {ImGuiKey_X, ImGuiMod_Ctrl}, [this]() {
442 if (current_editor_)
444 });
446 "Copy", {ImGuiKey_C, ImGuiMod_Ctrl}, [this]() {
447 if (current_editor_)
449 });
451 "Paste", {ImGuiKey_V, ImGuiMod_Ctrl}, [this]() {
452 if (current_editor_)
454 });
456 "Find", {ImGuiKey_F, ImGuiMod_Ctrl}, [this]() {
457 if (current_editor_)
459 });
460
461 // Command Palette and Global Search
463 "Command Palette", {ImGuiKey_P, ImGuiMod_Ctrl, ImGuiMod_Shift},
464 [this]() { show_command_palette_ = true; });
466 "Global Search", {ImGuiKey_K, ImGuiMod_Ctrl, ImGuiMod_Shift},
467 [this]() { show_global_search_ = true; });
468
470 "Load Last ROM", {ImGuiKey_R, ImGuiMod_Ctrl}, [this]() {
472 if (!manager.GetRecentFiles().empty()) {
473 auto front = manager.GetRecentFiles().front();
474 status_ = OpenRomOrProject(front);
475 }
476 });
477
479 "F1", ImGuiKey_F1, [this]() { popup_manager_->Show("About"); });
480
481 // Editor shortcuts (Ctrl+1-9, Ctrl+0)
483 "Overworld Editor", {ImGuiKey_1, ImGuiMod_Ctrl},
486 "Dungeon Editor", {ImGuiKey_2, ImGuiMod_Ctrl},
489 "Graphics Editor", {ImGuiKey_3, ImGuiMod_Ctrl},
492 "Sprite Editor", {ImGuiKey_4, ImGuiMod_Ctrl},
495 "Message Editor", {ImGuiKey_5, ImGuiMod_Ctrl},
498 "Music Editor", {ImGuiKey_6, ImGuiMod_Ctrl},
501 "Palette Editor", {ImGuiKey_7, ImGuiMod_Ctrl},
504 "Screen Editor", {ImGuiKey_8, ImGuiMod_Ctrl},
507 "Assembly Editor", {ImGuiKey_9, ImGuiMod_Ctrl},
508 [this]() { show_asm_editor_ = true; });
510 "Settings Editor", {ImGuiKey_0, ImGuiMod_Ctrl},
512
513 // Editor Selection Dialog shortcut
515 "Editor Selection", {ImGuiKey_E, ImGuiMod_Ctrl},
516 [this]() { show_editor_selection_ = true; });
517
518 // Card Browser shortcut
520 "Card Browser", {ImGuiKey_B, ImGuiMod_Ctrl, ImGuiMod_Shift},
521 [this]() { show_card_browser_ = true; });
522
523 // === SIMPLIFIED CARD SHORTCUTS - Use Card Browser instead of individual shortcuts ===
524 // Individual card shortcuts removed to prevent hash table overflow
525 // Users can:
526 // 1. Use Card Browser (Ctrl+Shift+B) to toggle any card
527 // 2. Use compact card control button in menu bar
528 // 3. Use View menu for category-based toggles
529
530 // Only register essential category-level shortcuts
532 "Show All Dungeon Cards", {ImGuiKey_D, ImGuiMod_Ctrl, ImGuiMod_Shift},
535 "Show All Graphics Cards", {ImGuiKey_G, ImGuiMod_Ctrl, ImGuiMod_Shift},
538 "Show All Screen Cards", {ImGuiKey_S, ImGuiMod_Ctrl, ImGuiMod_Shift},
540
541#ifdef YAZE_WITH_GRPC
542 // Agent Editor shortcut
544 "Agent Editor", {ImGuiKey_A, ImGuiMod_Ctrl, ImGuiMod_Shift},
545 [this]() { agent_editor_.SetChatActive(true); });
546
547 // Agent Chat History Popup shortcut
549 "Chat History Popup", {ImGuiKey_H, ImGuiMod_Ctrl},
550 [this]() { agent_chat_history_popup_.Toggle(); });
551
552 // Agent Proposal Drawer shortcut
554 "Proposal Drawer", {ImGuiKey_P, ImGuiMod_Ctrl},
555 [this]() { proposal_drawer_.Toggle(); });
556
557 // Start the agent control server if the flag is set
558 // if (absl::GetFlag(FLAGS_agent_control)) {
559 // agent_control_server_ = std::make_unique<agent::AgentControlServer>(&emulator_);
560 // agent_control_server_->Start();
561 // }
562#endif
563
564 // Testing shortcuts (only when tests are enabled)
565#ifdef YAZE_ENABLE_TESTING
567 "Test Dashboard", {ImGuiKey_T, ImGuiMod_Ctrl},
568 [this]() { show_test_dashboard_ = true; });
569#endif
570
571 // Workspace shortcuts
573 "New Session",
574 std::vector<ImGuiKey>{ImGuiKey_N, ImGuiMod_Ctrl, ImGuiMod_Shift},
575 [this]() { CreateNewSession(); });
577 "Close Session",
578 std::vector<ImGuiKey>{ImGuiKey_W, ImGuiMod_Ctrl, ImGuiMod_Shift},
579 [this]() { CloseCurrentSession(); });
581 "Session Switcher", std::vector<ImGuiKey>{ImGuiKey_Tab, ImGuiMod_Ctrl},
582 [this]() { show_session_switcher_ = true; });
584 "Save Layout",
585 std::vector<ImGuiKey>{ImGuiKey_S, ImGuiMod_Ctrl, ImGuiMod_Shift},
586 [this]() { SaveWorkspaceLayout(); });
588 "Load Layout",
589 std::vector<ImGuiKey>{ImGuiKey_O, ImGuiMod_Ctrl, ImGuiMod_Shift},
590 [this]() { LoadWorkspaceLayout(); });
592 "Maximize Window", ImGuiKey_F11, [this]() { MaximizeCurrentWindow(); });
593}
594
596 const std::string& editor_name, const std::string& cards_str) {
597 if (editor_name.empty()) {
598 return;
599 }
600
601 LOG_INFO("EditorManager", "Processing startup flags: editor='%s', cards='%s'",
602 editor_name.c_str(), cards_str.c_str());
603
604 EditorType editor_type_to_open = EditorType::kUnknown;
605 for (int i = 0; i < static_cast<int>(EditorType::kSettings); ++i) {
606 if (GetEditorName(static_cast<EditorType>(i)) == editor_name) {
607 editor_type_to_open = static_cast<EditorType>(i);
608 break;
609 }
610 }
611
612 if (editor_type_to_open == EditorType::kUnknown) {
613 LOG_WARN("EditorManager", "Unknown editor specified via flag: %s",
614 editor_name.c_str());
615 return;
616 }
617
618 // Activate the main editor window
620 auto* editor = current_editor_set_->active_editors_[static_cast<int>(editor_type_to_open)];
621 if (editor) {
622 editor->set_active(true);
623 }
624 }
625
626 // Handle specific cards for the Dungeon Editor
627 if (editor_type_to_open == EditorType::kDungeon && !cards_str.empty()) {
628 std::stringstream ss(cards_str);
629 std::string card_name;
630 while (std::getline(ss, card_name, ',')) {
631 // Trim whitespace
632 card_name.erase(0, card_name.find_first_not_of(" \t"));
633 card_name.erase(card_name.find_last_not_of(" \t") + 1);
634
635 LOG_DEBUG("EditorManager", "Attempting to open card: '%s'",
636 card_name.c_str());
637
638 if (card_name == "Rooms List") {
640 } else if (card_name == "Room Matrix") {
642 } else if (card_name == "Entrances List") {
644 } else if (card_name == "Room Graphics") {
646 } else if (card_name == "Object Editor") {
648 } else if (card_name == "Palette Editor") {
650 } else if (absl::StartsWith(card_name, "Room ")) {
651 try {
652 int room_id = std::stoi(card_name.substr(5));
654 } catch (const std::exception& e) {
655 LOG_WARN("EditorManager", "Invalid room ID format: %s",
656 card_name.c_str());
657 }
658 } else {
659 LOG_WARN("EditorManager", "Unknown card name for Dungeon Editor: %s",
660 card_name.c_str());
661 }
662 }
663 }
664}
665
666absl::Status EditorManager::Update() {
667
668 // Update timing manager for accurate delta time across the application
669 // This fixes animation timing issues that occur when mouse isn't moving
671
672 popup_manager_->DrawPopups();
675
676 // Draw editor selection dialog
679 }
680
681 // Draw card browser
682 if (show_card_browser_) {
684 }
685
686#ifdef YAZE_WITH_GRPC
687 // Update agent editor dashboard
688 status_ = agent_editor_.Update();
689
690 // Draw chat widget separately (always visible when active)
691 if (agent_editor_.GetChatWidget()) {
692 agent_editor_.GetChatWidget()->Draw();
693 }
694#endif
695
696 // Draw background grid effects for the entire viewport
697 if (ImGui::GetCurrentContext()) {
698 ImDrawList* bg_draw_list = ImGui::GetBackgroundDrawList();
699 const ImGuiViewport* viewport = ImGui::GetMainViewport();
700
701 auto& theme_manager = gui::ThemeManager::Get();
702 auto current_theme = theme_manager.GetCurrentTheme();
703 auto& bg_renderer = gui::BackgroundRenderer::Get();
704
705 // Draw grid covering the entire main viewport
706 ImVec2 grid_pos = viewport->WorkPos;
707 ImVec2 grid_size = viewport->WorkSize;
708 bg_renderer.RenderDockingBackground(bg_draw_list, grid_pos, grid_size,
709 current_theme.primary);
710 }
711
712 // Ensure TestManager always has the current ROM
713 static Rom* last_test_rom = nullptr;
714 if (last_test_rom != current_rom_) {
715 LOG_DEBUG("EditorManager",
716 "EditorManager::Update - ROM changed, updating TestManager: %p -> "
717 "%p",
718 (void*)last_test_rom, (void*)current_rom_);
720 last_test_rom = current_rom_;
721 }
722
723 // Autosave timer
725 autosave_timer_ += ImGui::GetIO().DeltaTime;
727 autosave_timer_ = 0.0f;
729 s.backup = true;
730 s.save_new = false;
731 auto st = current_rom_->SaveToFile(s);
732 if (st.ok()) {
733 toast_manager_.Show("Autosave completed", editor::ToastType::kSuccess);
734 } else {
735 toast_manager_.Show(std::string(st.message()),
737 }
738 }
739 } else {
740 autosave_timer_ = 0.0f;
741 }
742
743 // Check if ROM is loaded before allowing editor updates
744 if (!current_editor_set_) {
745 // Show welcome screen when no session is active, but only if not manually closed
748 }
749 // Don't auto-show here, let the manual control handle it
750 return absl::OkStatus();
751 }
752
753 // Check if current ROM is valid
754 if (!current_rom_) {
755 // Only show welcome screen for truly empty state, not when ROM is loaded but current_rom_ is null
758 }
759 return absl::OkStatus();
760 }
761
762 // ROM is loaded and valid - don't auto-show welcome screen
763 // Welcome screen should only be shown manually at this point
764
765 // Iterate through ALL sessions to support multi-session docking
766 for (size_t session_idx = 0; session_idx < sessions_.size(); ++session_idx) {
767 auto& session = sessions_[session_idx];
768 if (!session.rom.is_loaded())
769 continue; // Skip sessions with invalid ROMs
770
771 for (auto editor : session.editors.active_editors_) {
772 if (*editor->active()) {
773 if (editor->type() == EditorType::kOverworld) {
774 auto& overworld_editor = static_cast<OverworldEditor&>(*editor);
775 if (overworld_editor.jump_to_tab() != -1) {
776 session.editors.dungeon_editor_.set_active(true);
777 // Set the dungeon editor to the jump to tab
778 session.editors.dungeon_editor_.add_room(
779 overworld_editor.jump_to_tab());
780 overworld_editor.jump_to_tab_ = -1;
781 }
782 }
783
784 // CARD-BASED EDITORS: Don't wrap in Begin/End, they manage own windows
785 bool is_card_based_editor = (editor->type() == EditorType::kDungeon);
786 // TODO: Add EditorType::kGraphics, EditorType::kPalette when converted
787
788 if (is_card_based_editor) {
789 // Card-based editors create their own top-level windows
790 // No parent wrapper needed - this allows independent docking
791 Rom* prev_rom = current_rom_;
792 EditorSet* prev_editor_set = current_editor_set_;
793 size_t prev_session_id = context_.session_id;
794
795 current_rom_ = &session.rom;
796 current_editor_set_ = &session.editors;
797 current_editor_ = editor;
798 context_.session_id = session_idx;
799
800 status_ = editor->Update();
801
802 // Route editor errors to toast manager
803 if (!status_.ok()) {
804 std::string editor_name = GetEditorName(editor->type());
806 absl::StrFormat("%s Error: %s", editor_name, status_.message()),
808 }
809
810 // Restore context
811 current_rom_ = prev_rom;
812 current_editor_set_ = prev_editor_set;
813 context_.session_id = prev_session_id;
814
815 } else {
816 // TRADITIONAL EDITORS: Wrap in Begin/End
817 std::string window_title =
818 GenerateUniqueEditorTitle(editor->type(), session_idx);
819
820 // Set window to maximize on first open
821 ImGui::SetNextWindowSize(ImGui::GetMainViewport()->WorkSize, ImGuiCond_FirstUseEver);
822 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->WorkPos, ImGuiCond_FirstUseEver);
823
824 if (ImGui::Begin(window_title.c_str(), editor->active(),
825 ImGuiWindowFlags_None)) { // Allow full docking
826 // Temporarily switch context for this editor's update
827 Rom* prev_rom = current_rom_;
828 EditorSet* prev_editor_set = current_editor_set_;
829 size_t prev_session_id = context_.session_id;
830
831 current_rom_ = &session.rom;
832 current_editor_set_ = &session.editors;
833 current_editor_ = editor;
834 context_.session_id = session_idx;
835
836 status_ = editor->Update();
837
838 // Route editor errors to toast manager
839 if (!status_.ok()) {
840 std::string editor_name = GetEditorName(editor->type());
842 absl::StrFormat("%s Error: %s", editor_name, status_.message()),
844 }
845
846 // Restore context
847 current_rom_ = prev_rom;
848 current_editor_set_ = prev_editor_set;
849 context_.session_id = prev_session_id;
850 }
851 ImGui::End();
852 }
853 }
854 }
855 }
856
859 }
860
861 // Always draw proposal drawer (it manages its own visibility)
863
864#ifdef YAZE_WITH_GRPC
865 // Update ROM context for agent editor
867 agent_editor_.SetRomContext(current_rom_);
868 }
869#endif
870
871 return absl::OkStatus();
872}
873
875 SameLine((GetWindowWidth() / 2) - 100);
877 SetNextItemWidth(GetWindowWidth() / 6);
878 if (BeginCombo("##ROMSelector", current_rom_->short_name().c_str())) {
879 int idx = 0;
880 for (auto it = sessions_.begin(); it != sessions_.end(); ++it, ++idx) {
881 Rom* rom = &it->rom;
882 PushID(idx);
883 bool selected = (rom == current_rom_);
884 if (Selectable(rom->short_name().c_str(), selected)) {
886 }
887 PopID();
888 }
889 EndCombo();
890 }
891 // Inline status next to ROM selector
892 SameLine();
893 Text("Size: %.1f MB", current_rom_->size() / 1048576.0f);
894
895 // Context-sensitive card control (right after ROM info)
896 SameLine();
898 } else {
899 Text("No ROM loaded");
900 }
901 return absl::OkStatus();
902}
903
906 return;
907 }
908
909 // Determine which category to show based on active editor
910 std::string category;
911
912 switch (current_editor_->type()) {
914 category = "Dungeon";
915 break;
917 category = "Graphics";
918 break;
920 category = "Screen";
921 break;
923 category = "Sprite";
924 break;
926 category = "Overworld";
927 break;
929 category = "Message";
930 break;
932 // Palette editor doesn't use cards (uses internal tabs)
933 return;
935 // Assembly editor uses dynamic file tabs
936 return;
938 category = "Emulator";
939 break;
941 // Music editor doesn't use cards yet
942 return;
943 default:
944 return; // No cards for this editor type
945 }
946
947 // Draw compact card control for the active editor's cards
948 auto& card_manager = gui::EditorCardManager::Get();
949 card_manager.DrawCompactCardControl(category);
950
951 // Show visible/total count
952 SameLine();
953 card_manager.DrawInlineCardToggles(category);
954}
955
958
959 // File Menu - enhanced with ROM features
961 .Item("Open ROM", ICON_MD_FILE_OPEN,
962 [this]() { status_ = LoadRom(); }, "Ctrl+O")
963 .Item("Save ROM", ICON_MD_SAVE,
964 [this]() { status_ = SaveRom(); }, "Ctrl+S",
965 [this]() { return current_rom_ && current_rom_->is_loaded(); })
966 .Item("Save As...", ICON_MD_SAVE_AS,
967 [this]() { popup_manager_->Show("Save As.."); },
968 nullptr,
969 [this]() { return current_rom_ && current_rom_->is_loaded(); })
970 .Separator()
971 .Item("New Project", ICON_MD_CREATE_NEW_FOLDER,
972 [this]() { status_ = CreateNewProject(); })
973 .Item("Open Project", ICON_MD_FOLDER_OPEN,
974 [this]() { status_ = OpenProject(); })
975 .Item("Save Project", ICON_MD_SAVE,
976 [this]() { status_ = SaveProject(); },
977 nullptr,
978 [this]() { return !current_project_.filepath.empty(); })
979 .Item("Save Project As...", ICON_MD_SAVE_AS,
980 [this]() { status_ = SaveProjectAs(); },
981 nullptr,
982 [this]() { return !current_project_.filepath.empty(); })
983 .Separator()
984 .Item("ROM Information", ICON_MD_INFO,
985 [this]() { popup_manager_->Show("ROM Info"); },
986 nullptr,
987 [this]() { return current_rom_ && current_rom_->is_loaded(); })
988 .Item("Create Backup", ICON_MD_BACKUP,
989 [this]() {
991 Rom::SaveSettings settings;
992 settings.backup = true;
993 settings.filename = current_rom_->filename();
994 status_ = current_rom_->SaveToFile(settings);
995 if (status_.ok()) {
996 toast_manager_.Show("Backup created successfully", ToastType::kSuccess);
997 }
998 }
999 },
1000 nullptr,
1001 [this]() { return current_rom_ && current_rom_->is_loaded(); })
1002 .Item("Validate ROM", ICON_MD_CHECK_CIRCLE,
1003 [this]() {
1005 auto result = current_project_.Validate();
1006 if (result.ok()) {
1007 toast_manager_.Show("ROM validation passed", ToastType::kSuccess);
1008 } else {
1009 toast_manager_.Show("ROM validation failed: " + std::string(result.message()),
1011 }
1012 }
1013 },
1014 nullptr,
1015 [this]() { return current_rom_ && current_rom_->is_loaded(); })
1016 .Separator()
1017 .Item("Settings", ICON_MD_SETTINGS,
1018 [this]() { current_editor_set_->settings_editor_.set_active(true); })
1019 .Separator()
1020 .Item("Quit", ICON_MD_EXIT_TO_APP,
1021 [this]() { quit_ = true; }, "Ctrl+Q")
1022 .EndMenu();
1023
1024 // Edit Menu
1025 menu_builder_.BeginMenu("Edit")
1026 .Item("Undo", ICON_MD_UNDO,
1027 [this]() { if (current_editor_) status_ = current_editor_->Undo(); }, "Ctrl+Z")
1028 .Item("Redo", ICON_MD_REDO,
1029 [this]() { if (current_editor_) status_ = current_editor_->Redo(); }, "Ctrl+Y")
1030 .Separator()
1031 .Item("Cut", ICON_MD_CONTENT_CUT,
1032 [this]() { if (current_editor_) status_ = current_editor_->Cut(); }, "Ctrl+X")
1033 .Item("Copy", ICON_MD_CONTENT_COPY,
1034 [this]() { if (current_editor_) status_ = current_editor_->Copy(); }, "Ctrl+C")
1035 .Item("Paste", ICON_MD_CONTENT_PASTE,
1036 [this]() { if (current_editor_) status_ = current_editor_->Paste(); }, "Ctrl+V")
1037 .Separator()
1038 .Item("Find", ICON_MD_SEARCH,
1039 [this]() { if (current_editor_) status_ = current_editor_->Find(); }, "Ctrl+F")
1040 .Item("Find in Files", ICON_MD_SEARCH,
1041 [this]() { show_global_search_ = true; }, "Ctrl+Shift+F")
1042 .EndMenu();
1043
1044 // View Menu - editors and cards
1045 menu_builder_.BeginMenu("View")
1046 .Item("Editor Selection", ICON_MD_DASHBOARD,
1047 [this]() { show_editor_selection_ = true; }, "Ctrl+E")
1048 .Separator()
1049 .Item("Overworld", ICON_MD_MAP,
1050 [this]() { current_editor_set_->overworld_editor_.set_active(true); }, "Ctrl+1")
1051 .Item("Dungeon", ICON_MD_CASTLE,
1052 [this]() { current_editor_set_->dungeon_editor_.set_active(true); }, "Ctrl+2")
1053 .Item("Graphics", ICON_MD_IMAGE,
1054 [this]() { current_editor_set_->graphics_editor_.set_active(true); }, "Ctrl+3")
1055 .Item("Sprites", ICON_MD_TOYS,
1056 [this]() { current_editor_set_->sprite_editor_.set_active(true); }, "Ctrl+4")
1057 .Item("Messages", ICON_MD_CHAT_BUBBLE,
1058 [this]() { current_editor_set_->message_editor_.set_active(true); }, "Ctrl+5")
1059 .Item("Music", ICON_MD_MUSIC_NOTE,
1060 [this]() { current_editor_set_->music_editor_.set_active(true); }, "Ctrl+6")
1061 .Item("Palettes", ICON_MD_PALETTE,
1062 [this]() { current_editor_set_->palette_editor_.set_active(true); }, "Ctrl+7")
1063 .Item("Screens", ICON_MD_TV,
1064 [this]() { current_editor_set_->screen_editor_.set_active(true); }, "Ctrl+8")
1065 .Item("Hex Editor", ICON_MD_DATA_ARRAY,
1066 [this]() { show_memory_editor_ = true; }, "Ctrl+0")
1067#ifdef YAZE_WITH_GRPC
1068 .Item("AI Agent", ICON_MD_SMART_TOY,
1069 [this]() { agent_editor_.set_active(true); }, "Ctrl+Shift+A")
1070 .Item("Chat History", ICON_MD_CHAT,
1071 [this]() { agent_chat_history_popup_.Toggle(); }, "Ctrl+H")
1072 .Item("Proposal Drawer", ICON_MD_PREVIEW,
1073 [this]() { proposal_drawer_.Toggle(); }, "Ctrl+P")
1074#endif
1075 .Separator();
1076
1077 // // Dynamic card menu sections (from EditorCardManager)
1078 // auto& card_manager = gui::EditorCardManager::Get();
1079 // card_manager.DrawViewMenuAll();
1080
1082 .Separator()
1083 .Item("Card Browser", ICON_MD_DASHBOARD,
1084 [this]() { show_card_browser_ = true; }, "Ctrl+Shift+B")
1085 .Separator()
1086 .Item("Welcome Screen", ICON_MD_HOME,
1087 [this]() { show_welcome_screen_ = true; })
1088 .Item("Command Palette", ICON_MD_TERMINAL,
1089 [this]() { show_command_palette_ = true; }, "Ctrl+Shift+P")
1090 .Item("Emulator", ICON_MD_GAMEPAD,
1091 [this]() { show_emulator_ = true; },
1092 nullptr, nullptr,
1093 [this]() { return show_emulator_; })
1094 .EndMenu();
1095
1096 // Window Menu - layout and session management
1097 menu_builder_.BeginMenu("Window")
1098 .BeginSubMenu("Sessions", ICON_MD_TAB)
1099 .Item("New Session", ICON_MD_ADD,
1100 [this]() { CreateNewSession(); }, "Ctrl+Shift+N")
1101 .Item("Duplicate Session", ICON_MD_CONTENT_COPY,
1102 [this]() { DuplicateCurrentSession(); },
1103 nullptr, [this]() { return current_rom_ != nullptr; })
1104 .Item("Close Session", ICON_MD_CLOSE,
1105 [this]() { CloseCurrentSession(); }, "Ctrl+Shift+W",
1106 [this]() { return sessions_.size() > 1; })
1107 .Separator()
1108 .Item("Session Switcher", ICON_MD_SWITCH_ACCOUNT,
1109 [this]() { show_session_switcher_ = true; }, "Ctrl+Tab",
1110 [this]() { return sessions_.size() > 1; })
1111 .Item("Session Manager", ICON_MD_VIEW_LIST,
1112 [this]() { show_session_manager_ = true; })
1113 .EndMenu()
1114 .Separator()
1115 .Item("Save Layout", ICON_MD_SAVE,
1116 [this]() { SaveWorkspaceLayout(); }, "Ctrl+Shift+S")
1117 .Item("Load Layout", ICON_MD_FOLDER_OPEN,
1118 [this]() { LoadWorkspaceLayout(); }, "Ctrl+Shift+O")
1119 .Item("Reset Layout", ICON_MD_RESET_TV,
1120 [this]() { ResetWorkspaceLayout(); })
1121 .Item("Layout Presets", ICON_MD_BOOKMARK,
1122 [this]() { show_layout_presets_ = true; })
1123 .Separator()
1124 .Item("Show All Windows", ICON_MD_VISIBILITY,
1125 [this]() { ShowAllWindows(); })
1126 .Item("Hide All Windows", ICON_MD_VISIBILITY_OFF,
1127 [this]() { HideAllWindows(); })
1128 .Item("Maximize Current", ICON_MD_FULLSCREEN,
1129 [this]() { MaximizeCurrentWindow(); }, "F11")
1130 .Item("Restore All", ICON_MD_FULLSCREEN_EXIT,
1131 [this]() { RestoreAllWindows(); })
1132 .Item("Close All Floating", ICON_MD_CLOSE_FULLSCREEN,
1133 [this]() { CloseAllFloatingWindows(); })
1134 .EndMenu();
1135
1136
1137#ifdef YAZE_WITH_GRPC
1138 // Collaboration Menu - combined Agent + Network features
1139 menu_builder_.BeginMenu("Collaborate")
1140 .Item("AI Agent Chat", ICON_MD_SMART_TOY,
1141 [this]() { agent_editor_.SetChatActive(true); }, "Ctrl+Shift+A",
1142 nullptr,
1143 [this]() { return agent_editor_.IsChatActive(); })
1144 .Item("Proposal Drawer", ICON_MD_RATE_REVIEW,
1146 nullptr, nullptr,
1147 [this]() { return show_proposal_drawer_; })
1148 .Separator()
1149 .Item("Host Session", ICON_MD_ADD_CIRCLE,
1150 [this]() {
1151 auto result = agent_editor_.HostSession("New Session");
1152 if (result.ok()) {
1153 toast_manager_.Show("Hosted session: " + result->session_name,
1155 } else {
1156 toast_manager_.Show("Failed to host session: " + std::string(result.status().message()),
1158 }
1159 })
1160 .Item("Join Session", ICON_MD_LOGIN,
1161 [this]() { popup_manager_->Show("Join Collaboration Session"); })
1162 .Item("Leave Session", ICON_MD_LOGOUT,
1163 [this]() {
1164 status_ = agent_editor_.LeaveSession();
1165 if (status_.ok()) {
1166 toast_manager_.Show("Left collaboration session", ToastType::kInfo);
1167 }
1168 },
1169 nullptr,
1170 [this]() { return agent_editor_.IsInSession(); })
1171 .Item("Refresh Session", ICON_MD_REFRESH,
1172 [this]() {
1173 auto result = agent_editor_.RefreshSession();
1174 if (result.ok()) {
1175 toast_manager_.Show("Session refreshed: " + std::to_string(result->participants.size()) + " participants",
1177 }
1178 },
1179 nullptr,
1180 [this]() { return agent_editor_.IsInSession(); })
1181 .Separator()
1182 .Item("Connect to Server", ICON_MD_CLOUD_UPLOAD,
1183 [this]() { popup_manager_->Show("Connect to Server"); })
1184 .Item("Disconnect from Server", ICON_MD_CLOUD_OFF,
1185 [this]() {
1186 agent_editor_.DisconnectFromServer();
1187 toast_manager_.Show("Disconnected from server", ToastType::kInfo);
1188 },
1189 nullptr,
1190 [this]() { return agent_editor_.IsConnectedToServer(); })
1191 .Separator()
1192 .Item("Capture Active Editor", ICON_MD_SCREENSHOT,
1193 [this]() {
1194 std::filesystem::path output;
1197 status_ = agent_editor_.CaptureSnapshot(&output, config);
1198 })
1199 .Item("Capture Full Window", ICON_MD_FULLSCREEN,
1200 [this]() {
1201 std::filesystem::path output;
1204 status_ = agent_editor_.CaptureSnapshot(&output, config);
1205 })
1206 .Separator()
1207 .Item("Local Mode", ICON_MD_FOLDER,
1208 [this]() { /* Set local mode */ },
1209 nullptr, nullptr,
1210 [this]() { return agent_editor_.GetCurrentMode() == AgentEditor::CollaborationMode::kLocal; })
1211 .Item("Network Mode", ICON_MD_WIFI,
1212 [this]() { /* Set network mode */ },
1213 nullptr, nullptr,
1214 [this]() { return agent_editor_.GetCurrentMode() == AgentEditor::CollaborationMode::kNetwork; })
1215 .EndMenu();
1216#endif
1217
1218 // Debug Menu - comprehensive development tools
1219 menu_builder_.BeginMenu("Debug");
1220
1221#ifdef YAZE_ENABLE_TESTING
1222 // Testing and Validation section
1224 .Item("Test Dashboard", ICON_MD_SCIENCE,
1225 [this]() { show_test_dashboard_ = true; }, "Ctrl+T")
1226 .Item("Run All Tests", ICON_MD_PLAY_ARROW,
1227 [this]() { [[maybe_unused]] auto status = test::TestManager::Get().RunAllTests(); })
1228 .Item("Run Unit Tests", ICON_MD_INTEGRATION_INSTRUCTIONS,
1229 [this]() { [[maybe_unused]] auto status = test::TestManager::Get().RunTestsByCategory(test::TestCategory::kUnit); })
1230 .Item("Run Integration Tests", ICON_MD_MEMORY,
1231 [this]() { [[maybe_unused]] auto status = test::TestManager::Get().RunTestsByCategory(test::TestCategory::kIntegration); })
1232 .Item("Run UI Tests", ICON_MD_VISIBILITY,
1233 [this]() { [[maybe_unused]] auto status = test::TestManager::Get().RunTestsByCategory(test::TestCategory::kUI); })
1234 .Item("Run Performance Tests", ICON_MD_SPEED,
1235 [this]() { [[maybe_unused]] auto status = test::TestManager::Get().RunTestsByCategory(test::TestCategory::kPerformance); })
1236 .Item("Run Memory Tests", ICON_MD_STORAGE,
1237 [this]() { [[maybe_unused]] auto status = test::TestManager::Get().RunTestsByCategory(test::TestCategory::kMemory); })
1238 .Item("Clear Test Results", ICON_MD_CLEAR_ALL,
1239 [this]() { test::TestManager::Get().ClearResults(); })
1240 .Separator();
1241#endif
1242
1243 // ROM and ASM Management
1245 .BeginSubMenu("ROM Analysis", ICON_MD_STORAGE)
1246 .Item("ROM Information", ICON_MD_INFO,
1247 [this]() { popup_manager_->Show("ROM Information"); },
1248 nullptr,
1249 [this]() { return current_rom_ && current_rom_->is_loaded(); })
1250#ifdef YAZE_ENABLE_TESTING
1251 .Item("Data Integrity Check", ICON_MD_ANALYTICS,
1252 [this]() {
1253 if (current_rom_) {
1254 [[maybe_unused]] auto status = test::TestManager::Get().RunTestSuite("RomIntegrity");
1255 toast_manager_.Show("Running ROM integrity tests...", ToastType::kInfo);
1256 }
1257 },
1258 nullptr,
1259 [this]() { return current_rom_ && current_rom_->is_loaded(); })
1260 .Item("Test Save/Load", ICON_MD_SAVE_ALT,
1261 [this]() {
1262 if (current_rom_) {
1263 [[maybe_unused]] auto status = test::TestManager::Get().RunTestSuite("RomSaveLoad");
1264 toast_manager_.Show("Running ROM save/load tests...", ToastType::kInfo);
1265 }
1266 },
1267 nullptr,
1268 [this]() { return current_rom_ && current_rom_->is_loaded(); })
1269#endif
1270 .EndMenu()
1271 .BeginSubMenu("ZSCustomOverworld", ICON_MD_CODE)
1272 .Item("Check ROM Version", ICON_MD_INFO,
1273 [this]() {
1274 if (current_rom_) {
1275 uint8_t version = (*current_rom_)[zelda3::OverworldCustomASMHasBeenApplied];
1276 std::string version_str = (version == 0xFF) ? "Vanilla" : absl::StrFormat("v%d", version);
1277 toast_manager_.Show(absl::StrFormat("ROM: %s | ZSCustomOverworld: %s",
1278 current_rom_->title().c_str(), version_str.c_str()),
1279 ToastType::kInfo, 5.0f);
1280 }
1281 },
1282 nullptr,
1283 [this]() { return current_rom_ && current_rom_->is_loaded(); })
1284 .Item("Upgrade ROM", ICON_MD_UPGRADE,
1285 [this]() {
1286 if (current_rom_) {
1287 toast_manager_.Show("Use Overworld Editor to upgrade ROM version",
1288 ToastType::kInfo, 4.0f);
1289 }
1290 },
1291 nullptr,
1292 [this]() { return current_rom_ && current_rom_->is_loaded(); })
1293 .Item("Toggle Custom Loading", ICON_MD_SETTINGS,
1294 [this]() {
1295 auto& flags = core::FeatureFlags::get();
1296 flags.overworld.kLoadCustomOverworld = !flags.overworld.kLoadCustomOverworld;
1297 toast_manager_.Show(absl::StrFormat("Custom Overworld Loading: %s",
1298 flags.overworld.kLoadCustomOverworld ? "Enabled" : "Disabled"),
1300 })
1301 .EndMenu()
1302 .BeginSubMenu("Asar Integration", ICON_MD_BUILD)
1303 .Item("Asar Status", ICON_MD_INFO,
1304 [this]() { popup_manager_->Show("Asar Integration"); })
1305 .Item("Toggle ASM Patch", ICON_MD_CODE,
1306 [this]() {
1307 if (current_rom_) {
1308 auto& flags = core::FeatureFlags::get();
1309 flags.overworld.kApplyZSCustomOverworldASM = !flags.overworld.kApplyZSCustomOverworldASM;
1310 toast_manager_.Show(absl::StrFormat("ZSCustomOverworld ASM Application: %s",
1311 flags.overworld.kApplyZSCustomOverworldASM ? "Enabled" : "Disabled"),
1313 }
1314 },
1315 nullptr,
1316 [this]() { return current_rom_ && current_rom_->is_loaded(); })
1317 .Item("Load ASM File", ICON_MD_FOLDER_OPEN,
1318 [this]() {
1319 toast_manager_.Show("ASM file loading not yet implemented",
1321 })
1322 .EndMenu()
1323 .Separator()
1324 // Development Tools
1325 .Item("Memory Editor", ICON_MD_MEMORY,
1326 [this]() { show_memory_editor_ = true; })
1327 .Item("Assembly Editor", ICON_MD_CODE,
1328 [this]() { show_asm_editor_ = true; })
1329 .Item("Feature Flags", ICON_MD_FLAG,
1330 [this]() { popup_manager_->Show("Feature Flags"); })
1331 .Separator()
1332 .Item("Performance Dashboard", ICON_MD_SPEED,
1333 [this]() { show_performance_dashboard_ = true; })
1334#ifdef YAZE_WITH_GRPC
1335 .Item("Agent Proposals", ICON_MD_PREVIEW,
1336 [this]() { proposal_drawer_.Toggle(); })
1337#endif
1338 .Separator()
1339 .Item("ImGui Demo", ICON_MD_HELP,
1340 [this]() { show_imgui_demo_ = true; },
1341 nullptr, nullptr,
1342 [this]() { return show_imgui_demo_; })
1343 .Item("ImGui Metrics", ICON_MD_ANALYTICS,
1344 [this]() { show_imgui_metrics_ = true; },
1345 nullptr, nullptr,
1346 [this]() { return show_imgui_metrics_; })
1347 .EndMenu();
1348
1349 // Help Menu
1350 menu_builder_.BeginMenu("Help")
1351 .Item("Getting Started", ICON_MD_PLAY_ARROW,
1352 [this]() { popup_manager_->Show("Getting Started"); })
1353 .Item("About", ICON_MD_INFO,
1354 [this]() { popup_manager_->Show("About"); }, "F1")
1355 .EndMenu();
1356
1358}
1359
1361 auto* current_rom = GetCurrentRom();
1362 std::string version_text = absl::StrFormat("v%s", version_.c_str());
1363 float version_width = ImGui::CalcTextSize(version_text.c_str()).x;
1364 float session_rom_area_width = 280.0f;
1365
1366 SameLine(ImGui::GetWindowWidth() - version_width - 10 - session_rom_area_width);
1367
1368 if (GetActiveSessionCount() > 1) {
1369 if (ImGui::SmallButton(absl::StrFormat("%s%zu", ICON_MD_TAB, GetActiveSessionCount()).c_str())) {
1371 }
1372 if (ImGui::IsItemHovered()) {
1373 ImGui::SetTooltip("Sessions: %zu active\nClick to switch", GetActiveSessionCount());
1374 }
1375 ImGui::SameLine();
1376 }
1377
1378 if (current_rom && current_rom->is_loaded()) {
1379 if (ImGui::SmallButton(ICON_MD_APPS)) {
1381 }
1382 if (ImGui::IsItemHovered()) {
1383 ImGui::SetTooltip(ICON_MD_DASHBOARD " Editor Selection (Ctrl+E)");
1384 }
1385 ImGui::SameLine();
1386 if (ImGui::SmallButton(ICON_MD_DISPLAY_SETTINGS)) {
1388 }
1389 if (ImGui::IsItemHovered()) {
1390 ImGui::SetTooltip(ICON_MD_TUNE " Display Settings");
1391 }
1392 ImGui::SameLine();
1393 ImGui::Separator();
1394 ImGui::SameLine();
1395 }
1396
1397 if (current_rom && current_rom->is_loaded()) {
1398 std::string rom_display = current_rom->title();
1399 if (rom_display.length() > 22) {
1400 rom_display = rom_display.substr(0, 19) + "...";
1401 }
1402 if (ImGui::SmallButton(absl::StrFormat("%s%s", rom_display.c_str(), current_rom->dirty() ? "*" : "").c_str())) {
1403 ImGui::OpenPopup("ROM Details");
1404 }
1405 } else {
1406 ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f), "No ROM");
1407 ImGui::SameLine();
1408 }
1409
1410 SameLine(ImGui::GetWindowWidth() - version_width - 10);
1411 ImGui::Text("%s", version_text.c_str());
1412}
1413
1415
1417
1418void EditorManager::ShowDisplaySettings() { if (popup_manager_) popup_manager_->Show("Display Settings"); }
1419
1421 static bool show_display_settings = false;
1422 static bool save_as_menu = false;
1423 std::string version_text = absl::StrFormat("v%s", version_.c_str());
1424 float version_width = ImGui::CalcTextSize(version_text.c_str()).x;
1425
1426 if (BeginMenuBar()) {
1428
1429 // Inline ROM selector and status
1431
1433
1434 // Version display on far right
1435 SameLine(GetWindowWidth() - version_width - 10);
1436 Text("%s", version_text.c_str());
1437 EndMenuBar();
1438 }
1439
1440 if (show_display_settings) {
1441 // Use the popup manager instead of a separate window
1442 popup_manager_->Show("Display Settings");
1443 show_display_settings = false; // Close the old-style window
1444 }
1445
1446 if (show_imgui_demo_)
1447 ShowDemoWindow(&show_imgui_demo_);
1449 ShowMetricsWindow(&show_imgui_metrics_);
1452 }
1455 }
1456
1457 // Project file editor
1463 if (!gfx::PerformanceDashboard::Get().IsVisible()) {
1465 }
1466 }
1467
1468 // Testing interface (only when tests are enabled)
1469#ifdef YAZE_ENABLE_TESTING
1471 auto& test_manager = test::TestManager::Get();
1472 test_manager.UpdateResourceStats(); // Update monitoring data
1473 test_manager.DrawTestDashboard(&show_test_dashboard_);
1474 }
1475#endif
1476
1477 // Agent proposal drawer (right side)
1480
1481 // Agent chat history popup (left side)
1483
1484 // Welcome screen (accessible from View menu)
1487 }
1488
1489 if (show_emulator_) {
1490 Begin("Emulator", &show_emulator_, ImGuiWindowFlags_MenuBar);
1492 End();
1493 }
1494
1495 // Enhanced Command Palette UI with Fuzzy Search
1497 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(),
1498 ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
1499 ImGui::SetNextWindowSize(ImVec2(800, 600), ImGuiCond_FirstUseEver);
1500
1501 if (Begin(absl::StrFormat("%s Command Palette", ICON_MD_SEARCH).c_str(),
1502 &show_command_palette_, ImGuiWindowFlags_NoCollapse)) {
1503
1504 // Search input with focus management
1505 static char query[256] = {};
1506 static int selected_idx = 0;
1507 ImGui::SetNextItemWidth(-100);
1508 if (ImGui::IsWindowAppearing()) {
1509 ImGui::SetKeyboardFocusHere();
1510 selected_idx = 0;
1511 }
1512
1513 bool input_changed = InputTextWithHint(
1514 "##cmd_query",
1515 absl::StrFormat("%s Search commands (fuzzy matching enabled)...", ICON_MD_SEARCH).c_str(),
1516 query, IM_ARRAYSIZE(query));
1517
1518 ImGui::SameLine();
1519 if (ImGui::Button(absl::StrFormat("%s Clear", ICON_MD_CLEAR).c_str())) {
1520 query[0] = '\0';
1521 input_changed = true;
1522 selected_idx = 0;
1523 }
1524
1525 Separator();
1526
1527 // Fuzzy filter commands with scoring
1528 std::vector<std::pair<int, std::pair<std::string, std::string>>> scored_commands;
1529 std::string query_lower = query;
1530 std::transform(query_lower.begin(), query_lower.end(), query_lower.begin(), ::tolower);
1531
1532 for (const auto& entry : context_.shortcut_manager.GetShortcuts()) {
1533 const auto& name = entry.first;
1534 const auto& shortcut = entry.second;
1535
1536 std::string name_lower = name;
1537 std::transform(name_lower.begin(), name_lower.end(), name_lower.begin(), ::tolower);
1538
1539 int score = 0;
1540 if (query[0] == '\0') {
1541 score = 1; // Show all when no query
1542 } else if (name_lower.find(query_lower) == 0) {
1543 score = 1000; // Starts with
1544 } else if (name_lower.find(query_lower) != std::string::npos) {
1545 score = 500; // Contains
1546 } else {
1547 // Fuzzy match - characters in order
1548 size_t text_idx = 0, query_idx = 0;
1549 while (text_idx < name_lower.length() && query_idx < query_lower.length()) {
1550 if (name_lower[text_idx] == query_lower[query_idx]) {
1551 score += 10;
1552 query_idx++;
1553 }
1554 text_idx++;
1555 }
1556 if (query_idx != query_lower.length()) score = 0;
1557 }
1558
1559 if (score > 0) {
1560 std::string shortcut_text = shortcut.keys.empty()
1561 ? "" : absl::StrFormat("(%s)", PrintShortcut(shortcut.keys).c_str());
1562 scored_commands.push_back({score, {name, shortcut_text}});
1563 }
1564 }
1565
1566 std::sort(scored_commands.begin(), scored_commands.end(),
1567 [](const auto& a, const auto& b) { return a.first > b.first; });
1568
1569 // Display results with categories
1570 if (ImGui::BeginTabBar("CommandCategories")) {
1571 if (ImGui::BeginTabItem(ICON_MD_LIST " All Commands")) {
1572 if (ImGui::BeginTable("CommandPaletteTable", 3,
1573 ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg |
1574 ImGuiTableFlags_SizingStretchProp,
1575 ImVec2(0, -30))) {
1576
1577 ImGui::TableSetupColumn("Command", ImGuiTableColumnFlags_WidthStretch, 0.5f);
1578 ImGui::TableSetupColumn("Shortcut", ImGuiTableColumnFlags_WidthStretch, 0.3f);
1579 ImGui::TableSetupColumn("Score", ImGuiTableColumnFlags_WidthStretch, 0.2f);
1580 ImGui::TableHeadersRow();
1581
1582 for (size_t i = 0; i < scored_commands.size(); ++i) {
1583 const auto& [score, cmd_pair] = scored_commands[i];
1584 const auto& [command_name, shortcut_text] = cmd_pair;
1585
1586 ImGui::TableNextRow();
1587 ImGui::TableNextColumn();
1588
1589 ImGui::PushID(static_cast<int>(i));
1590 bool is_selected = (static_cast<int>(i) == selected_idx);
1591 if (Selectable(command_name.c_str(), is_selected,
1592 ImGuiSelectableFlags_SpanAllColumns)) {
1593 selected_idx = i;
1594 const auto& shortcuts = context_.shortcut_manager.GetShortcuts();
1595 auto it = shortcuts.find(command_name);
1596 if (it != shortcuts.end() && it->second.callback) {
1597 it->second.callback();
1598 show_command_palette_ = false;
1599 }
1600 }
1601 ImGui::PopID();
1602
1603 ImGui::TableNextColumn();
1604 ImGui::TextDisabled("%s", shortcut_text.c_str());
1605
1606 ImGui::TableNextColumn();
1607 if (score > 0) ImGui::TextDisabled("%d", score);
1608 }
1609
1610 ImGui::EndTable();
1611 }
1612 ImGui::EndTabItem();
1613 }
1614
1615 if (ImGui::BeginTabItem(ICON_MD_HISTORY " Recent")) {
1616 ImGui::Text("Recent commands coming soon...");
1617 ImGui::EndTabItem();
1618 }
1619
1620 if (ImGui::BeginTabItem(ICON_MD_STAR " Frequent")) {
1621 ImGui::Text("Frequent commands coming soon...");
1622 ImGui::EndTabItem();
1623 }
1624
1625 ImGui::EndTabBar();
1626 }
1627
1628 // Status bar with tips
1629 ImGui::Separator();
1630 ImGui::Text("%s %zu commands | Score: fuzzy match", ICON_MD_INFO, scored_commands.size());
1631 ImGui::SameLine();
1632 ImGui::TextDisabled("| ↑↓=Navigate | Enter=Execute | Esc=Close");
1633 }
1634 End();
1635 }
1636
1637 // Enhanced Global Search UI
1638 if (show_global_search_) {
1639 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(),
1640 ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
1641 ImGui::SetNextWindowSize(ImVec2(800, 600), ImGuiCond_FirstUseEver);
1642
1643 if (Begin(
1644 absl::StrFormat("%s Global Search", ICON_MD_MANAGE_SEARCH).c_str(),
1645 &show_global_search_, ImGuiWindowFlags_NoCollapse)) {
1646
1647 // Enhanced search input with focus management
1648 static char query[256] = {};
1649 ImGui::SetNextItemWidth(-100);
1650 if (ImGui::IsWindowAppearing()) {
1651 ImGui::SetKeyboardFocusHere();
1652 }
1653
1654 bool input_changed = InputTextWithHint(
1655 "##global_query",
1656 absl::StrFormat("%s Search everything...", ICON_MD_SEARCH).c_str(),
1657 query, IM_ARRAYSIZE(query));
1658
1659 ImGui::SameLine();
1660 if (ImGui::Button(absl::StrFormat("%s Clear", ICON_MD_CLEAR).c_str())) {
1661 query[0] = '\0';
1662 input_changed = true;
1663 }
1664
1665 Separator();
1666
1667 // Tabbed search results for better organization
1668 if (ImGui::BeginTabBar("SearchResultTabs")) {
1669
1670 // Recent Files Tab
1671 if (ImGui::BeginTabItem(
1672 absl::StrFormat("%s Recent Files", ICON_MD_HISTORY).c_str())) {
1673 auto& manager = core::RecentFilesManager::GetInstance();
1674 auto recent_files = manager.GetRecentFiles();
1675
1676 if (ImGui::BeginTable("RecentFilesTable", 3,
1677 ImGuiTableFlags_ScrollY |
1678 ImGuiTableFlags_RowBg |
1679 ImGuiTableFlags_SizingStretchProp)) {
1680
1681 ImGui::TableSetupColumn("File", ImGuiTableColumnFlags_WidthStretch,
1682 0.6f);
1683 ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed,
1684 80.0f);
1685 ImGui::TableSetupColumn("Action", ImGuiTableColumnFlags_WidthFixed,
1686 100.0f);
1687 ImGui::TableHeadersRow();
1688
1689 for (const auto& file : recent_files) {
1690 if (query[0] != '\0' && file.find(query) == std::string::npos)
1691 continue;
1692
1693 ImGui::TableNextRow();
1694 ImGui::TableNextColumn();
1695 ImGui::Text("%s", util::GetFileName(file).c_str());
1696
1697 ImGui::TableNextColumn();
1698 std::string ext = util::GetFileExtension(file);
1699 if (ext == "sfc" || ext == "smc") {
1700 ImGui::TextColored(ImVec4(0.2f, 0.8f, 0.2f, 1.0f), "%s ROM",
1702 } else if (ext == "yaze") {
1703 ImGui::TextColored(ImVec4(0.2f, 0.6f, 0.8f, 1.0f), "%s Project",
1705 } else {
1706 ImGui::Text("%s File", ICON_MD_DESCRIPTION);
1707 }
1708
1709 ImGui::TableNextColumn();
1710 ImGui::PushID(file.c_str());
1711 if (ImGui::Button("Open")) {
1712 status_ = OpenRomOrProject(file);
1713 show_global_search_ = false;
1714 }
1715 ImGui::PopID();
1716 }
1717
1718 ImGui::EndTable();
1719 }
1720 ImGui::EndTabItem();
1721 }
1722
1723 // Labels Tab (only if ROM is loaded)
1725 if (ImGui::BeginTabItem(
1726 absl::StrFormat("%s Labels", ICON_MD_LABEL).c_str())) {
1727 auto& labels = current_rom_->resource_label()->labels_;
1728
1729 if (ImGui::BeginTable("LabelsTable", 3,
1730 ImGuiTableFlags_ScrollY |
1731 ImGuiTableFlags_RowBg |
1732 ImGuiTableFlags_SizingStretchProp)) {
1733
1734 ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed,
1735 100.0f);
1736 ImGui::TableSetupColumn("Label",
1737 ImGuiTableColumnFlags_WidthStretch, 0.4f);
1738 ImGui::TableSetupColumn("Value",
1739 ImGuiTableColumnFlags_WidthStretch, 0.6f);
1740 ImGui::TableHeadersRow();
1741
1742 for (const auto& type_pair : labels) {
1743 for (const auto& kv : type_pair.second) {
1744 if (query[0] != '\0' &&
1745 kv.first.find(query) == std::string::npos &&
1746 kv.second.find(query) == std::string::npos)
1747 continue;
1748
1749 ImGui::TableNextRow();
1750 ImGui::TableNextColumn();
1751 ImGui::Text("%s", type_pair.first.c_str());
1752
1753 ImGui::TableNextColumn();
1754 if (Selectable(kv.first.c_str(), false,
1755 ImGuiSelectableFlags_SpanAllColumns)) {
1756 // Future: navigate to related editor/location
1757 }
1758
1759 ImGui::TableNextColumn();
1760 ImGui::TextDisabled("%s", kv.second.c_str());
1761 }
1762 }
1763
1764 ImGui::EndTable();
1765 }
1766 ImGui::EndTabItem();
1767 }
1768 }
1769
1770 // Sessions Tab
1771 if (GetActiveSessionCount() > 1) {
1772 if (ImGui::BeginTabItem(
1773 absl::StrFormat("%s Sessions", ICON_MD_TAB).c_str())) {
1774 ImGui::Text("Search and switch between active sessions:");
1775
1776 for (size_t i = 0; i < sessions_.size(); ++i) {
1777 auto& session = sessions_[i];
1778 if (session.custom_name == "[CLOSED SESSION]")
1779 continue;
1780
1781 std::string session_info = session.GetDisplayName();
1782 if (query[0] != '\0' &&
1783 session_info.find(query) == std::string::npos)
1784 continue;
1785
1786 bool is_current = (&session.rom == current_rom_);
1787 if (is_current) {
1788 ImGui::PushStyleColor(ImGuiCol_Text,
1789 ImVec4(0.2f, 0.8f, 0.2f, 1.0f));
1790 }
1791
1792 if (Selectable(absl::StrFormat("%s %s %s", ICON_MD_TAB,
1793 session_info.c_str(),
1794 is_current ? "(Current)" : "")
1795 .c_str())) {
1796 if (!is_current) {
1797 SwitchToSession(i);
1798 show_global_search_ = false;
1799 }
1800 }
1801
1802 if (is_current) {
1803 ImGui::PopStyleColor();
1804 }
1805 }
1806 ImGui::EndTabItem();
1807 }
1808 }
1809
1810 ImGui::EndTabBar();
1811 }
1812
1813 // Status bar
1814 ImGui::Separator();
1815 ImGui::Text("%s Global search across all YAZE data", ICON_MD_INFO);
1816 }
1817 End();
1818 }
1819
1821 Begin("Palette Editor", &show_palette_editor_);
1823
1824 // Route palette editor errors to toast manager
1825 if (!status_.ok()) {
1827 absl::StrFormat("Palette Editor Error: %s", status_.message()),
1829 }
1830
1831 End();
1832 }
1833
1840 }
1841 }
1842
1843 if (save_as_menu) {
1844 Begin("Save ROM As", &save_as_menu, ImGuiWindowFlags_AlwaysAutoResize);
1845
1846 ImGui::Text("%s Save ROM to new location", ICON_MD_SAVE_AS);
1847 ImGui::Separator();
1848
1849 static std::string save_as_filename = "";
1850 if (current_rom_ && save_as_filename.empty()) {
1851 save_as_filename = current_rom_->title();
1852 }
1853
1854 ImGui::InputText("Filename", &save_as_filename);
1855
1856 ImGui::Separator();
1857
1858 if (Button(absl::StrFormat("%s Browse...", ICON_MD_FOLDER_OPEN).c_str(),
1860 // Use save file dialog for ROM files
1861 auto file_path =
1862 util::FileDialogWrapper::ShowSaveFileDialog(save_as_filename, "sfc");
1863 if (!file_path.empty()) {
1864 save_as_filename = file_path;
1865 }
1866 }
1867
1868 ImGui::SameLine();
1869 if (Button(absl::StrFormat("%s Save", ICON_MD_SAVE).c_str(),
1871 if (!save_as_filename.empty()) {
1872 // Ensure proper file extension
1873 std::string final_filename = save_as_filename;
1874 if (final_filename.find(".sfc") == std::string::npos &&
1875 final_filename.find(".smc") == std::string::npos) {
1876 final_filename += ".sfc";
1877 }
1878
1879 status_ = SaveRomAs(final_filename);
1880 if (status_.ok()) {
1881 save_as_menu = false;
1883 absl::StrFormat("ROM saved as: %s", final_filename),
1885 } else {
1887 absl::StrFormat("Failed to save ROM: %s", status_.message()),
1889 }
1890 }
1891 }
1892
1893 ImGui::SameLine();
1894 if (Button(absl::StrFormat("%s Cancel", ICON_MD_CANCEL).c_str(),
1896 save_as_menu = false;
1897 }
1898 End();
1899 }
1900
1901 if (new_project_menu) {
1902 Begin("New Project", &new_project_menu, ImGuiWindowFlags_AlwaysAutoResize);
1903 static std::string save_as_filename = "";
1904 InputText("Project Name", &save_as_filename);
1905 if (Button(absl::StrFormat("%s Destination Folder", ICON_MD_FOLDER).c_str(),
1908 }
1909 SameLine();
1910 Text("%s", current_project_.filepath.c_str());
1911
1912 if (Button(absl::StrFormat("%s ROM File", ICON_MD_VIDEOGAME_ASSET).c_str(),
1915 }
1916 SameLine();
1917 Text("%s", current_project_.rom_filename.c_str());
1918
1919 if (Button(absl::StrFormat("%s Labels File", ICON_MD_LABEL).c_str(),
1923 }
1924 SameLine();
1925 Text("%s", current_project_.labels_filename.c_str());
1926
1927 if (Button(absl::StrFormat("%s Code Folder", ICON_MD_CODE).c_str(),
1930 }
1931 SameLine();
1932 Text("%s", current_project_.code_folder.c_str());
1933
1934 Separator();
1935
1936 if (Button(absl::StrFormat("%s Choose Project File Location", ICON_MD_SAVE)
1937 .c_str(),
1939 auto project_file_path =
1940 util::FileDialogWrapper::ShowSaveFileDialog(save_as_filename, "yaze");
1941 if (!project_file_path.empty()) {
1942 // Ensure .yaze extension
1943 if (project_file_path.find(".yaze") == std::string::npos) {
1944 project_file_path += ".yaze";
1945 }
1946
1947 // Update project filepath to the chosen location
1948 current_project_.filepath = project_file_path;
1949
1950 // Also set the project directory to the parent directory
1951 size_t last_slash = project_file_path.find_last_of("/\\");
1952 if (last_slash != std::string::npos) {
1953 std::string project_dir = project_file_path.substr(0, last_slash);
1954 Text("Project will be saved to: %s", project_dir.c_str());
1955 }
1956 }
1957 }
1958
1959 if (Button(absl::StrFormat("%s Create Project", ICON_MD_ADD).c_str(),
1961 if (!current_project_.filepath.empty()) {
1962 new_project_menu = false;
1963 status_ = current_project_.Create(save_as_filename,
1965 if (status_.ok()) {
1967 }
1968 } else {
1969 toast_manager_.Show("Please choose a project file location first",
1971 }
1972 }
1973 SameLine();
1974 if (Button("Cancel", gui::kDefaultModalSize)) {
1975 new_project_menu = false;
1976 }
1977 End();
1978 }
1979
1980 // Workspace preset dialogs
1982 Begin("Save Workspace Preset", &show_save_workspace_preset_,
1983 ImGuiWindowFlags_AlwaysAutoResize);
1984 static std::string preset_name = "";
1985 InputText("Name", &preset_name);
1986 if (Button("Save", gui::kDefaultModalSize)) {
1987 SaveWorkspacePreset(preset_name);
1990 }
1991 SameLine();
1992 if (Button("Cancel", gui::kDefaultModalSize)) {
1994 }
1995 End();
1996 }
1997
1999 Begin("Load Workspace Preset", &show_load_workspace_preset_,
2000 ImGuiWindowFlags_AlwaysAutoResize);
2001
2002 // Lazy load workspace presets when UI is accessed
2005 }
2006
2007 for (const auto& name : workspace_manager_.workspace_presets()) {
2008 if (Selectable(name.c_str())) {
2009 LoadWorkspacePreset(name);
2012 }
2013 }
2015 Text("No presets found");
2016 End();
2017 }
2018
2019 // Draw new workspace UI elements
2024}
2025
2027 auto file_name = FileDialogWrapper::ShowOpenFileDialog();
2028 if (file_name.empty()) {
2029 return absl::OkStatus();
2030 }
2031
2032 // Check for duplicate sessions
2033 if (HasDuplicateSession(file_name)) {
2034 toast_manager_.Show("ROM already open in another session",
2036 return absl::OkStatus();
2037 }
2038
2039 Rom temp_rom;
2040 RETURN_IF_ERROR(temp_rom.LoadFromFile(file_name));
2041
2042 // Check if there's an empty session we can populate instead of creating new one
2043 RomSession* target_session = nullptr;
2044 for (auto& session : sessions_) {
2045 if (!session.rom.is_loaded()) {
2046 target_session = &session;
2047 LOG_DEBUG("EditorManager", "Found empty session to populate with ROM: %s",
2048 file_name.c_str());
2049 break;
2050 }
2051 }
2052
2053 if (target_session) {
2054 // Populate existing empty session
2055 target_session->rom = std::move(temp_rom);
2056 target_session->filepath = file_name;
2057 current_rom_ = &target_session->rom;
2058 current_editor_set_ = &target_session->editors;
2059 } else {
2060 // Create new session only if no empty ones exist
2061 sessions_.emplace_back(std::move(temp_rom), &user_settings_);
2062 RomSession& session = sessions_.back();
2063 session.filepath = file_name; // Store filepath for duplicate detection
2064
2065 // Wire editor contexts
2066 for (auto* editor : session.editors.active_editors_) {
2067 editor->set_context(&context_);
2068 }
2069 current_rom_ = &session.rom;
2070 current_editor_set_ = &session.editors;
2071 }
2072
2073 // Update test manager with current ROM for ROM-dependent tests (only when tests are enabled)
2074#ifdef YAZE_ENABLE_TESTING
2075 LOG_DEBUG("EditorManager", "Setting ROM in TestManager - %p ('%s')",
2076 (void*)current_rom_,
2077 current_rom_ ? current_rom_->title().c_str() : "null");
2079#endif
2080
2081 auto& manager = core::RecentFilesManager::GetInstance();
2082 manager.AddFile(file_name);
2083 manager.Save();
2085
2086 // Hide welcome screen when ROM is successfully loaded - don't reset manual close state
2087 show_welcome_screen_ = false;
2088
2089 // Clear recent editors for fresh start with new ROM and show editor selection dialog
2092
2093 return absl::OkStatus();
2094}
2095
2098 return absl::FailedPreconditionError("No ROM or editor set loaded");
2099 }
2100
2101 auto start_time = std::chrono::steady_clock::now();
2102
2103 // Set renderer for emulator (lazy initialization happens in Run())
2104 if (renderer_) {
2106 }
2107
2110 // Initialize the dungeon editor with the renderer
2112 ASSIGN_OR_RETURN(*gfx::Arena::Get().mutable_gfx_sheets(),
2122
2124
2125 auto end_time = std::chrono::steady_clock::now();
2126 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
2127 end_time - start_time);
2128 LOG_DEBUG("EditorManager", "ROM assets loaded in %lld ms", duration.count());
2129
2130 return absl::OkStatus();
2131}
2132
2135 return absl::FailedPreconditionError("No ROM or editor set loaded");
2136 }
2137
2138 if (core::FeatureFlags::get().kSaveDungeonMaps) {
2141 }
2142
2144
2145 if (core::FeatureFlags::get().kSaveGraphicsSheet)
2148
2149 Rom::SaveSettings settings;
2152 return current_rom_->SaveToFile(settings);
2153}
2154
2155absl::Status EditorManager::SaveRomAs(const std::string& filename) {
2157 return absl::FailedPreconditionError("No ROM or editor set loaded");
2158 }
2159
2160 if (filename.empty()) {
2161 return absl::InvalidArgumentError("Filename cannot be empty");
2162 }
2163
2164 // Save editor data first (same as SaveRom)
2165 if (core::FeatureFlags::get().kSaveDungeonMaps) {
2168 }
2169
2171
2172 if (core::FeatureFlags::get().kSaveGraphicsSheet)
2175
2176 // Create save settings with custom filename
2177 Rom::SaveSettings settings;
2179 settings.save_new = false; // Don't auto-generate name, use provided filename
2180 settings.filename = filename;
2181
2182 auto save_status = current_rom_->SaveToFile(settings);
2183 if (save_status.ok()) {
2184 // Update current ROM filepath to the new location
2185 size_t current_session_idx = GetCurrentSessionIndex();
2186 if (current_session_idx < sessions_.size()) {
2187 sessions_[current_session_idx].filepath = filename;
2188 }
2189
2190 // Add to recent files
2191 auto& manager = core::RecentFilesManager::GetInstance();
2192 manager.AddFile(filename);
2193 manager.Save();
2194
2195 toast_manager_.Show(absl::StrFormat("ROM saved as: %s", filename),
2197 }
2198
2199 return save_status;
2200}
2201
2202absl::Status EditorManager::OpenRomOrProject(const std::string& filename) {
2203 if (filename.empty()) {
2204 return absl::OkStatus();
2205 }
2206 if (absl::StrContains(filename, ".yaze")) {
2209 } else {
2210 Rom temp_rom;
2211 RETURN_IF_ERROR(temp_rom.LoadFromFile(filename));
2212 sessions_.emplace_back(std::move(temp_rom), &user_settings_);
2213 RomSession& session = sessions_.back();
2214 for (auto* editor : session.editors.active_editors_) {
2215 editor->set_context(&context_);
2216 }
2217 current_rom_ = &session.rom;
2218 current_editor_set_ = &session.editors;
2220
2221 // Hide welcome screen and show editor selection when ROM is loaded
2222 show_welcome_screen_ = false;
2225 }
2226 return absl::OkStatus();
2227}
2228
2229absl::Status EditorManager::CreateNewProject(const std::string& template_name) {
2231 if (dialog_path.empty()) {
2232 return absl::OkStatus(); // User cancelled
2233 }
2234
2235 // Show project creation dialog
2236 popup_manager_->Show("Create New Project");
2237 return absl::OkStatus();
2238}
2239
2242 if (file_path.empty()) {
2243 return absl::OkStatus();
2244 }
2245
2246 core::YazeProject new_project;
2247 RETURN_IF_ERROR(new_project.Open(file_path));
2248
2249 // Validate project
2250 auto validation_status = new_project.Validate();
2251 if (!validation_status.ok()) {
2252 toast_manager_.Show(absl::StrFormat("Project validation failed: %s",
2253 validation_status.message()),
2255
2256 // Ask user if they want to repair
2257 popup_manager_->Show("Project Repair");
2258 }
2259
2260 current_project_ = std::move(new_project);
2261
2262 // Load ROM if specified in project
2263 if (!current_project_.rom_filename.empty()) {
2264 Rom temp_rom;
2266
2267 if (!current_project_.labels_filename.empty()) {
2268 if (!temp_rom.resource_label()->LoadLabels(
2270 toast_manager_.Show("Could not load labels file from project",
2272 }
2273 }
2274
2275 sessions_.emplace_back(std::move(temp_rom), &user_settings_);
2276 RomSession& session = sessions_.back();
2277 for (auto* editor : session.editors.active_editors_) {
2278 editor->set_context(&context_);
2279 }
2280 current_rom_ = &session.rom;
2281 current_editor_set_ = &session.editors;
2282
2283 // Apply project feature flags to the session
2285
2286 // Update test manager with current ROM for ROM-dependent tests (only when tests are enabled)
2287#ifdef YAZE_ENABLE_TESTING
2288 LOG_DEBUG("EditorManager", "Setting ROM in TestManager - %p ('%s')",
2289 (void*)current_rom_,
2290 current_rom_ ? current_rom_->title().c_str() : "null");
2292#endif
2293
2297 }
2298
2300
2301 // Hide welcome screen and show editor selection when project ROM is loaded
2302 show_welcome_screen_ = false;
2305 }
2306
2307 // Apply workspace settings
2312 ImGui::GetIO().FontGlobalScale = user_settings_.prefs().font_global_scale;
2313
2314 // Add to recent files
2315 auto& manager = core::RecentFilesManager::GetInstance();
2316 manager.AddFile(current_project_.filepath);
2317 manager.Save();
2318
2319 toast_manager_.Show(absl::StrFormat("Project '%s' loaded successfully",
2322
2323 return absl::OkStatus();
2324}
2325
2328 return CreateNewProject();
2329 }
2330
2331 // Update project with current settings
2333 size_t session_idx = GetCurrentSessionIndex();
2334 if (session_idx < sessions_.size()) {
2335 current_project_.feature_flags = sessions_[session_idx].feature_flags;
2336 }
2337
2342
2343 // Save recent files
2344 auto& manager = core::RecentFilesManager::GetInstance();
2346 for (const auto& file : manager.GetRecentFiles()) {
2348 }
2349 }
2350
2351 return current_project_.Save();
2352}
2353
2355 // Get current project name for default filename
2356 std::string default_name = current_project_.project_opened()
2358 : "untitled_project";
2359
2360 auto file_path =
2361 util::FileDialogWrapper::ShowSaveFileDialog(default_name, "yaze");
2362 if (file_path.empty()) {
2363 return absl::OkStatus();
2364 }
2365
2366 // Ensure .yaze extension
2367 if (file_path.find(".yaze") == std::string::npos) {
2368 file_path += ".yaze";
2369 }
2370
2371 // Update project filepath and save
2372 std::string old_filepath = current_project_.filepath;
2373 current_project_.filepath = file_path;
2374
2375 auto save_status = current_project_.Save();
2376 if (save_status.ok()) {
2377 // Add to recent files
2378 auto& manager = core::RecentFilesManager::GetInstance();
2379 manager.AddFile(file_path);
2380 manager.Save();
2381
2382 toast_manager_.Show(absl::StrFormat("Project saved as: %s", file_path),
2384 } else {
2385 // Restore old filepath on failure
2386 current_project_.filepath = old_filepath;
2388 absl::StrFormat("Failed to save project: %s", save_status.message()),
2390 }
2391
2392 return save_status;
2393}
2394
2395absl::Status EditorManager::ImportProject(const std::string& project_path) {
2396 core::YazeProject imported_project;
2397
2398 if (project_path.ends_with(".zsproj")) {
2399 RETURN_IF_ERROR(imported_project.ImportZScreamProject(project_path));
2401 "ZScream project imported successfully. Please configure ROM and "
2402 "folders.",
2404 } else {
2405 RETURN_IF_ERROR(imported_project.Open(project_path));
2406 }
2407
2408 current_project_ = std::move(imported_project);
2409 return absl::OkStatus();
2410}
2411
2414 return absl::FailedPreconditionError("No project is currently open");
2415 }
2416
2418 toast_manager_.Show("Project repaired successfully",
2420
2421 return absl::OkStatus();
2422}
2423
2425 popup_manager_->Show("Project Help");
2426}
2427
2429 if (!rom) {
2430 return absl::InvalidArgumentError("Invalid ROM pointer");
2431 }
2432
2433 for (auto& session : sessions_) {
2434 if (&session.rom == rom) {
2435 current_rom_ = &session.rom;
2436 current_editor_set_ = &session.editors;
2437
2438 // Update test manager with current ROM for ROM-dependent tests
2440
2441 return absl::OkStatus();
2442 }
2443 }
2444 // If ROM wasn't found in existing sessions, treat as new session.
2445 // Copying an external ROM object is avoided; instead, fail.
2446 return absl::NotFoundError("ROM not found in existing sessions");
2447}
2448
2450 // Check session limit
2451 if (sessions_.size() >= 8) {
2452 popup_manager_->Show("Session Limit Warning");
2453 return;
2454 }
2455
2456 // Create a blank session
2457 sessions_.emplace_back();
2458 RomSession& session = sessions_.back();
2459
2460 // Set user settings for the blank session
2462
2463 // Wire editor contexts for new session
2464 for (auto* editor : session.editors.active_editors_) {
2465 editor->set_context(&context_);
2466 }
2467
2468 // Don't switch to the new session automatically
2470 absl::StrFormat("New session created (Session %zu)", sessions_.size()),
2472
2473 // Show session manager if user has multiple sessions now
2474 if (sessions_.size() > 2) {
2476 "Tip: Use Workspace → Sessions → Session Switcher for quick navigation",
2478 }
2479}
2480
2482 if (!current_rom_) {
2483 toast_manager_.Show("No current ROM to duplicate",
2485 return;
2486 }
2487
2488 // Create a copy of the current ROM
2489 Rom rom_copy = *current_rom_;
2490 sessions_.emplace_back(std::move(rom_copy), &user_settings_);
2491 RomSession& session = sessions_.back();
2492
2493 // Wire editor contexts
2494 for (auto* editor : session.editors.active_editors_) {
2495 editor->set_context(&context_);
2496 }
2497
2499 absl::StrFormat("Session duplicated (Session %zu)", sessions_.size()),
2501}
2502
2504 if (GetActiveSessionCount() <= 1) {
2505 toast_manager_.Show("Cannot close the last active session",
2507 return;
2508 }
2509
2510 // Find current session index
2511 size_t current_index = GetCurrentSessionIndex();
2512
2513 // Switch to another active session before removing current one
2514 size_t next_index = 0;
2515 for (size_t i = 0; i < sessions_.size(); ++i) {
2516 if (i != current_index && sessions_[i].custom_name != "[CLOSED SESSION]") {
2517 next_index = i;
2518 break;
2519 }
2520 }
2521
2522 current_rom_ = &sessions_[next_index].rom;
2523 current_editor_set_ = &sessions_[next_index].editors;
2525
2526 // Now remove the current session
2527 RemoveSession(current_index);
2528
2529 toast_manager_.Show("Session closed successfully",
2531}
2532
2534 if (index >= sessions_.size()) {
2535 toast_manager_.Show("Invalid session index for removal",
2537 return;
2538 }
2539
2540 if (GetActiveSessionCount() <= 1) {
2541 toast_manager_.Show("Cannot remove the last active session",
2543 return;
2544 }
2545
2546 // Get session info for logging
2547 std::string session_name = sessions_[index].GetDisplayName();
2548
2549 // For now, mark the session as invalid instead of removing it from the deque
2550 // This is a safer approach until RomSession becomes fully movable
2551 sessions_[index].rom.Close(); // Close the ROM to mark as invalid
2552 sessions_[index].custom_name = "[CLOSED SESSION]";
2553 sessions_[index].filepath = "";
2554
2555 LOG_DEBUG("EditorManager", "Marked session as closed: %s (index %zu)",
2556 session_name.c_str(), index);
2558 absl::StrFormat("Session marked as closed: %s", session_name),
2560
2561 // TODO: Implement proper session removal when EditorSet becomes movable
2562 // The current workaround marks sessions as closed instead of removing them
2563}
2564
2566 if (index >= sessions_.size()) {
2567 toast_manager_.Show("Invalid session index", editor::ToastType::kError);
2568 return;
2569 }
2570
2571 auto& session = sessions_[index];
2572 current_rom_ = &session.rom;
2573 current_editor_set_ = &session.editors;
2574
2575 // Update test manager with current ROM for ROM-dependent tests
2576 util::logf("EditorManager: Setting ROM in TestManager - %p ('%s')",
2577 (void*)current_rom_,
2578 current_rom_ ? current_rom_->title().c_str() : "null");
2580
2581 std::string session_name = session.GetDisplayName();
2582 toast_manager_.Show(absl::StrFormat("Switched to %s", session_name),
2584}
2585
2587 for (size_t i = 0; i < sessions_.size(); ++i) {
2588 if (&sessions_[i].rom == current_rom_ &&
2589 sessions_[i].custom_name != "[CLOSED SESSION]") {
2590 return i;
2591 }
2592 }
2593 return 0; // Default to first session if not found
2594}
2595
2597 size_t count = 0;
2598 for (const auto& session : sessions_) {
2599 if (session.custom_name != "[CLOSED SESSION]") {
2600 count++;
2601 }
2602 }
2603 return count;
2604}
2605
2607 EditorType type, size_t session_index) const {
2608 const char* base_name = kEditorNames[static_cast<int>(type)];
2609
2610 if (sessions_.size() <= 1) {
2611 // Single session - use simple name
2612 return std::string(base_name);
2613 }
2614
2615 // Multi-session - include session identifier
2616 const auto& session = sessions_[session_index];
2617 std::string session_name = session.GetDisplayName();
2618
2619 // Truncate long session names
2620 if (session_name.length() > 20) {
2621 session_name = session_name.substr(0, 17) + "...";
2622 }
2623
2624 return absl::StrFormat("%s - %s##session_%zu", base_name, session_name,
2625 session_index);
2626}
2627
2629 // Show confirmation popup first
2630 popup_manager_->Show("Layout Reset Confirm");
2631}
2632
2634 ImGui::SaveIniSettingsToDisk("yaze_workspace.ini");
2635 toast_manager_.Show("Workspace layout saved", editor::ToastType::kSuccess);
2636}
2637
2639 ImGui::LoadIniSettingsFromDisk("yaze_workspace.ini");
2640 toast_manager_.Show("Workspace layout loaded", editor::ToastType::kSuccess);
2641}
2642
2645 return;
2646
2647 for (auto* editor : current_editor_set_->active_editors_) {
2648 editor->set_active(true);
2649 }
2650 show_imgui_demo_ = true;
2651 show_imgui_metrics_ = true;
2653#ifdef YAZE_ENABLE_TESTING
2654 show_test_dashboard_ = true;
2655#endif
2656
2657 toast_manager_.Show("All windows shown", editor::ToastType::kInfo);
2658}
2659
2662 return;
2663
2664 for (auto* editor : current_editor_set_->active_editors_) {
2665 editor->set_active(false);
2666 }
2667 show_imgui_demo_ = false;
2668 show_imgui_metrics_ = false;
2670#ifdef YAZE_ENABLE_TESTING
2671 show_test_dashboard_ = false;
2672#endif
2673
2674 toast_manager_.Show("All windows hidden", editor::ToastType::kInfo);
2675}
2676
2678 // This would maximize the current focused window
2679 // Implementation depends on ImGui internal window management
2680 toast_manager_.Show("Current window maximized", editor::ToastType::kInfo);
2681}
2682
2684 // Restore all windows to normal size
2685 toast_manager_.Show("All windows restored", editor::ToastType::kInfo);
2686}
2687
2689 // Close all floating (undocked) windows
2690 toast_manager_.Show("All floating windows closed", editor::ToastType::kInfo);
2691}
2692
2695 return;
2696
2697 // Developer layout: Code editor, assembly editor, test dashboard
2699#ifdef YAZE_ENABLE_TESTING
2700 show_test_dashboard_ = true;
2701#endif
2702 show_imgui_metrics_ = true;
2703
2704 // Hide non-dev windows
2708
2709 toast_manager_.Show("Developer layout loaded", editor::ToastType::kSuccess);
2710}
2711
2714 return;
2715
2716 // Designer layout: Graphics, palette, sprite editors
2721
2722 // Hide non-design windows
2724#ifdef YAZE_ENABLE_TESTING
2725 show_test_dashboard_ = false;
2726#endif
2727 show_imgui_metrics_ = false;
2728
2729 toast_manager_.Show("Designer layout loaded", editor::ToastType::kSuccess);
2730}
2731
2734 return;
2735
2736 // Modder layout: All editors except technical ones
2744
2745 // Hide technical windows
2747 show_imgui_metrics_ = false;
2748
2749 toast_manager_.Show("Modder layout loaded", editor::ToastType::kSuccess);
2750}
2751
2754 return;
2755
2756 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(),
2757 ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
2758 ImGui::SetNextWindowSize(ImVec2(700, 450), ImGuiCond_Appearing);
2759
2760 if (ImGui::Begin(
2761 absl::StrFormat("%s Session Switcher", ICON_MD_SWITCH_ACCOUNT)
2762 .c_str(),
2763 &show_session_switcher_, ImGuiWindowFlags_NoCollapse)) {
2764
2765 // Header with enhanced info
2766 ImGui::Text("%s %zu Sessions Available", ICON_MD_TAB, sessions_.size());
2767 ImGui::SameLine(ImGui::GetWindowWidth() - 120);
2768 if (ImGui::Button(absl::StrFormat("%s New", ICON_MD_ADD).c_str(),
2769 ImVec2(50, 0))) {
2771 }
2772 ImGui::SameLine();
2773 if (ImGui::Button(absl::StrFormat("%s Manager", ICON_MD_SETTINGS).c_str(),
2774 ImVec2(60, 0))) {
2775 show_session_manager_ = true;
2776 }
2777
2778 ImGui::Separator();
2779
2780 // Enhanced session list using table for better layout
2781 const float TABLE_HEIGHT = ImGui::GetContentRegionAvail().y -
2782 50; // Reserve space for close button
2783
2784 if (ImGui::BeginTable("SessionSwitcherTable", 4,
2785 ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg |
2786 ImGuiTableFlags_SizingStretchProp |
2787 ImGuiTableFlags_ScrollY |
2788 ImGuiTableFlags_Resizable,
2789 ImVec2(0, TABLE_HEIGHT))) {
2790
2791 // Setup columns with proper sizing weights
2792 ImGui::TableSetupColumn("Session", ImGuiTableColumnFlags_WidthStretch,
2793 0.3f);
2794 ImGui::TableSetupColumn("ROM Info", ImGuiTableColumnFlags_WidthStretch,
2795 0.4f);
2796 ImGui::TableSetupColumn("Status", ImGuiTableColumnFlags_WidthFixed,
2797 90.0f);
2798 ImGui::TableSetupColumn("Actions", ImGuiTableColumnFlags_WidthFixed,
2799 140.0f);
2800 ImGui::TableHeadersRow();
2801
2802 for (size_t i = 0; i < sessions_.size(); ++i) {
2803 auto& session = sessions_[i];
2804
2805 // Skip closed sessions
2806 if (session.custom_name == "[CLOSED SESSION]") {
2807 continue;
2808 }
2809
2810 bool is_current = (&session.rom == current_rom_);
2811
2812 ImGui::PushID(static_cast<int>(i));
2813 ImGui::TableNextRow(ImGuiTableRowFlags_None,
2814 55.0f); // Consistent row height
2815
2816 // Session name column with better styling
2817 ImGui::TableNextColumn();
2818 ImGui::AlignTextToFramePadding();
2819
2820 if (is_current) {
2821 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.2f, 0.8f, 0.2f, 1.0f));
2822 ImGui::Text("%s %s", ICON_MD_STAR, session.GetDisplayName().c_str());
2823 ImGui::PopStyleColor();
2824 } else {
2825 ImGui::Text("%s %s", ICON_MD_TAB, session.GetDisplayName().c_str());
2826 }
2827
2828 // ROM info column with better information layout
2829 ImGui::TableNextColumn();
2830 ImGui::AlignTextToFramePadding();
2831
2832 if (session.rom.is_loaded()) {
2833 ImGui::Text("%s %s", ICON_MD_VIDEOGAME_ASSET,
2834 session.rom.title().c_str());
2835 ImGui::Text("%.1f MB | %s", session.rom.size() / 1048576.0f,
2836 session.rom.dirty() ? "Modified" : "Clean");
2837 } else {
2838 ImGui::TextColored(ImVec4(0.6f, 0.6f, 0.6f, 1.0f), "%s No ROM loaded",
2840 }
2841
2842 // Status column with better visual indicators
2843 ImGui::TableNextColumn();
2844 ImGui::AlignTextToFramePadding();
2845
2846 if (session.rom.is_loaded()) {
2847 if (session.rom.dirty()) {
2848 ImGui::TextColored(ImVec4(1.0f, 0.5f, 0.0f, 1.0f), "%s",
2849 ICON_MD_EDIT);
2850 } else {
2851 ImGui::TextColored(ImVec4(0.2f, 0.8f, 0.2f, 1.0f), "%s",
2853 }
2854 } else {
2855 ImGui::TextColored(ImVec4(0.6f, 0.6f, 0.6f, 1.0f), "%s",
2857 }
2858
2859 // Actions column with improved button layout
2860 ImGui::TableNextColumn();
2861
2862 // Create button group for better alignment
2863 ImGui::BeginGroup();
2864
2865 if (!is_current) {
2866 if (ImGui::Button("Switch")) {
2867 SwitchToSession(i);
2868 show_session_switcher_ = false;
2869 }
2870 } else {
2871 ImGui::BeginDisabled();
2872 ImGui::Button("Current");
2873 ImGui::EndDisabled();
2874 }
2875
2876 ImGui::SameLine();
2877 if (ImGui::Button("Rename")) {
2879 // Safe string copy with bounds checking
2880 const std::string& name = session.GetDisplayName();
2881 size_t copy_len =
2882 std::min(name.length(), sizeof(session_rename_buffer_) - 1);
2883 std::memcpy(session_rename_buffer_, name.c_str(), copy_len);
2884 session_rename_buffer_[copy_len] = '\0';
2886 }
2887
2888 ImGui::SameLine();
2889
2890 // Close button logic
2891 bool can_close = GetActiveSessionCount() > 1;
2892 if (!can_close) {
2893 ImGui::BeginDisabled();
2894 }
2895
2896 if (ImGui::Button("Close")) {
2897 if (is_current) {
2899 } else {
2900 // Remove non-current session directly
2901 RemoveSession(i);
2903 false; // Close switcher since indices changed
2904 }
2905 }
2906
2907 if (!can_close) {
2908 ImGui::EndDisabled();
2909 }
2910
2911 ImGui::EndGroup();
2912
2913 ImGui::PopID();
2914 }
2915
2916 ImGui::EndTable();
2917 }
2918
2919 ImGui::Separator();
2920 if (ImGui::Button(
2921 absl::StrFormat("%s Close Switcher", ICON_MD_CLOSE).c_str(),
2922 ImVec2(-1, 0))) {
2923 show_session_switcher_ = false;
2924 }
2925 }
2926 ImGui::End();
2927}
2928
2931 return;
2932
2933 if (ImGui::Begin(absl::StrCat(ICON_MD_VIEW_LIST, " Session Manager").c_str(),
2935
2936 ImGui::Text("%s Session Management", ICON_MD_MANAGE_ACCOUNTS);
2937
2938 if (ImGui::Button(absl::StrCat(ICON_MD_ADD, " New Session").c_str())) {
2940 }
2941 ImGui::SameLine();
2942 if (ImGui::Button(
2943 absl::StrCat(ICON_MD_CONTENT_COPY, " Duplicate Current").c_str()) &&
2944 current_rom_) {
2946 }
2947
2948 ImGui::Separator();
2949 ImGui::Text("%s Active Sessions (%zu)", ICON_MD_TAB,
2951
2952 // Enhanced session management table with proper sizing
2953 const float AVAILABLE_HEIGHT = ImGui::GetContentRegionAvail().y;
2954
2955 if (ImGui::BeginTable("SessionTable", 6,
2956 ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg |
2957 ImGuiTableFlags_Resizable |
2958 ImGuiTableFlags_ScrollY |
2959 ImGuiTableFlags_SizingStretchProp |
2960 ImGuiTableFlags_ContextMenuInBody,
2961 ImVec2(0, AVAILABLE_HEIGHT))) {
2962
2963 // Setup columns with explicit sizing for better control
2964 ImGui::TableSetupColumn("Session", ImGuiTableColumnFlags_WidthStretch,
2965 0.15f);
2966 ImGui::TableSetupColumn("ROM Title", ImGuiTableColumnFlags_WidthStretch,
2967 0.3f);
2968 ImGui::TableSetupColumn("Size", ImGuiTableColumnFlags_WidthFixed, 70.0f);
2969 ImGui::TableSetupColumn("Status", ImGuiTableColumnFlags_WidthFixed,
2970 80.0f);
2971 ImGui::TableSetupColumn("Custom OW", ImGuiTableColumnFlags_WidthFixed,
2972 80.0f);
2973 ImGui::TableSetupColumn("Actions", ImGuiTableColumnFlags_WidthStretch,
2974 0.25f);
2975 ImGui::TableHeadersRow();
2976
2977 for (size_t i = 0; i < sessions_.size(); ++i) {
2978 auto& session = sessions_[i];
2979
2980 // Skip closed sessions in session manager too
2981 if (session.custom_name == "[CLOSED SESSION]") {
2982 continue;
2983 }
2984
2985 bool is_current = (&session.rom == current_rom_);
2986
2987 ImGui::TableNextRow(ImGuiTableRowFlags_None,
2988 50.0f); // Consistent row height
2989 ImGui::PushID(static_cast<int>(i));
2990
2991 // Session name column
2992 ImGui::TableNextColumn();
2993 ImGui::AlignTextToFramePadding();
2994 if (is_current) {
2995 ImGui::TextColored(ImVec4(0.2f, 0.8f, 0.2f, 1.0f), "%s Session %zu",
2996 ICON_MD_STAR, i + 1);
2997 } else {
2998 ImGui::Text("%s Session %zu", ICON_MD_TAB, i + 1);
2999 }
3000
3001 // ROM title column
3002 ImGui::TableNextColumn();
3003 ImGui::AlignTextToFramePadding();
3004 std::string display_name = session.GetDisplayName();
3005 if (!session.custom_name.empty()) {
3006 ImGui::TextColored(ImVec4(0.7f, 0.9f, 1.0f, 1.0f), "%s %s",
3007 ICON_MD_EDIT, display_name.c_str());
3008 } else {
3009 // Use TextWrapped for long ROM titles
3010 ImGui::PushTextWrapPos(ImGui::GetCursorPos().x +
3011 ImGui::GetColumnWidth());
3012 ImGui::Text("%s", display_name.c_str());
3013 ImGui::PopTextWrapPos();
3014 }
3015
3016 // File size column
3017 ImGui::TableNextColumn();
3018 ImGui::AlignTextToFramePadding();
3019 if (session.rom.is_loaded()) {
3020 ImGui::Text("%.1f MB", session.rom.size() / 1048576.0f);
3021 } else {
3022 ImGui::TextDisabled("N/A");
3023 }
3024
3025 // Status column
3026 ImGui::TableNextColumn();
3027 ImGui::AlignTextToFramePadding();
3028 if (session.rom.is_loaded()) {
3029 if (session.rom.dirty()) {
3030 ImGui::TextColored(ImVec4(1.0f, 0.5f, 0.0f, 1.0f), "%s",
3031 ICON_MD_EDIT);
3032 } else {
3033 ImGui::TextColored(ImVec4(0.2f, 0.8f, 0.2f, 1.0f), "%s",
3035 }
3036 } else {
3037 ImGui::TextColored(ImVec4(0.6f, 0.6f, 0.6f, 1.0f), "%s",
3039 }
3040
3041 // Custom Overworld checkbox column
3042 ImGui::TableNextColumn();
3043
3044 // Center the checkbox vertically
3045 float checkbox_offset =
3046 (ImGui::GetFrameHeight() - ImGui::GetTextLineHeight()) * 0.5f;
3047 ImGui::SetCursorPosY(ImGui::GetCursorPosY() + checkbox_offset);
3048
3049 ImGui::PushID(
3050 static_cast<int>(i + 100)); // Different ID to avoid conflicts
3051 bool custom_ow_enabled =
3052 session.feature_flags.overworld.kLoadCustomOverworld;
3053 if (ImGui::Checkbox("##CustomOW", &custom_ow_enabled)) {
3054 session.feature_flags.overworld.kLoadCustomOverworld =
3055 custom_ow_enabled;
3056 if (is_current) {
3058 custom_ow_enabled;
3059 }
3061 absl::StrFormat("Session %zu: Custom Overworld %s", i + 1,
3062 custom_ow_enabled ? "Enabled" : "Disabled"),
3064 }
3065 ImGui::PopID();
3066
3067 // Actions column with better button layout
3068 ImGui::TableNextColumn();
3069
3070 // Create button group for better alignment
3071 ImGui::BeginGroup();
3072
3073 if (!is_current) {
3074 if (ImGui::Button("Switch")) {
3075 SwitchToSession(i);
3076 }
3077 } else {
3078 ImGui::BeginDisabled();
3079 ImGui::Button("Current");
3080 ImGui::EndDisabled();
3081 }
3082
3083 ImGui::SameLine();
3084 if (ImGui::Button("Rename")) {
3086 // Safe string copy with bounds checking
3087 const std::string& name = session.GetDisplayName();
3088 size_t copy_len =
3089 std::min(name.length(), sizeof(session_rename_buffer_) - 1);
3090 std::memcpy(session_rename_buffer_, name.c_str(), copy_len);
3091 session_rename_buffer_[copy_len] = '\0';
3093 }
3094
3095 ImGui::SameLine();
3096
3097 // Close button logic
3098 bool can_close = GetActiveSessionCount() > 1;
3099 if (!can_close || is_current) {
3100 ImGui::BeginDisabled();
3101 }
3102
3103 if (ImGui::Button("Close")) {
3104 if (is_current) {
3106 break; // Exit loop since current session was closed
3107 } else {
3108 // Remove non-current session directly
3109 RemoveSession(i);
3110 break; // Exit loop since session indices changed
3111 }
3112 }
3113
3114 if (!can_close || is_current) {
3115 ImGui::EndDisabled();
3116 }
3117
3118 ImGui::EndGroup();
3119
3120 ImGui::PopID();
3121 }
3122
3123 ImGui::EndTable();
3124 }
3125 }
3126 ImGui::End();
3127}
3128
3131 return;
3132
3133 if (ImGui::Begin(absl::StrCat(ICON_MD_BOOKMARK, " Layout Presets").c_str(),
3135
3136 ImGui::Text("%s Predefined Layouts", ICON_MD_DASHBOARD);
3137
3138 // Predefined layouts
3139 if (ImGui::Button(
3140 absl::StrCat(ICON_MD_DEVELOPER_MODE, " Developer Layout").c_str(),
3141 ImVec2(-1, 40))) {
3143 }
3144 ImGui::SameLine();
3145 ImGui::Text("Code editing, debugging, testing");
3146
3147 if (ImGui::Button(
3148 absl::StrCat(ICON_MD_DESIGN_SERVICES, " Designer Layout").c_str(),
3149 ImVec2(-1, 40))) {
3151 }
3152 ImGui::SameLine();
3153 ImGui::Text("Graphics, palettes, sprites");
3154
3155 if (ImGui::Button(absl::StrCat(ICON_MD_GAMEPAD, " Modder Layout").c_str(),
3156 ImVec2(-1, 40))) {
3158 }
3159 ImGui::SameLine();
3160 ImGui::Text("All gameplay editors");
3161
3162 ImGui::Separator();
3163 ImGui::Text("%s Custom Presets", ICON_MD_BOOKMARK);
3164
3165 // Lazy load workspace presets when UI is accessed
3168 }
3169
3170 for (const auto& preset : workspace_manager_.workspace_presets()) {
3171 if (ImGui::Button(
3172 absl::StrFormat("%s %s", ICON_MD_BOOKMARK, preset.c_str())
3173 .c_str(),
3174 ImVec2(-1, 0))) {
3175 LoadWorkspacePreset(preset);
3177 absl::StrFormat("Loaded preset: %s", preset.c_str()),
3179 }
3180 }
3181
3182 if (workspace_manager_.workspace_presets().empty()) {
3183 ImGui::TextColored(ImVec4(0.6f, 0.6f, 0.6f, 1.0f),
3184 "No custom presets saved");
3185 }
3186
3187 ImGui::Separator();
3188 if (ImGui::Button(
3189 absl::StrCat(ICON_MD_ADD, " Save Current Layout").c_str())) {
3191 }
3192 }
3193 ImGui::End();
3194}
3195
3196bool EditorManager::HasDuplicateSession(const std::string& filepath) {
3197 for (const auto& session : sessions_) {
3198 if (session.filepath == filepath) {
3199 return true;
3200 }
3201 }
3202 return false;
3203}
3204
3205void EditorManager::RenameSession(size_t index, const std::string& new_name) {
3206 if (index < sessions_.size()) {
3207 sessions_[index].custom_name = new_name;
3209 absl::StrFormat("Session renamed to: %s", new_name.c_str()),
3211 }
3212}
3213
3216 return;
3217
3218 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(),
3219 ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
3220 ImGui::SetNextWindowSize(ImVec2(400, 150), ImGuiCond_Appearing);
3221
3222 if (ImGui::Begin("Rename Session", &show_session_rename_dialog_,
3223 ImGuiWindowFlags_NoResize)) {
3224 if (session_to_rename_ < sessions_.size()) {
3225 const auto& session = sessions_[session_to_rename_];
3226
3227 ImGui::Text("Rename Session:");
3228 ImGui::Text("Current: %s", session.GetDisplayName().c_str());
3229 ImGui::Separator();
3230
3231 ImGui::InputText("New Name", session_rename_buffer_,
3232 sizeof(session_rename_buffer_));
3233
3234 ImGui::Separator();
3235 if (ImGui::Button("Rename", ImVec2(120, 0))) {
3236 std::string new_name(session_rename_buffer_);
3237 if (!new_name.empty()) {
3239 }
3241 }
3242
3243 ImGui::SameLine();
3244 if (ImGui::Button("Cancel", ImVec2(120, 0))) {
3246 }
3247 }
3248 }
3249 ImGui::End();
3250}
3251
3253 // Use the new WelcomeScreen class for a modern, feature-rich experience
3255 bool was_open = show_welcome_screen_;
3256 bool action_taken = welcome_screen_.Show(&show_welcome_screen_);
3257
3258 // Check if the welcome screen was manually closed via the close button
3259 if (was_open && !show_welcome_screen_) {
3262 }
3263}
3264
3265// ============================================================================
3266// Jump-to Functionality for Cross-Editor Navigation
3267// ============================================================================
3268
3270 if (!current_editor_set_) return;
3271
3272 // Switch to dungeon editor
3274
3275 // Open the room in the dungeon editor
3277}
3278
3280 if (!current_editor_set_) return;
3281
3282 // Switch to overworld editor
3284
3285 // Set the current map in the overworld editor
3287}
3288
3290 // Find the editor tab and activate it
3291 for (size_t i = 0; i < current_editor_set_->active_editors_.size(); ++i) {
3292 if (current_editor_set_->active_editors_[i]->type() == editor_type) {
3293 current_editor_set_->active_editors_[i]->set_active(true);
3294
3295 // Set editor as the current/focused one
3296 // This will make it visible when tabs are rendered
3297 break;
3298 }
3299 }
3300}
3301
3302// ============================================================================
3303// User Settings Management
3304// ============================================================================
3305
3307 // Apply font scale after loading
3308 ImGui::GetIO().FontGlobalScale = user_settings_.prefs().font_global_scale;
3309
3310 // Apply welcome screen preference
3312 show_welcome_screen_ = false;
3314 }
3315}
3316
3318 auto status = user_settings_.Save();
3319 if (!status.ok()) {
3320 LOG_WARN("EditorManager", "Failed to save user settings: %s", status.ToString().c_str());
3321 }
3322}
3323
3324} // namespace editor
3325} // namespace yaze
The Rom class is used to load, save, and modify Rom data.
Definition rom.h:71
absl::Status LoadFromFile(const std::string &filename, bool z3_load=true)
Definition rom.cc:289
auto filename() const
Definition rom.h:208
absl::Status SaveToFile(const SaveSettings &settings)
Definition rom.cc:536
auto size() const
Definition rom.h:202
bool dirty() const
Definition rom.h:198
core::ResourceLabelManager * resource_label()
Definition rom.h:220
auto short_name() const
Definition rom.h:210
void Close()
Definition rom.h:107
bool is_loaded() const
Definition rom.h:197
auto title() const
Definition rom.h:201
absl::StatusOr< AgentResponse > GenerateMultimodalResponse(const std::string &image_path, const std::string &prompt)
static Flags & get()
Definition features.h:79
static RecentFilesManager & GetInstance()
Definition project.h:244
static TimingManager & Get()
Definition timing.h:20
float Update()
Update the timing manager (call once per frame)
Definition timing.h:29
void SetToastManager(ToastManager *toast_manager)
void OpenFolder(const std::string &folder_path)
void Update(bool &is_loaded)
void Initialize(gfx::IRenderer *renderer, Rom *rom)
std::deque< RomSession > sessions_
void SaveWorkspacePreset(const std::string &name)
absl::Status SaveRomAs(const std::string &filename)
void JumpToDungeonRoom(int room_id)
void SwitchToSession(size_t index)
bool HasDuplicateSession(const std::string &filepath)
void SwitchToEditor(EditorType editor_type)
EditorSelectionDialog editor_selection_dialog_
void LoadWorkspacePreset(const std::string &name)
std::string GenerateUniqueEditorTitle(EditorType type, size_t session_index) const
void RenameSession(size_t index, const std::string &new_name)
void Initialize(gfx::IRenderer *renderer, const std::string &filename="")
AgentChatHistoryPopup agent_chat_history_popup_
absl::Status CreateNewProject(const std::string &template_name="Basic ROM Hack")
ProjectFileEditor project_file_editor_
void OpenEditorAndCardsFromFlags(const std::string &editor_name, const std::string &cards_str)
void JumpToOverworldMap(int map_id)
absl::Status ImportProject(const std::string &project_path)
core::YazeProject current_project_
WorkspaceManager workspace_manager_
absl::Status OpenRomOrProject(const std::string &filename)
void RemoveSession(size_t index)
std::unique_ptr< PopupManager > popup_manager_
absl::Status SetCurrentRom(Rom *rom)
void ClearRecentEditors()
Clear recent editors (for new ROM sessions)
void SetSelectionCallback(std::function< void(EditorType)> callback)
Set callback for when editor is selected.
void MarkRecentlyUsed(EditorType type)
Mark an editor as recently used.
bool Show(bool *p_open=nullptr)
Show the dialog.
Contains a complete set of editors for a single ROM instance.
SettingsEditor settings_editor_
DungeonEditorV2 dungeon_editor_
MemoryEditorWithDiffChecker memory_editor_
GraphicsEditor graphics_editor_
void set_user_settings(UserSettings *settings)
AssemblyEditor assembly_editor_
OverworldEditor overworld_editor_
MessageEditor message_editor_
std::vector< Editor * > active_editors_
PaletteEditor palette_editor_
virtual absl::Status Cut()=0
virtual absl::Status Copy()=0
virtual absl::Status Redo()=0
EditorType type() const
Definition editor.h:110
void set_active(bool active)
Definition editor.h:115
virtual absl::Status Find()=0
virtual absl::Status Paste()=0
virtual absl::Status Undo()=0
MenuBuilder & Item(const char *label, const char *icon, Callback callback, const char *shortcut=nullptr, EnabledCheck enabled=nullptr, EnabledCheck checked=nullptr)
Add a menu item.
void Draw()
Draw the menu bar (call in main menu bar)
void Clear()
Clear all menus.
MenuBuilder & BeginMenu(const char *label, const char *icon=nullptr)
Begin a top-level menu.
MenuBuilder & Separator()
Add a separator.
MenuBuilder & BeginSubMenu(const char *label, const char *icon=nullptr, EnabledCheck enabled=nullptr)
Begin a submenu.
absl::Status Load() override
absl::Status Load() override
Manipulates the Overworld and OverworldMap data in a Rom.
absl::Status Load() override
void Initialize(gfx::IRenderer *renderer, Rom *rom)
absl::Status Update() override
absl::Status Load() override
void SetToastManager(ToastManager *toast_manager)
Set toast manager for notifications.
void FocusProposal(const std::string &proposal_id)
absl::Status Load() override
std::vector< zelda3::DungeonMap > dungeon_maps_
absl::Status Load() override
void RegisterShortcut(const std::string &name, const std::vector< ImGuiKey > &keys)
absl::Status Load() override
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 MarkManuallyClosed()
Mark as manually closed (don't show again this session)
void RefreshRecentProjects()
Refresh recent projects list from the project manager.
void SetOpenRomCallback(std::function< void()> callback)
Set callback for opening ROM.
bool Show(bool *p_open)
Show the welcome screen.
void SetOpenProjectCallback(std::function< void(const std::string &)> callback)
Set callback for opening project.
const std::vector< std::string > & workspace_presets() const
void set_renderer(gfx::IRenderer *renderer)
Definition emulator.h:58
void Run(Rom *rom)
Definition emulator.cc:134
static Arena & Get()
Definition arena.cc:15
Defines an abstract interface for all rendering operations.
Definition irenderer.h:35
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 BackgroundRenderer & Get()
void ShowAllCardsInCategory(const std::string &category)
static EditorCardManager & Get()
static ThemeManager & Get()
absl::Status ShowHarnessActiveTests()
absl::Status ShowHarnessDashboard()
absl::Status RunTestSuite(const std::string &suite_name)
void SetCurrentRom(Rom *rom)
absl::Status RunAllTests()
static TestManager & Get()
absl::Status RunTestsByCategory(TestCategory category)
absl::Status ReplayLastPlan()
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_CLOUD_OFF
Definition icons.h:425
#define ICON_MD_DEVELOPER_MODE
Definition icons.h:547
#define ICON_MD_FOLDER_OPEN
Definition icons.h:811
#define ICON_MD_CONTENT_CUT
Definition icons.h:464
#define ICON_MD_SAVE_ALT
Definition icons.h:1643
#define ICON_MD_SETTINGS
Definition icons.h:1697
#define ICON_MD_FILE_OPEN
Definition icons.h:745
#define ICON_MD_EXIT_TO_APP
Definition icons.h:697
#define ICON_MD_APPS
Definition icons.h:166
#define ICON_MD_INFO
Definition icons.h:991
#define ICON_MD_CHAT
Definition icons.h:392
#define ICON_MD_CANCEL
Definition icons.h:362
#define ICON_MD_MEMORY
Definition icons.h:1193
#define ICON_MD_STORAGE
Definition icons.h:1863
#define ICON_MD_WARNING
Definition icons.h:2121
#define ICON_MD_RATE_REVIEW
Definition icons.h:1554
#define ICON_MD_UPGRADE
Definition icons.h:2045
#define ICON_MD_VIEW_LIST
Definition icons.h:2090
#define ICON_MD_SEARCH
Definition icons.h:1671
#define ICON_MD_DATA_ARRAY
Definition icons.h:517
#define ICON_MD_STAR
Definition icons.h:1846
#define ICON_MD_PLAY_ARROW
Definition icons.h:1477
#define ICON_MD_SAVE_AS
Definition icons.h:1644
#define ICON_MD_DESIGN_SERVICES
Definition icons.h:539
#define ICON_MD_LOGOUT
Definition icons.h:1146
#define ICON_MD_FULLSCREEN_EXIT
Definition icons.h:860
#define ICON_MD_INTEGRATION_INSTRUCTIONS
Definition icons.h:1006
#define ICON_MD_RESET_TV
Definition icons.h:1599
#define ICON_MD_TUNE
Definition icons.h:2020
#define ICON_MD_REFRESH
Definition icons.h:1570
#define ICON_MD_MAP
Definition icons.h:1171
#define ICON_MD_CODE
Definition icons.h:432
#define ICON_MD_LABEL
Definition icons.h:1051
#define ICON_MD_VIDEOGAME_ASSET
Definition icons.h:2074
#define ICON_MD_REDO
Definition icons.h:1568
#define ICON_MD_CHAT_BUBBLE
Definition icons.h:393
#define ICON_MD_MESSAGE
Definition icons.h:1199
#define ICON_MD_SWITCH_ACCOUNT
Definition icons.h:1911
#define ICON_MD_VISIBILITY
Definition icons.h:2099
#define ICON_MD_EDIT
Definition icons.h:643
#define ICON_MD_LOGIN
Definition icons.h:1144
#define ICON_MD_SPEED
Definition icons.h:1815
#define ICON_MD_CASTLE
Definition icons.h:378
#define ICON_MD_SCREENSHOT
Definition icons.h:1664
#define ICON_MD_MUSIC_NOTE
Definition icons.h:1262
#define ICON_MD_CONTENT_PASTE
Definition icons.h:465
#define ICON_MD_HOME
Definition icons.h:951
#define ICON_MD_FULLSCREEN
Definition icons.h:859
#define ICON_MD_LIST
Definition icons.h:1092
#define ICON_MD_MANAGE_SEARCH
Definition icons.h:1170
#define ICON_MD_CLEAR_ALL
Definition icons.h:415
#define ICON_MD_VISIBILITY_OFF
Definition icons.h:2100
#define ICON_MD_LAYERS
Definition icons.h:1066
#define ICON_MD_CLOSE_FULLSCREEN
Definition icons.h:417
#define ICON_MD_DISPLAY_SETTINGS
Definition icons.h:585
#define ICON_MD_ADD
Definition icons.h:84
#define ICON_MD_SCIENCE
Definition icons.h:1654
#define ICON_MD_WIFI
Definition icons.h:2158
#define ICON_MD_IMAGE
Definition icons.h:980
#define ICON_MD_CHECK_CIRCLE
Definition icons.h:398
#define ICON_MD_TERMINAL
Definition icons.h:1949
#define ICON_MD_CLEAR
Definition icons.h:414
#define ICON_MD_PREVIEW
Definition icons.h:1510
#define ICON_MD_FLAG
Definition icons.h:782
#define ICON_MD_DESCRIPTION
Definition icons.h:537
#define ICON_MD_MANAGE_ACCOUNTS
Definition icons.h:1168
#define ICON_MD_BUILD
Definition icons.h:326
#define ICON_MD_CREATE_NEW_FOLDER
Definition icons.h:481
#define ICON_MD_DASHBOARD
Definition icons.h:515
#define ICON_MD_SAVE
Definition icons.h:1642
#define ICON_MD_BOOKMARK
Definition icons.h:283
#define ICON_MD_TAB
Definition icons.h:1928
#define ICON_MD_FOLDER
Definition icons.h:807
#define ICON_MD_BACKUP
Definition icons.h:229
#define ICON_MD_PALETTE
Definition icons.h:1368
#define ICON_MD_CONTENT_COPY
Definition icons.h:463
#define ICON_MD_TV
Definition icons.h:2030
#define ICON_MD_RADIO_BUTTON_UNCHECKED
Definition icons.h:1549
#define ICON_MD_PHOTO
Definition icons.h:1449
#define ICON_MD_CLOSE
Definition icons.h:416
#define ICON_MD_ANALYTICS
Definition icons.h:152
#define ICON_MD_TOYS
Definition icons.h:2000
#define ICON_MD_GAMEPAD
Definition icons.h:864
#define ICON_MD_HELP
Definition icons.h:931
#define ICON_MD_CLOUD_UPLOAD
Definition icons.h:428
#define ICON_MD_UNDO
Definition icons.h:2037
#define ICON_MD_ADD_CIRCLE
Definition icons.h:93
#define ICON_MD_SMART_TOY
Definition icons.h:1779
#define ICON_MD_HISTORY
Definition icons.h:944
#define LOG_DEBUG(category, format,...)
Definition log.h:104
#define LOG_WARN(category, format,...)
Definition log.h:108
#define LOG_INFO(category, format,...)
Definition log.h:106
#define PRINT_IF_ERROR(expression)
Definition macro.h:27
#define RETURN_IF_ERROR(expression)
Definition macro.h:53
#define ASSIGN_OR_RETURN(type_variable_name, expression)
Definition macro.h:61
Definition input.cc:20
constexpr std::array< const char *, 14 > kEditorNames
Definition editor.h:70
constexpr const char * kDungeonEditorName
constexpr const char * kMessageEditorName
constexpr const char * kGraphicsEditorName
constexpr const char * kScreenEditorName
constexpr const char * kSettingsEditorName
constexpr const char * kMusicEditorName
constexpr const char * kOverworldEditorName
std::string PrintShortcut(const std::vector< ImGuiKey > &keys)
constexpr const char * kAssemblyEditorName
constexpr const char * kSpriteEditorName
void ExecuteShortcuts(const ShortcutManager &shortcut_manager)
constexpr const char * kPaletteEditorName
constexpr ImVec2 kDefaultModalSize
Definition input.h:21
void RegisterZ3edTestSuites()
std::string GetFileName(const std::string &filename)
Gets the filename from a full path.
Definition file_util.cc:19
std::string GetFileExtension(const std::string &filename)
Gets the file extension from a filename.
Definition file_util.cc:15
void logf(const absl::FormatSpec< Args... > &format, Args &&... args)
Definition log.h:116
constexpr int OverworldCustomASMHasBeenApplied
absl::Status SaveDungeonMaps(Rom &rom, std::vector< DungeonMap > &dungeon_maps)
Save the dungeon maps to the ROM.
Main namespace for the application.
absl::StatusOr< std::array< gfx::Bitmap, kNumGfxSheets > > LoadAllGraphicsData(Rom &rom, bool defer_render)
This function iterates over all graphics sheets in the Rom and loads them into memory....
Definition rom.cc:172
absl::Status SaveAllGraphicsData(Rom &rom, std::array< gfx::Bitmap, kNumGfxSheets > &gfx_sheets)
Definition rom.cc:253
std::string filename
Definition rom.h:77
struct yaze::core::FeatureFlags::Flags::Overworld overworld
bool LoadLabels(const std::string &filename)
Definition project.cc:770
void DisplayLabels(bool *p_open)
Definition project.cc:828
std::unordered_map< std::string, std::unordered_map< std::string, std::string > > labels_
Definition project.h:235
std::vector< std::string > recent_files
Definition project.h:70
Modern project structure with comprehensive settings consolidation.
Definition project.h:78
absl::Status Open(const std::string &project_path)
Definition project.cc:115
bool project_opened() const
Definition project.h:162
WorkspaceSettings workspace_settings
Definition project.h:99
std::string code_folder
Definition project.h:91
absl::Status Validate() const
Definition project.cc:461
FeatureFlags::Flags feature_flags
Definition project.h:98
std::string labels_filename
Definition project.h:94
absl::Status RepairProject()
Definition project.cc:504
absl::Status Save()
Definition project.cc:150
std::string GetDisplayName() const
Definition project.cc:532
absl::Status Create(const std::string &project_name, const std::string &base_path)
Definition project.cc:76
std::string filepath
Definition project.h:82
std::string rom_filename
Definition project.h:86
absl::Status ImportZScreamProject(const std::string &zscream_project_path)
Definition project.cc:402
std::function< void(const std::string &)> focus_proposal
std::function< absl::Status(const std::filesystem::path &, const std::string &)> send_to_gemini
std::function< absl::Status(std::filesystem::path *)> capture_snapshot
std::function< absl::StatusOr< std::string >(const std::string &)> diff_proposal
std::function< absl::StatusOr< std::vector< std::string > >()> list_proposals
std::function< absl::Status(const std::string &)> accept_proposal
std::function< absl::Status(const std::string &)> reject_proposal
ShortcutManager shortcut_manager
Definition editor.h:30
PopupManager * popup_manager
Definition editor.h:29
core::FeatureFlags::Flags feature_flags
void Update(bool &show_memory_editor)