feat(editor): enhance card-based editor functionality and streamline initialization

- Updated EditorManager to support new card-based editors, including Emulator, Hex, and Assembly editors.
- Added centralized registration of editor cards with EditorCardManager, improving visibility management.
- Implemented methods to retrieve editor types from categories and manage card visibility dynamically.
- Enhanced initialization processes for various editors to ensure proper card registration and default visibility settings.

Benefits:
- Improved user experience by organizing editor cards and providing quick access to new editor functionalities.
- Streamlined editor management, making it easier to switch between different editing contexts and enhancing overall workflow efficiency.
This commit is contained in:
scawful
2025-10-12 20:43:42 -04:00
parent bdb2d1ed14
commit e5aff24bfc
22 changed files with 595 additions and 670 deletions

View File

@@ -174,6 +174,17 @@ FolderItem LoadFolder(const std::string& folder) {
void AssemblyEditor::Initialize() { void AssemblyEditor::Initialize() {
text_editor_.SetLanguageDefinition(GetAssemblyLanguageDef()); text_editor_.SetLanguageDefinition(GetAssemblyLanguageDef());
// Register cards with EditorCardManager
auto& card_manager = gui::EditorCardManager::Get();
card_manager.RegisterCard({.card_id = "assembly.editor", .display_name = "Assembly Editor",
.icon = ICON_MD_CODE, .category = "Assembly",
.shortcut_hint = "", .priority = 10});
card_manager.RegisterCard({.card_id = "assembly.file_browser", .display_name = "File Browser",
.icon = ICON_MD_FOLDER_OPEN, .category = "Assembly",
.shortcut_hint = "", .priority = 20});
// Don't show by default - only show when user explicitly opens Assembly Editor
} }
absl::Status AssemblyEditor::Load() { absl::Status AssemblyEditor::Load() {

View File

@@ -108,6 +108,10 @@ void DungeonEditorV2::Initialize(gfx::IRenderer* renderer, Rom* rom) {
.visibility_flag = &show_debug_controls_, .visibility_flag = &show_debug_controls_,
.priority = 80 .priority = 80
}); });
// Show control panel and room selector by default when Dungeon Editor is activated
show_control_panel_ = true;
show_room_selector_ = true;
} }
void DungeonEditorV2::Initialize() {} void DungeonEditorV2::Initialize() {}
@@ -187,32 +191,6 @@ absl::Status DungeonEditorV2::Update() {
// CARD-BASED EDITOR: All windows are independent top-level cards // CARD-BASED EDITOR: All windows are independent top-level cards
// No parent wrapper - this allows closing control panel without affecting rooms // No parent wrapper - this allows closing control panel without affecting rooms
// Optional control panel (can be hidden/minimized)
if (show_control_panel_) {
DrawControlPanel();
} else if (control_panel_minimized_) {
// Draw floating icon button to reopen
ImGui::SetNextWindowPos(ImVec2(10, 100));
ImGui::SetNextWindowSize(ImVec2(50, 50));
ImGuiWindowFlags icon_flags = ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoScrollbar |
ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoDocking;
if (ImGui::Begin("##DungeonControlIcon", nullptr, icon_flags)) {
if (ImGui::Button(ICON_MD_CASTLE, ImVec2(40, 40))) {
show_control_panel_ = true;
control_panel_minimized_ = false;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Open Dungeon Controls");
}
}
ImGui::End();
}
// Render all independent cards (these are ALL top-level windows now)
DrawLayout(); DrawLayout();
return absl::OkStatus(); return absl::OkStatus();
@@ -235,73 +213,6 @@ absl::Status DungeonEditorV2::Save() {
return absl::OkStatus(); return absl::OkStatus();
} }
void DungeonEditorV2::DrawToolset() {
// Draw VSCode-style sidebar using EditorCardManager
// auto& card_manager = gui::EditorCardManager::Get();
// card_manager.DrawSidebar("Dungeon");
}
void DungeonEditorV2::DrawControlPanel() {
// Small, collapsible control panel for dungeon editor
ImGui::SetNextWindowSize(ImVec2(280, 280), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowPos(ImVec2(10, 100), ImGuiCond_FirstUseEver);
ImGuiWindowFlags flags = ImGuiWindowFlags_None;
if (ImGui::Begin(ICON_MD_CASTLE " Dungeon Controls", &show_control_panel_, flags)) {
ImGui::TextWrapped("Welcome to Dungeon Editor V2!");
ImGui::TextDisabled("Use checkboxes below to open cards");
ImGui::Separator();
DrawToolset();
ImGui::Separator();
ImGui::Text("Quick Toggles:");
// Checkbox grid for quick toggles
if (ImGui::BeginTable("##QuickToggles", 2, ImGuiTableFlags_SizingStretchSame)) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Checkbox("Rooms", &show_room_selector_);
ImGui::TableNextColumn();
ImGui::Checkbox("Matrix", &show_room_matrix_);
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Checkbox("Entrances", &show_entrances_list_);
ImGui::TableNextColumn();
ImGui::Checkbox("Graphics", &show_room_graphics_);
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Checkbox("Objects", &show_object_editor_);
ImGui::TableNextColumn();
ImGui::Checkbox("Palette", &show_palette_editor_);
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Checkbox("Debug", &show_debug_controls_);
ImGui::EndTable();
}
ImGui::Separator();
// Minimize button
if (ImGui::SmallButton(ICON_MD_MINIMIZE " Minimize to Icon")) {
control_panel_minimized_ = true;
show_control_panel_ = false;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Collapse to floating icon. Rooms stay open.");
}
}
ImGui::End();
}
void DungeonEditorV2::DrawLayout() { void DungeonEditorV2::DrawLayout() {
// NO TABLE LAYOUT - All independent dockable EditorCards // NO TABLE LAYOUT - All independent dockable EditorCards
// All cards check their visibility flags and can be closed with X button // All cards check their visibility flags and can be closed with X button

View File

@@ -102,13 +102,11 @@ class DungeonEditorV2 : public Editor {
// Simple UI layout // Simple UI layout
void DrawLayout(); void DrawLayout();
void DrawRoomTab(int room_id); void DrawRoomTab(int room_id);
void DrawToolset();
void DrawRoomMatrixCard(); void DrawRoomMatrixCard();
void DrawRoomsListCard(); void DrawRoomsListCard();
void DrawEntrancesListCard(); void DrawEntrancesListCard();
void DrawRoomGraphicsCard(); void DrawRoomGraphicsCard();
void DrawDebugControlsCard(); void DrawDebugControlsCard();
void DrawControlPanel();
// Texture processing (critical for rendering) // Texture processing (critical for rendering)
void ProcessDeferredTextures(); void ProcessDeferredTextures();

View File

@@ -91,17 +91,22 @@ std::string GetEditorName(EditorType type) {
} // namespace } // namespace
// Static registry of editors that use the card-based layout system // Static registry of editors that use the card-based layout system
// These editors register their cards with EditorCardManager // These editors register their cards with EditorCardManager and manage their own windows
// They do NOT need the traditional ImGui::Begin/End wrapper - they create cards internally
bool EditorManager::IsCardBasedEditor(EditorType type) { bool EditorManager::IsCardBasedEditor(EditorType type) {
switch (type) { switch (type) {
case EditorType::kDungeon: case EditorType::kDungeon: // ✅ Full card system
case EditorType::kPalette: case EditorType::kPalette: // ✅ Full card system
case EditorType::kGraphics: case EditorType::kGraphics: // ✅ EditorCard wrappers + Toolset
case EditorType::kScreen: case EditorType::kScreen: // ✅ EditorCard wrappers + Toolset
case EditorType::kSprite: case EditorType::kSprite: // ✅ EditorCard wrappers + Toolset
case EditorType::kMessage: case EditorType::kOverworld: // ✅ Inline EditorCard + Toolset
case EditorType::kOverworld: case EditorType::kEmulator: // ✅ Emulator UI panels as cards
case EditorType::kMessage: // ✅ Message editor cards
case EditorType::kHex: // ✅ Memory/Hex editor
case EditorType::kAssembly: // ✅ Assembly editor
return true; return true;
// Music: Traditional UI - needs wrapper
default: default:
return false; return false;
} }
@@ -127,11 +132,30 @@ std::string EditorManager::GetEditorCategory(EditorType type) {
return "Screen"; return "Screen";
case EditorType::kEmulator: case EditorType::kEmulator:
return "Emulator"; return "Emulator";
case EditorType::kHex:
return "Memory";
case EditorType::kAssembly:
return "Assembly";
default: default:
return "Unknown"; return "Unknown";
} }
} }
EditorType EditorManager::GetEditorTypeFromCategory(const std::string& category) {
if (category == "Dungeon") return EditorType::kDungeon;
if (category == "Palette") return EditorType::kPalette;
if (category == "Graphics") return EditorType::kGraphics;
if (category == "Overworld") return EditorType::kOverworld;
if (category == "Sprite") return EditorType::kSprite;
if (category == "Message") return EditorType::kMessage;
if (category == "Music") return EditorType::kMusic;
if (category == "Screen") return EditorType::kScreen;
if (category == "Emulator") return EditorType::kEmulator;
if (category == "Memory") return EditorType::kHex;
if (category == "Assembly") return EditorType::kAssembly;
return EditorType::kUnknown;
}
void EditorManager::HideCurrentEditorCards() { void EditorManager::HideCurrentEditorCards() {
if (!current_editor_) { if (!current_editor_) {
return; return;
@@ -221,6 +245,37 @@ void EditorManager::Initialize(gfx::IRenderer* renderer, const std::string& file
"global.toggle_sidebar", "global.toggle_sidebar",
{ImGuiKey_LeftCtrl, ImGuiKey_B}, {ImGuiKey_LeftCtrl, ImGuiKey_B},
[this]() { show_card_sidebar_ = !show_card_sidebar_; }); [this]() { show_card_sidebar_ = !show_card_sidebar_; });
// Register emulator cards early (emulator Initialize might not be called)
auto& card_manager = gui::EditorCardManager::Get();
card_manager.RegisterCard({.card_id = "emulator.cpu_debugger", .display_name = "CPU Debugger",
.icon = ICON_MD_BUG_REPORT, .category = "Emulator", .priority = 10});
card_manager.RegisterCard({.card_id = "emulator.ppu_viewer", .display_name = "PPU Viewer",
.icon = ICON_MD_VIDEOGAME_ASSET, .category = "Emulator", .priority = 20});
card_manager.RegisterCard({.card_id = "emulator.memory_viewer", .display_name = "Memory Viewer",
.icon = ICON_MD_MEMORY, .category = "Emulator", .priority = 30});
card_manager.RegisterCard({.card_id = "emulator.breakpoints", .display_name = "Breakpoints",
.icon = ICON_MD_STOP, .category = "Emulator", .priority = 40});
card_manager.RegisterCard({.card_id = "emulator.performance", .display_name = "Performance",
.icon = ICON_MD_SPEED, .category = "Emulator", .priority = 50});
card_manager.RegisterCard({.card_id = "emulator.ai_agent", .display_name = "AI Agent",
.icon = ICON_MD_SMART_TOY, .category = "Emulator", .priority = 60});
card_manager.RegisterCard({.card_id = "emulator.save_states", .display_name = "Save States",
.icon = ICON_MD_SAVE, .category = "Emulator", .priority = 70});
card_manager.RegisterCard({.card_id = "emulator.keyboard_config", .display_name = "Keyboard Config",
.icon = ICON_MD_KEYBOARD, .category = "Emulator", .priority = 80});
card_manager.RegisterCard({.card_id = "emulator.apu_debugger", .display_name = "APU Debugger",
.icon = ICON_MD_AUDIOTRACK, .category = "Emulator", .priority = 90});
card_manager.RegisterCard({.card_id = "emulator.audio_mixer", .display_name = "Audio Mixer",
.icon = ICON_MD_AUDIO_FILE, .category = "Emulator", .priority = 100});
// Show CPU debugger and PPU viewer by default for emulator
card_manager.ShowCard("emulator.cpu_debugger");
card_manager.ShowCard("emulator.ppu_viewer");
// Register memory/hex editor card
card_manager.RegisterCard({.card_id = "memory.hex_editor", .display_name = "Hex Editor",
.icon = ICON_MD_MEMORY, .category = "Memory", .priority = 10});
// Initialize project file editor // Initialize project file editor
project_file_editor_.SetToastManager(&toast_manager_); project_file_editor_.SetToastManager(&toast_manager_);
@@ -411,54 +466,7 @@ void EditorManager::Initialize(gfx::IRenderer* renderer, const std::string& file
// Initialize editor selection dialog callback // Initialize editor selection dialog callback
editor_selection_dialog_.SetSelectionCallback([this](EditorType type) { editor_selection_dialog_.SetSelectionCallback([this](EditorType type) {
editor_selection_dialog_.MarkRecentlyUsed(type); editor_selection_dialog_.MarkRecentlyUsed(type);
SwitchToEditor(type); // Use centralized method
// Handle agent editor separately (doesn't require ROM)
if (type == EditorType::kAgent) {
#ifdef YAZE_WITH_GRPC
agent_editor_.toggle_active();
#endif
return;
}
if (!current_editor_set_) return;
switch (type) {
case EditorType::kOverworld:
current_editor_set_->overworld_editor_.toggle_active();
break;
case EditorType::kDungeon:
current_editor_set_->dungeon_editor_.toggle_active();
break;
case EditorType::kGraphics:
current_editor_set_->graphics_editor_.toggle_active();
break;
case EditorType::kSprite:
current_editor_set_->sprite_editor_.toggle_active();
break;
case EditorType::kMessage:
current_editor_set_->message_editor_.toggle_active();
break;
case EditorType::kMusic:
current_editor_set_->music_editor_.toggle_active();
break;
case EditorType::kPalette:
current_editor_set_->palette_editor_.toggle_active();
break;
case EditorType::kScreen:
current_editor_set_->screen_editor_.toggle_active();
break;
case EditorType::kAssembly:
show_asm_editor_ = true;
break;
case EditorType::kEmulator:
show_emulator_ = true;
break;
case EditorType::kSettings:
current_editor_set_->settings_editor_.toggle_active();
break;
default:
break;
}
}); });
// Load user settings - this must happen after context is initialized // Load user settings - this must happen after context is initialized
@@ -537,37 +545,37 @@ void EditorManager::Initialize(gfx::IRenderer* renderer, const std::string& file
context_.shortcut_manager.RegisterShortcut( context_.shortcut_manager.RegisterShortcut(
"F1", ImGuiKey_F1, [this]() { popup_manager_->Show("About"); }); "F1", ImGuiKey_F1, [this]() { popup_manager_->Show("About"); });
// Editor shortcuts (Ctrl+1-9, Ctrl+0) // Editor shortcuts (Ctrl+1-9, Ctrl+0) - all use SwitchToEditor for consistency
context_.shortcut_manager.RegisterShortcut( context_.shortcut_manager.RegisterShortcut(
"Overworld Editor", {ImGuiKey_1, ImGuiMod_Ctrl}, "Overworld Editor", {ImGuiKey_LeftCtrl, ImGuiKey_1},
[this]() { if (current_editor_set_) current_editor_set_->overworld_editor_.toggle_active(); }); [this]() { SwitchToEditor(EditorType::kOverworld); });
context_.shortcut_manager.RegisterShortcut( context_.shortcut_manager.RegisterShortcut(
"Dungeon Editor", {ImGuiKey_2, ImGuiMod_Ctrl}, "Dungeon Editor", {ImGuiKey_LeftCtrl, ImGuiKey_2},
[this]() { if (current_editor_set_) current_editor_set_->dungeon_editor_.toggle_active(); }); [this]() { SwitchToEditor(EditorType::kDungeon); });
context_.shortcut_manager.RegisterShortcut( context_.shortcut_manager.RegisterShortcut(
"Graphics Editor", {ImGuiKey_3, ImGuiMod_Ctrl}, "Graphics Editor", {ImGuiKey_LeftCtrl, ImGuiKey_3},
[this]() { if (current_editor_set_) current_editor_set_->graphics_editor_.toggle_active(); }); [this]() { SwitchToEditor(EditorType::kGraphics); });
context_.shortcut_manager.RegisterShortcut( context_.shortcut_manager.RegisterShortcut(
"Sprite Editor", {ImGuiKey_4, ImGuiMod_Ctrl}, "Sprite Editor", {ImGuiKey_LeftCtrl, ImGuiKey_4},
[this]() { if (current_editor_set_) current_editor_set_->sprite_editor_.toggle_active(); }); [this]() { SwitchToEditor(EditorType::kSprite); });
context_.shortcut_manager.RegisterShortcut( context_.shortcut_manager.RegisterShortcut(
"Message Editor", {ImGuiKey_5, ImGuiMod_Ctrl}, "Message Editor", {ImGuiKey_LeftCtrl, ImGuiKey_5},
[this]() { if (current_editor_set_) current_editor_set_->message_editor_.toggle_active(); }); [this]() { SwitchToEditor(EditorType::kMessage); });
context_.shortcut_manager.RegisterShortcut( context_.shortcut_manager.RegisterShortcut(
"Music Editor", {ImGuiKey_6, ImGuiMod_Ctrl}, "Music Editor", {ImGuiKey_LeftCtrl, ImGuiKey_6},
[this]() { if (current_editor_set_) current_editor_set_->music_editor_.toggle_active(); }); [this]() { SwitchToEditor(EditorType::kMusic); });
context_.shortcut_manager.RegisterShortcut( context_.shortcut_manager.RegisterShortcut(
"Palette Editor", {ImGuiKey_7, ImGuiMod_Ctrl}, "Palette Editor", {ImGuiKey_LeftCtrl, ImGuiKey_7},
[this]() { if (current_editor_set_) current_editor_set_->palette_editor_.toggle_active(); }); [this]() { SwitchToEditor(EditorType::kPalette); });
context_.shortcut_manager.RegisterShortcut( context_.shortcut_manager.RegisterShortcut(
"Screen Editor", {ImGuiKey_8, ImGuiMod_Ctrl}, "Screen Editor", {ImGuiKey_LeftCtrl, ImGuiKey_8},
[this]() { if (current_editor_set_) current_editor_set_->screen_editor_.toggle_active(); }); [this]() { SwitchToEditor(EditorType::kScreen); });
context_.shortcut_manager.RegisterShortcut( context_.shortcut_manager.RegisterShortcut(
"Assembly Editor", {ImGuiKey_9, ImGuiMod_Ctrl}, "Assembly Editor", {ImGuiKey_LeftCtrl, ImGuiKey_9},
[this]() { show_asm_editor_ = true; }); [this]() { SwitchToEditor(EditorType::kAssembly); });
context_.shortcut_manager.RegisterShortcut( context_.shortcut_manager.RegisterShortcut(
"Settings Editor", {ImGuiKey_0, ImGuiMod_Ctrl}, "Settings Editor", {ImGuiKey_LeftCtrl, ImGuiKey_0},
[this]() { if (current_editor_set_) current_editor_set_->settings_editor_.toggle_active(); }); [this]() { SwitchToEditor(EditorType::kSettings); });
// Editor Selection Dialog shortcut // Editor Selection Dialog shortcut
context_.shortcut_manager.RegisterShortcut( context_.shortcut_manager.RegisterShortcut(
@@ -912,13 +920,6 @@ absl::Status EditorManager::Update() {
} }
} }
// Draw sidebar for current card-based editor (only if sidebar is visible)
if (show_card_sidebar_ && current_editor_ && IsCardBasedEditor(current_editor_->type())) {
std::string category = GetEditorCategory(current_editor_->type());
auto& card_manager = gui::EditorCardManager::Get();
card_manager.DrawSidebar(category);
}
if (show_performance_dashboard_) { if (show_performance_dashboard_) {
gfx::PerformanceDashboard::Get().Render(); gfx::PerformanceDashboard::Get().Render();
} }
@@ -933,6 +934,59 @@ absl::Status EditorManager::Update() {
} }
#endif #endif
// Draw unified sidebar LAST so it appears on top of all other windows
if (show_card_sidebar_ && current_editor_set_) {
auto& card_manager = gui::EditorCardManager::Get();
// Collect all active card-based editors
std::vector<std::string> active_categories;
for (size_t session_idx = 0; session_idx < sessions_.size(); ++session_idx) {
auto& session = sessions_[session_idx];
if (!session.rom.is_loaded()) continue;
for (auto editor : session.editors.active_editors_) {
if (*editor->active() && IsCardBasedEditor(editor->type())) {
std::string category = GetEditorCategory(editor->type());
if (std::find(active_categories.begin(), active_categories.end(), category) == active_categories.end()) {
active_categories.push_back(category);
}
}
}
}
// Determine which category to show in sidebar
std::string sidebar_category;
// Priority 1: Use active_category from card manager (user's last interaction)
if (!card_manager.GetActiveCategory().empty() &&
std::find(active_categories.begin(), active_categories.end(),
card_manager.GetActiveCategory()) != active_categories.end()) {
sidebar_category = card_manager.GetActiveCategory();
}
// Priority 2: Use first active category
else if (!active_categories.empty()) {
sidebar_category = active_categories[0];
card_manager.SetActiveCategory(sidebar_category);
}
// Draw sidebar if we have a category
if (!sidebar_category.empty()) {
// Callback to switch editors when category button is clicked
auto category_switch_callback = [this](const std::string& new_category) {
EditorType editor_type = GetEditorTypeFromCategory(new_category);
if (editor_type != EditorType::kUnknown) {
SwitchToEditor(editor_type);
}
};
auto collapse_callback = [this]() {
show_card_sidebar_ = false;
};
card_manager.DrawSidebar(sidebar_category, active_categories, category_switch_callback, collapse_callback);
}
}
return absl::OkStatus(); return absl::OkStatus();
} }
@@ -1112,23 +1166,23 @@ void EditorManager::BuildModernMenu() {
[this]() { show_editor_selection_ = true; }, "Ctrl+E") [this]() { show_editor_selection_ = true; }, "Ctrl+E")
.Separator() .Separator()
.Item("Overworld", ICON_MD_MAP, .Item("Overworld", ICON_MD_MAP,
[this]() { current_editor_set_->overworld_editor_.set_active(true); }, "Ctrl+1") [this]() { SwitchToEditor(EditorType::kOverworld); }, "Ctrl+1")
.Item("Dungeon", ICON_MD_CASTLE, .Item("Dungeon", ICON_MD_CASTLE,
[this]() { current_editor_set_->dungeon_editor_.set_active(true); }, "Ctrl+2") [this]() { SwitchToEditor(EditorType::kDungeon); }, "Ctrl+2")
.Item("Graphics", ICON_MD_IMAGE, .Item("Graphics", ICON_MD_IMAGE,
[this]() { current_editor_set_->graphics_editor_.set_active(true); }, "Ctrl+3") [this]() { SwitchToEditor(EditorType::kGraphics); }, "Ctrl+3")
.Item("Sprites", ICON_MD_TOYS, .Item("Sprites", ICON_MD_TOYS,
[this]() { current_editor_set_->sprite_editor_.set_active(true); }, "Ctrl+4") [this]() { SwitchToEditor(EditorType::kSprite); }, "Ctrl+4")
.Item("Messages", ICON_MD_CHAT_BUBBLE, .Item("Messages", ICON_MD_CHAT_BUBBLE,
[this]() { current_editor_set_->message_editor_.set_active(true); }, "Ctrl+5") [this]() { SwitchToEditor(EditorType::kMessage); }, "Ctrl+5")
.Item("Music", ICON_MD_MUSIC_NOTE, .Item("Music", ICON_MD_MUSIC_NOTE,
[this]() { current_editor_set_->music_editor_.set_active(true); }, "Ctrl+6") [this]() { SwitchToEditor(EditorType::kMusic); }, "Ctrl+6")
.Item("Palettes", ICON_MD_PALETTE, .Item("Palettes", ICON_MD_PALETTE,
[this]() { current_editor_set_->palette_editor_.set_active(true); }, "Ctrl+7") [this]() { SwitchToEditor(EditorType::kPalette); }, "Ctrl+7")
.Item("Screens", ICON_MD_TV, .Item("Screens", ICON_MD_TV,
[this]() { current_editor_set_->screen_editor_.set_active(true); }, "Ctrl+8") [this]() { SwitchToEditor(EditorType::kScreen); }, "Ctrl+8")
.Item("Hex Editor", ICON_MD_DATA_ARRAY, .Item("Hex Editor", ICON_MD_DATA_ARRAY,
[this]() { show_memory_editor_ = true; }, "Ctrl+0") [this]() { gui::EditorCardManager::Get().ShowCard("memory.hex_editor"); }, "Ctrl+0")
#ifdef YAZE_WITH_GRPC #ifdef YAZE_WITH_GRPC
.Item("AI Agent", ICON_MD_SMART_TOY, .Item("AI Agent", ICON_MD_SMART_TOY,
[this]() { agent_editor_.set_active(true); }, "Ctrl+Shift+A") [this]() { agent_editor_.set_active(true); }, "Ctrl+Shift+A")
@@ -1388,9 +1442,9 @@ void EditorManager::BuildModernMenu() {
.Separator() .Separator()
// Development Tools // Development Tools
.Item("Memory Editor", ICON_MD_MEMORY, .Item("Memory Editor", ICON_MD_MEMORY,
[this]() { show_memory_editor_ = true; }) [this]() { gui::EditorCardManager::Get().ShowCard("memory.hex_editor"); })
.Item("Assembly Editor", ICON_MD_CODE, .Item("Assembly Editor", ICON_MD_CODE,
[this]() { show_asm_editor_ = true; }) [this]() { gui::EditorCardManager::Get().ShowCard("assembly.editor"); })
.Item("Feature Flags", ICON_MD_FLAG, .Item("Feature Flags", ICON_MD_FLAG,
[this]() { popup_manager_->Show("Feature Flags"); }) [this]() { popup_manager_->Show("Feature Flags"); })
.Separator() .Separator()
@@ -1512,11 +1566,15 @@ void EditorManager::DrawMenuBar() {
ShowDemoWindow(&show_imgui_demo_); ShowDemoWindow(&show_imgui_demo_);
if (show_imgui_metrics_) if (show_imgui_metrics_)
ShowMetricsWindow(&show_imgui_metrics_); ShowMetricsWindow(&show_imgui_metrics_);
if (show_memory_editor_ && current_editor_set_) {
current_editor_set_->memory_editor_.Update(show_memory_editor_); auto& card_manager = gui::EditorCardManager::Get();
if (card_manager.IsCardVisible("memory.hex_editor") && current_editor_set_) {
bool show_memory = true;
current_editor_set_->memory_editor_.Update(show_memory);
} }
if (show_asm_editor_ && current_editor_set_) { if (card_manager.IsCardVisible("assembly.editor") && current_editor_set_) {
current_editor_set_->assembly_editor_.Update(show_asm_editor_); bool show_asm = true;
current_editor_set_->assembly_editor_.Update(show_asm);
} }
// Project file editor // Project file editor
@@ -1551,10 +1609,9 @@ void EditorManager::DrawMenuBar() {
DrawWelcomeScreen(); DrawWelcomeScreen();
} }
// Emulator is now card-based - it creates its own windows
if (show_emulator_) { if (show_emulator_) {
Begin("Emulator", &show_emulator_, ImGuiWindowFlags_MenuBar);
emulator_.Run(current_rom_); emulator_.Run(current_rom_);
End();
} }
// Enhanced Command Palette UI with Fuzzy Search // Enhanced Command Palette UI with Fuzzy Search
@@ -2172,6 +2229,11 @@ absl::Status EditorManager::LoadAssets() {
current_editor_set_->overworld_editor_.Initialize(); current_editor_set_->overworld_editor_.Initialize();
current_editor_set_->message_editor_.Initialize(); current_editor_set_->message_editor_.Initialize();
current_editor_set_->graphics_editor_.Initialize();
current_editor_set_->screen_editor_.Initialize();
current_editor_set_->sprite_editor_.Initialize();
current_editor_set_->palette_editor_.Initialize();
current_editor_set_->assembly_editor_.Initialize();
// Initialize the dungeon editor with the renderer // Initialize the dungeon editor with the renderer
current_editor_set_->dungeon_editor_.Initialize(renderer_, current_rom_); current_editor_set_->dungeon_editor_.Initialize(renderer_, current_rom_);
ASSIGN_OR_RETURN(*gfx::Arena::Get().mutable_gfx_sheets(), ASSIGN_OR_RETURN(*gfx::Arena::Get().mutable_gfx_sheets(),
@@ -3352,14 +3414,40 @@ void EditorManager::JumpToOverworldMap(int map_id) {
} }
void EditorManager::SwitchToEditor(EditorType editor_type) { void EditorManager::SwitchToEditor(EditorType editor_type) {
// Find the editor tab and activate it if (!current_editor_set_) return;
for (size_t i = 0; i < current_editor_set_->active_editors_.size(); ++i) {
if (current_editor_set_->active_editors_[i]->type() == editor_type) { // Toggle the editor
current_editor_set_->active_editors_[i]->set_active(true); for (auto* editor : current_editor_set_->active_editors_) {
if (editor->type() == editor_type) {
editor->toggle_active();
// Set editor as the current/focused one if (IsCardBasedEditor(editor_type)) {
// This will make it visible when tabs are rendered auto& card_manager = gui::EditorCardManager::Get();
break;
if (*editor->active()) {
// Editor activated - set its category
card_manager.SetActiveCategory(GetEditorCategory(editor_type));
} else {
// Editor deactivated - switch to another active card-based editor
for (auto* other : current_editor_set_->active_editors_) {
if (*other->active() && IsCardBasedEditor(other->type()) && other != editor) {
card_manager.SetActiveCategory(GetEditorCategory(other->type()));
break;
}
}
}
}
return;
}
}
// Handle non-editor-class cases
if (editor_type == EditorType::kAssembly) {
show_asm_editor_ = !show_asm_editor_;
} else if (editor_type == EditorType::kEmulator) {
show_emulator_ = !show_emulator_;
if (show_emulator_) {
gui::EditorCardManager::Get().SetActiveCategory("Emulator");
} }
} }
} }

View File

@@ -171,6 +171,7 @@ class EditorManager {
// Card-based editor registry // Card-based editor registry
static bool IsCardBasedEditor(EditorType type); static bool IsCardBasedEditor(EditorType type);
static std::string GetEditorCategory(EditorType type); static std::string GetEditorCategory(EditorType type);
static EditorType GetEditorTypeFromCategory(const std::string& category);
bool IsSidebarVisible() const { return show_card_sidebar_; } bool IsSidebarVisible() const { return show_card_sidebar_; }
void SetSidebarVisible(bool visible) { show_card_sidebar_ = visible; } void SetSidebarVisible(bool visible) { show_card_sidebar_ = visible; }

View File

@@ -43,48 +43,23 @@ constexpr ImGuiTableFlags kGfxEditTableFlags =
ImGuiTableFlags_SizingFixedFit; ImGuiTableFlags_SizingFixedFit;
void GraphicsEditor::Initialize() { void GraphicsEditor::Initialize() {
// Register cards with EditorCardManager during initialization (once)
auto& card_manager = gui::EditorCardManager::Get(); auto& card_manager = gui::EditorCardManager::Get();
card_manager.RegisterCard({ card_manager.RegisterCard({.card_id = "graphics.sheet_editor", .display_name = "Sheet Editor",
.card_id = "graphics.sheet_editor", .icon = ICON_MD_EDIT, .category = "Graphics",
.display_name = "Sheet Editor", .shortcut_hint = "Ctrl+Shift+1", .priority = 10});
.icon = ICON_MD_EDIT, card_manager.RegisterCard({.card_id = "graphics.sheet_browser", .display_name = "Sheet Browser",
.category = "Graphics", .icon = ICON_MD_VIEW_LIST, .category = "Graphics",
.shortcut_hint = "Ctrl+Shift+1", .shortcut_hint = "Ctrl+Shift+2", .priority = 20});
.visibility_flag = &show_sheet_editor_, card_manager.RegisterCard({.card_id = "graphics.player_animations", .display_name = "Player Animations",
.priority = 10 .icon = ICON_MD_PERSON, .category = "Graphics",
}); .shortcut_hint = "Ctrl+Shift+3", .priority = 30});
card_manager.RegisterCard({.card_id = "graphics.prototype_viewer", .display_name = "Prototype Viewer",
.icon = ICON_MD_CONSTRUCTION, .category = "Graphics",
.shortcut_hint = "Ctrl+Shift+4", .priority = 40});
card_manager.RegisterCard({ // Show sheet editor by default when Graphics Editor is activated
.card_id = "graphics.sheet_browser", card_manager.ShowCard("graphics.sheet_editor");
.display_name = "Sheet Browser",
.icon = ICON_MD_VIEW_LIST,
.category = "Graphics",
.shortcut_hint = "Ctrl+Shift+2",
.visibility_flag = &show_sheet_browser_,
.priority = 20
});
card_manager.RegisterCard({
.card_id = "graphics.player_animations",
.display_name = "Player Animations",
.icon = ICON_MD_PERSON,
.category = "Graphics",
.shortcut_hint = "Ctrl+Shift+3",
.visibility_flag = &show_player_animations_,
.priority = 30
});
card_manager.RegisterCard({
.card_id = "graphics.prototype_viewer",
.display_name = "Prototype Viewer",
.icon = ICON_MD_CONSTRUCTION,
.category = "Graphics",
.shortcut_hint = "Ctrl+Shift+4",
.visibility_flag = &show_prototype_viewer_,
.priority = 40
});
} }
absl::Status GraphicsEditor::Load() { absl::Status GraphicsEditor::Load() {
@@ -124,56 +99,46 @@ absl::Status GraphicsEditor::Load() {
} }
absl::Status GraphicsEditor::Update() { absl::Status GraphicsEditor::Update() {
DrawToolset(); auto& card_manager = gui::EditorCardManager::Get();
gui::VerticalSpacing(2.0f);
static gui::EditorCard sheet_editor_card("Sheet Editor", ICON_MD_EDIT);
static gui::EditorCard sheet_browser_card("Sheet Browser", ICON_MD_VIEW_LIST);
static gui::EditorCard player_anims_card("Player Animations", ICON_MD_PERSON);
static gui::EditorCard prototype_card("Prototype Viewer", ICON_MD_CONSTRUCTION);
// Create session-aware cards (non-static for multi-session support) sheet_editor_card.SetDefaultSize(900, 700);
gui::EditorCard sheet_editor_card(MakeCardTitle("Sheet Editor").c_str(), ICON_MD_EDIT); sheet_browser_card.SetDefaultSize(400, 600);
gui::EditorCard sheet_browser_card(MakeCardTitle("Sheet Browser").c_str(), ICON_MD_VIEW_LIST); player_anims_card.SetDefaultSize(500, 600);
gui::EditorCard player_anims_card(MakeCardTitle("Player Animations").c_str(), ICON_MD_PERSON); prototype_card.SetDefaultSize(600, 500);
gui::EditorCard prototype_card(MakeCardTitle("Prototype Viewer").c_str(), ICON_MD_CONSTRUCTION);
if (show_sheet_editor_) { // Get visibility flags from card manager and pass to Begin()
if (sheet_editor_card.Begin(&show_sheet_editor_)) { if (sheet_editor_card.Begin(card_manager.GetVisibilityFlag("graphics.sheet_editor"))) {
status_ = UpdateGfxEdit(); status_ = UpdateGfxEdit();
} sheet_editor_card.End();
sheet_editor_card.End(); // ALWAYS call End after Begin
} }
if (show_sheet_browser_) { if (sheet_browser_card.Begin(card_manager.GetVisibilityFlag("graphics.sheet_browser"))) {
if (sheet_browser_card.Begin(&show_sheet_browser_)) { if (asset_browser_.Initialized == false) {
if (asset_browser_.Initialized == false) { asset_browser_.Initialize(gfx::Arena::Get().gfx_sheets());
asset_browser_.Initialize(gfx::Arena::Get().gfx_sheets());
}
asset_browser_.Draw(gfx::Arena::Get().gfx_sheets());
} }
sheet_browser_card.End(); // ALWAYS call End after Begin asset_browser_.Draw(gfx::Arena::Get().gfx_sheets());
sheet_browser_card.End();
} }
if (show_player_animations_) { if (player_anims_card.Begin(card_manager.GetVisibilityFlag("graphics.player_animations"))) {
if (player_anims_card.Begin(&show_player_animations_)) { status_ = UpdateLinkGfxView();
status_ = UpdateLinkGfxView(); player_anims_card.End();
}
player_anims_card.End(); // ALWAYS call End after Begin
} }
if (show_prototype_viewer_) { if (prototype_card.Begin(card_manager.GetVisibilityFlag("graphics.prototype_viewer"))) {
if (prototype_card.Begin(&show_prototype_viewer_)) { status_ = UpdateScadView();
status_ = UpdateScadView(); prototype_card.End();
}
prototype_card.End(); // ALWAYS call End after Begin
} }
CLEAR_AND_RETURN_STATUS(status_) CLEAR_AND_RETURN_STATUS(status_)
return absl::OkStatus(); return absl::OkStatus();
} }
void GraphicsEditor::DrawToolset() {
// Sidebar is now drawn by EditorManager for card-based editors
// This method kept for compatibility but sidebar handles card toggles
}
absl::Status GraphicsEditor::UpdateGfxEdit() { absl::Status GraphicsEditor::UpdateGfxEdit() {
if (ImGui::BeginTable("##GfxEditTable", 3, kGfxEditTableFlags, if (ImGui::BeginTable("##GfxEditTable", 3, kGfxEditTableFlags,
ImVec2(0, 0))) { ImVec2(0, 0))) {
@@ -606,8 +571,6 @@ absl::Status GraphicsEditor::UpdateLinkGfxView() {
} }
absl::Status GraphicsEditor::UpdateScadView() { absl::Status GraphicsEditor::UpdateScadView() {
DrawToolset();
if (open_memory_editor_) { if (open_memory_editor_) {
ImGui::Begin("Memory Editor", &open_memory_editor_); ImGui::Begin("Memory Editor", &open_memory_editor_);
RETURN_IF_ERROR(DrawMemoryEditor()) RETURN_IF_ERROR(DrawMemoryEditor())

View File

@@ -107,7 +107,6 @@ class GraphicsEditor : public Editor {
absl::Status DrawTilemapImport(); absl::Status DrawTilemapImport();
// Other Functions // Other Functions
void DrawToolset();
absl::Status DrawPaletteControls(); absl::Status DrawPaletteControls();
absl::Status DrawClipboardImport(); absl::Status DrawClipboardImport();
absl::Status DrawExperimentalFeatures(); absl::Status DrawExperimentalFeatures();
@@ -117,12 +116,7 @@ class GraphicsEditor : public Editor {
absl::Status DecompressSuperDonkey(); absl::Status DecompressSuperDonkey();
// Member Variables // Member Variables
// Card visibility - ALL FALSE by default to prevent crash on ROM load // Card visibility managed by EditorCardManager
// Cards only shown when user explicitly opens them via View menu or shortcuts
bool show_sheet_editor_ = false;
bool show_sheet_browser_ = false;
bool show_player_animations_ = false;
bool show_prototype_viewer_ = false;
ImVec4 current_color_; ImVec4 current_color_;
uint16_t current_sheet_ = 0; uint16_t current_sheet_ = 0;

View File

@@ -30,58 +30,26 @@ namespace editor {
constexpr uint32_t kRedPen = 0xFF0000FF; constexpr uint32_t kRedPen = 0xFF0000FF;
void ScreenEditor::Initialize() { void ScreenEditor::Initialize() {
// Register cards with EditorCardManager during initialization (once)
auto& card_manager = gui::EditorCardManager::Get(); auto& card_manager = gui::EditorCardManager::Get();
card_manager.RegisterCard({ card_manager.RegisterCard({.card_id = "screen.dungeon_maps", .display_name = "Dungeon Maps",
.card_id = "screen.dungeon_maps", .icon = ICON_MD_MAP, .category = "Screen",
.display_name = "Dungeon Maps", .shortcut_hint = "Alt+1", .priority = 10});
.icon = ICON_MD_MAP, card_manager.RegisterCard({.card_id = "screen.inventory_menu", .display_name = "Inventory Menu",
.category = "Screen", .icon = ICON_MD_INVENTORY, .category = "Screen",
.shortcut_hint = "Alt+1", .shortcut_hint = "Alt+2", .priority = 20});
.visibility_flag = &show_dungeon_maps_, card_manager.RegisterCard({.card_id = "screen.overworld_map", .display_name = "Overworld Map",
.priority = 10 .icon = ICON_MD_PUBLIC, .category = "Screen",
}); .shortcut_hint = "Alt+3", .priority = 30});
card_manager.RegisterCard({.card_id = "screen.title_screen", .display_name = "Title Screen",
.icon = ICON_MD_TITLE, .category = "Screen",
.shortcut_hint = "Alt+4", .priority = 40});
card_manager.RegisterCard({.card_id = "screen.naming_screen", .display_name = "Naming Screen",
.icon = ICON_MD_EDIT, .category = "Screen",
.shortcut_hint = "Alt+5", .priority = 50});
card_manager.RegisterCard({ // Show title screen by default
.card_id = "screen.inventory_menu", card_manager.ShowCard("screen.title_screen");
.display_name = "Inventory Menu",
.icon = ICON_MD_INVENTORY,
.category = "Screen",
.shortcut_hint = "Alt+2",
.visibility_flag = &show_inventory_menu_,
.priority = 20
});
card_manager.RegisterCard({
.card_id = "screen.overworld_map",
.display_name = "Overworld Map",
.icon = ICON_MD_PUBLIC,
.category = "Screen",
.shortcut_hint = "Alt+3",
.visibility_flag = &show_overworld_map_,
.priority = 30
});
card_manager.RegisterCard({
.card_id = "screen.title_screen",
.display_name = "Title Screen",
.icon = ICON_MD_TITLE,
.category = "Screen",
.shortcut_hint = "Alt+4",
.visibility_flag = &show_title_screen_,
.priority = 40
});
card_manager.RegisterCard({
.card_id = "screen.naming_screen",
.display_name = "Naming Screen",
.icon = ICON_MD_EDIT,
.category = "Screen",
.shortcut_hint = "Alt+5",
.visibility_flag = &show_naming_screen_,
.priority = 50
});
} }
absl::Status ScreenEditor::Load() { absl::Status ScreenEditor::Load() {
@@ -119,45 +87,40 @@ absl::Status ScreenEditor::Load() {
} }
absl::Status ScreenEditor::Update() { absl::Status ScreenEditor::Update() {
DrawToolset(); auto& card_manager = gui::EditorCardManager::Get();
gui::VerticalSpacing(2.0f);
// Create session-aware cards (non-static for multi-session support) static gui::EditorCard dungeon_maps_card("Dungeon Maps", ICON_MD_MAP);
gui::EditorCard dungeon_maps_card(MakeCardTitle("Dungeon Maps").c_str(), ICON_MD_MAP); static gui::EditorCard inventory_menu_card("Inventory Menu", ICON_MD_INVENTORY);
gui::EditorCard inventory_menu_card(MakeCardTitle("Inventory Menu").c_str(), ICON_MD_INVENTORY); static gui::EditorCard overworld_map_card("Overworld Map", ICON_MD_PUBLIC);
gui::EditorCard overworld_map_card(MakeCardTitle("Overworld Map").c_str(), ICON_MD_PUBLIC); static gui::EditorCard title_screen_card("Title Screen", ICON_MD_TITLE);
gui::EditorCard title_screen_card(MakeCardTitle("Title Screen").c_str(), ICON_MD_TITLE); static gui::EditorCard naming_screen_card("Naming Screen", ICON_MD_EDIT_ATTRIBUTES);
gui::EditorCard naming_screen_card(MakeCardTitle("Naming Screen").c_str(), ICON_MD_EDIT_ATTRIBUTES);
if (show_dungeon_maps_) { dungeon_maps_card.SetDefaultSize(800, 600);
if (dungeon_maps_card.Begin(&show_dungeon_maps_)) { inventory_menu_card.SetDefaultSize(800, 600);
DrawDungeonMapsEditor(); overworld_map_card.SetDefaultSize(600, 500);
} title_screen_card.SetDefaultSize(600, 500);
dungeon_maps_card.End(); // ALWAYS call End after Begin naming_screen_card.SetDefaultSize(500, 400);
// Get visibility flags from card manager and pass to Begin()
if (dungeon_maps_card.Begin(card_manager.GetVisibilityFlag("screen.dungeon_maps"))) {
DrawDungeonMapsEditor();
dungeon_maps_card.End();
} }
if (show_inventory_menu_) { if (inventory_menu_card.Begin(card_manager.GetVisibilityFlag("screen.inventory_menu"))) {
if (inventory_menu_card.Begin(&show_inventory_menu_)) { DrawInventoryMenuEditor();
DrawInventoryMenuEditor(); inventory_menu_card.End();
}
inventory_menu_card.End(); // ALWAYS call End after Begin
} }
if (show_overworld_map_) { if (overworld_map_card.Begin(card_manager.GetVisibilityFlag("screen.overworld_map"))) {
if (overworld_map_card.Begin(&show_overworld_map_)) { DrawOverworldMapEditor();
DrawOverworldMapEditor(); overworld_map_card.End();
}
overworld_map_card.End(); // ALWAYS call End after Begin
} }
if (show_title_screen_) { if (title_screen_card.Begin(card_manager.GetVisibilityFlag("screen.title_screen"))) {
if (title_screen_card.Begin(&show_title_screen_)) { DrawTitleScreenEditor();
DrawTitleScreenEditor(); title_screen_card.End();
}
title_screen_card.End(); // ALWAYS call End after Begin
} }
if (show_naming_screen_) { if (naming_screen_card.Begin(card_manager.GetVisibilityFlag("screen.naming_screen"))) {
if (naming_screen_card.Begin(&show_naming_screen_)) { DrawNamingScreenEditor();
DrawNamingScreenEditor(); naming_screen_card.End();
}
naming_screen_card.End(); // ALWAYS call End after Begin
} }
return status_; return status_;

View File

@@ -78,14 +78,6 @@ class ScreenEditor : public Editor {
EditingMode current_mode_ = EditingMode::DRAW; EditingMode current_mode_ = EditingMode::DRAW;
// Card visibility - ALL FALSE by default to prevent crash on ROM load
// Cards only shown when user explicitly opens them via View menu or shortcuts
bool show_dungeon_maps_ = false;
bool show_inventory_menu_ = false;
bool show_overworld_map_ = false;
bool show_title_screen_ = false;
bool show_naming_screen_ = false;
bool binary_gfx_loaded_ = false; bool binary_gfx_loaded_ = false;
uint8_t selected_room = 0; uint8_t selected_room = 0;

View File

@@ -14,6 +14,7 @@
#include "app/gfx/snes_tile.h" #include "app/gfx/snes_tile.h"
#include "app/gui/canvas.h" #include "app/gui/canvas.h"
#include "app/gui/style.h" #include "app/gui/style.h"
#include "app/gui/icons.h"
#include "app/rom.h" #include "app/rom.h"
#include "app/gui/input.h" #include "app/gui/input.h"
#include "imgui.h" #include "imgui.h"
@@ -61,7 +62,7 @@ constexpr ImGuiTableFlags kMessageTableFlags = ImGuiTableFlags_Hideable |
ImGuiTableFlags_Resizable; ImGuiTableFlags_Resizable;
void MessageEditor::Initialize() { void MessageEditor::Initialize() {
// Register cards with EditorCardManager // Register cards with EditorCardManager (using centralized visibility)
auto& card_manager = gui::EditorCardManager::Get(); auto& card_manager = gui::EditorCardManager::Get();
card_manager.RegisterCard({ card_manager.RegisterCard({
@@ -69,7 +70,6 @@ void MessageEditor::Initialize() {
.display_name = "Message List", .display_name = "Message List",
.icon = ICON_MD_LIST, .icon = ICON_MD_LIST,
.category = "Message", .category = "Message",
.visibility_flag = &show_message_list_,
.priority = 10 .priority = 10
}); });
@@ -78,7 +78,6 @@ void MessageEditor::Initialize() {
.display_name = "Message Editor", .display_name = "Message Editor",
.icon = ICON_MD_EDIT, .icon = ICON_MD_EDIT,
.category = "Message", .category = "Message",
.visibility_flag = &show_message_editor_,
.priority = 20 .priority = 20
}); });
@@ -87,7 +86,6 @@ void MessageEditor::Initialize() {
.display_name = "Font Atlas", .display_name = "Font Atlas",
.icon = ICON_MD_FONT_DOWNLOAD, .icon = ICON_MD_FONT_DOWNLOAD,
.category = "Message", .category = "Message",
.visibility_flag = &show_font_atlas_,
.priority = 30 .priority = 30
}); });
@@ -96,10 +94,12 @@ void MessageEditor::Initialize() {
.display_name = "Dictionary", .display_name = "Dictionary",
.icon = ICON_MD_BOOK, .icon = ICON_MD_BOOK,
.category = "Message", .category = "Message",
.visibility_flag = &show_dictionary_,
.priority = 40 .priority = 40
}); });
// Show message list by default
card_manager.ShowCard("message.message_list");
for (int i = 0; i < kWidthArraySize; i++) { for (int i = 0; i < kWidthArraySize; i++) {
message_preview_.width_array[i] = rom()->data()[kCharactersWidth + i]; message_preview_.width_array[i] = rom()->data()[kCharactersWidth + i];
} }
@@ -143,30 +143,51 @@ absl::Status MessageEditor::Load() {
} }
absl::Status MessageEditor::Update() { absl::Status MessageEditor::Update() {
if (BeginTable("##MessageEditor", 4, kMessageTableFlags)) { auto& card_manager = gui::EditorCardManager::Get();
TableSetupColumn("List");
TableSetupColumn("Contents"); // Message List Card
TableSetupColumn("Font Atlas"); if (card_manager.IsCardVisible("message.message_list")) {
TableSetupColumn("Commands"); static gui::EditorCard list_card("Message List", ICON_MD_LIST);
TableHeadersRow(); list_card.SetDefaultSize(400, 600);
if (list_card.Begin()) {
TableNextColumn(); DrawMessageList();
DrawMessageList(); list_card.End();
}
TableNextColumn();
DrawCurrentMessage();
TableNextColumn();
DrawFontAtlas();
DrawExpandedMessageSettings();
TableNextColumn();
DrawTextCommands();
DrawSpecialCharacters();
DrawDictionary();
EndTable();
} }
// Message Editor Card
if (card_manager.IsCardVisible("message.message_editor")) {
static gui::EditorCard editor_card("Message Editor", ICON_MD_EDIT);
editor_card.SetDefaultSize(500, 600);
if (editor_card.Begin()) {
DrawCurrentMessage();
editor_card.End();
}
}
// Font Atlas Card
if (card_manager.IsCardVisible("message.font_atlas")) {
static gui::EditorCard font_card("Font Atlas", ICON_MD_FONT_DOWNLOAD);
font_card.SetDefaultSize(400, 500);
if (font_card.Begin()) {
DrawFontAtlas();
DrawExpandedMessageSettings();
font_card.End();
}
}
// Dictionary Card
if (card_manager.IsCardVisible("message.dictionary")) {
static gui::EditorCard dict_card("Dictionary", ICON_MD_BOOK);
dict_card.SetDefaultSize(400, 500);
if (dict_card.Begin()) {
DrawTextCommands();
DrawSpecialCharacters();
DrawDictionary();
dict_card.End();
}
}
return absl::OkStatus(); return absl::OkStatus();
} }

View File

@@ -10,6 +10,7 @@
#include "app/editor/message/message_data.h" #include "app/editor/message/message_data.h"
#include "app/editor/message/message_preview.h" #include "app/editor/message/message_preview.h"
#include "app/gui/editor_card_manager.h" #include "app/gui/editor_card_manager.h"
#include "app/gui/editor_layout.h"
#include "app/gfx/bitmap.h" #include "app/gfx/bitmap.h"
#include "app/gui/canvas.h" #include "app/gui/canvas.h"
#include "app/gui/style.h" #include "app/gui/style.h"

View File

@@ -51,6 +51,17 @@ void OverworldEditor::Initialize() {
// Register cards with EditorCardManager // Register cards with EditorCardManager
auto& card_manager = gui::EditorCardManager::Get(); auto& card_manager = gui::EditorCardManager::Get();
// Register Overworld Canvas (main canvas card with toolset)
card_manager.RegisterCard({
.card_id = "overworld.canvas",
.display_name = "Overworld Canvas",
.icon = ICON_MD_MAP,
.category = "Overworld",
.shortcut_hint = "Ctrl+Shift+O",
.visibility_flag = &show_overworld_canvas_,
.priority = 5 // Show first, most important
});
card_manager.RegisterCard({ card_manager.RegisterCard({
.card_id = "overworld.tile16_selector", .card_id = "overworld.tile16_selector",
.display_name = "Tile16 Selector", .display_name = "Tile16 Selector",
@@ -193,11 +204,8 @@ absl::Status OverworldEditor::Update() {
return status_; return status_;
} }
// Modern layout - no tabs, just toolbar + canvas + floating cards
DrawToolset();
gui::VerticalSpacing(2.0f);
// Create session-aware cards (non-static for multi-session support) // Create session-aware cards (non-static for multi-session support)
gui::EditorCard overworld_canvas_card(MakeCardTitle("Overworld Canvas").c_str(), ICON_MD_PUBLIC);
gui::EditorCard tile16_card(MakeCardTitle("Tile16 Selector").c_str(), ICON_MD_GRID_3X3); gui::EditorCard tile16_card(MakeCardTitle("Tile16 Selector").c_str(), ICON_MD_GRID_3X3);
gui::EditorCard tile8_card(MakeCardTitle("Tile8 Selector").c_str(), ICON_MD_GRID_4X4); gui::EditorCard tile8_card(MakeCardTitle("Tile8 Selector").c_str(), ICON_MD_GRID_4X4);
gui::EditorCard area_gfx_card(MakeCardTitle("Area Graphics").c_str(), ICON_MD_IMAGE); gui::EditorCard area_gfx_card(MakeCardTitle("Area Graphics").c_str(), ICON_MD_IMAGE);
@@ -211,6 +219,9 @@ absl::Status OverworldEditor::Update() {
static bool cards_configured = false; static bool cards_configured = false;
if (!cards_configured) { if (!cards_configured) {
// Position cards for optimal workflow // Position cards for optimal workflow
overworld_canvas_card.SetDefaultSize(1000, 700);
overworld_canvas_card.SetPosition(gui::EditorCard::Position::Floating);
tile16_card.SetDefaultSize(300, 600); tile16_card.SetDefaultSize(300, 600);
tile16_card.SetPosition(gui::EditorCard::Position::Right); tile16_card.SetPosition(gui::EditorCard::Position::Right);
@@ -239,7 +250,13 @@ absl::Status OverworldEditor::Update() {
} }
// Main canvas (full width when cards are docked) // Main canvas (full width when cards are docked)
DrawOverworldCanvas(); if (show_overworld_canvas_) {
if (overworld_canvas_card.Begin(&show_overworld_canvas_)) {
DrawToolset();
DrawOverworldCanvas();
}
overworld_canvas_card.End(); // ALWAYS call End after Begin
}
// Floating tile selector cards (4 tabs converted to separate cards) // Floating tile selector cards (4 tabs converted to separate cards)
if (show_tile16_selector_) { if (show_tile16_selector_) {

View File

@@ -296,6 +296,7 @@ class OverworldEditor : public Editor, public gfx::GfxContext {
bool show_overlay_preview_ = false; bool show_overlay_preview_ = false;
// Card visibility states - Start hidden to prevent crash // Card visibility states - Start hidden to prevent crash
bool show_overworld_canvas_ = true;
bool show_tile16_selector_ = false; bool show_tile16_selector_ = false;
bool show_tile8_selector_ = false; bool show_tile8_selector_ = false;
bool show_area_gfx_ = false; bool show_area_gfx_ = false;

View File

@@ -295,6 +295,9 @@ void PaletteEditor::Initialize() {
.visibility_flag = &show_custom_palette_, .visibility_flag = &show_custom_palette_,
.priority = 80 .priority = 80
}); });
// Show control panel by default when Palette Editor is activated
show_control_panel_ = true;
} }
absl::Status PaletteEditor::Load() { absl::Status PaletteEditor::Load() {

View File

@@ -25,28 +25,17 @@ using ImGui::TableSetupColumn;
using ImGui::Text; using ImGui::Text;
void SpriteEditor::Initialize() { void SpriteEditor::Initialize() {
// Register cards with EditorCardManager during initialization (once)
auto& card_manager = gui::EditorCardManager::Get(); auto& card_manager = gui::EditorCardManager::Get();
card_manager.RegisterCard({ card_manager.RegisterCard({.card_id = "sprite.vanilla_editor", .display_name = "Vanilla Sprites",
.card_id = "sprite.vanilla_editor", .icon = ICON_MD_SMART_TOY, .category = "Sprite",
.display_name = "Vanilla Sprites", .shortcut_hint = "Alt+Shift+1", .priority = 10});
.icon = ICON_MD_SMART_TOY, card_manager.RegisterCard({.card_id = "sprite.custom_editor", .display_name = "Custom Sprites",
.category = "Sprite", .icon = ICON_MD_ADD_CIRCLE, .category = "Sprite",
.shortcut_hint = "Alt+Shift+1", .shortcut_hint = "Alt+Shift+2", .priority = 20});
.visibility_flag = &show_vanilla_editor_,
.priority = 10
});
card_manager.RegisterCard({ // Show vanilla editor by default
.card_id = "sprite.custom_editor", card_manager.ShowCard("sprite.vanilla_editor");
.display_name = "Custom Sprites",
.icon = ICON_MD_ADD_CIRCLE,
.category = "Sprite",
.shortcut_hint = "Alt+Shift+2",
.visibility_flag = &show_custom_editor_,
.priority = 20
});
} }
absl::Status SpriteEditor::Load() { absl::Status SpriteEditor::Load() {
@@ -56,29 +45,26 @@ absl::Status SpriteEditor::Load() {
absl::Status SpriteEditor::Update() { absl::Status SpriteEditor::Update() {
if (rom()->is_loaded() && !sheets_loaded_) { if (rom()->is_loaded() && !sheets_loaded_) {
// Load the values for current_sheets_ array
sheets_loaded_ = true; sheets_loaded_ = true;
} }
DrawToolset(); auto& card_manager = gui::EditorCardManager::Get();
gui::VerticalSpacing(2.0f);
// Create session-aware cards (non-static for multi-session support) static gui::EditorCard vanilla_card("Vanilla Sprites", ICON_MD_SMART_TOY);
gui::EditorCard vanilla_card(MakeCardTitle("Vanilla Sprites").c_str(), ICON_MD_PEST_CONTROL_RODENT); static gui::EditorCard custom_card("Custom Sprites", ICON_MD_ADD_CIRCLE);
gui::EditorCard custom_card(MakeCardTitle("Custom Sprites").c_str(), ICON_MD_ADD_MODERATOR);
if (show_vanilla_editor_) { vanilla_card.SetDefaultSize(900, 700);
if (vanilla_card.Begin(&show_vanilla_editor_)) { custom_card.SetDefaultSize(800, 600);
DrawVanillaSpriteEditor();
} // Get visibility flags from card manager and pass to Begin()
vanilla_card.End(); // ALWAYS call End after Begin if (vanilla_card.Begin(card_manager.GetVisibilityFlag("sprite.vanilla_editor"))) {
DrawVanillaSpriteEditor();
vanilla_card.End();
} }
if (show_custom_editor_) { if (custom_card.Begin(card_manager.GetVisibilityFlag("sprite.custom_editor"))) {
if (custom_card.Begin(&show_custom_editor_)) { DrawCustomSprites();
DrawCustomSprites(); custom_card.End();
}
custom_card.End(); // ALWAYS call End after Begin
} }
return status_.ok() ? absl::OkStatus() : status_; return status_.ok() ? absl::OkStatus() : status_;

View File

@@ -37,8 +37,8 @@ constexpr ImGuiTableFlags kSpriteTableFlags =
*/ */
class SpriteEditor : public Editor { class SpriteEditor : public Editor {
public: public:
explicit SpriteEditor(Rom* rom = nullptr) : rom_(rom) { explicit SpriteEditor(Rom* rom = nullptr) : rom_(rom) {
type_ = EditorType::kSprite; type_ = EditorType::kSprite;
} }
void Initialize() override; void Initialize() override;
@@ -51,10 +51,10 @@ class SpriteEditor : public Editor {
absl::Status Paste() override { return absl::UnimplementedError("Paste"); } absl::Status Paste() override { return absl::UnimplementedError("Paste"); }
absl::Status Find() override { return absl::UnimplementedError("Find"); } absl::Status Find() override { return absl::UnimplementedError("Find"); }
absl::Status Save() override { return absl::UnimplementedError("Save"); } absl::Status Save() override { return absl::UnimplementedError("Save"); }
// Set the ROM pointer // Set the ROM pointer
void set_rom(Rom* rom) { rom_ = rom; } void set_rom(Rom* rom) { rom_ = rom; }
// Get the ROM pointer // Get the ROM pointer
Rom* rom() const { return rom_; } Rom* rom() const { return rom_; }
@@ -84,11 +84,6 @@ class SpriteEditor : public Editor {
void DrawAnimationFrames(); void DrawAnimationFrames();
void DrawToolset(); void DrawToolset();
// Card visibility - ALL FALSE by default to prevent crash on ROM load
// Cards only shown when user explicitly opens them via View menu or shortcuts
bool show_vanilla_editor_ = false;
bool show_custom_editor_ = false;
ImVector<int> active_sprites_; /**< Active sprites. */ ImVector<int> active_sprites_; /**< Active sprites. */
int current_sprite_id_; /**< Current sprite ID. */ int current_sprite_id_; /**< Current sprite ID. */

View File

@@ -140,25 +140,37 @@ std::vector<ImGuiKey> ParseShortcut(const std::string& shortcut) {
void ExecuteShortcuts(const ShortcutManager& shortcut_manager) { void ExecuteShortcuts(const ShortcutManager& shortcut_manager) {
// Check for keyboard shortcuts using the shortcut manager // Check for keyboard shortcuts using the shortcut manager
for (const auto& shortcut : shortcut_manager.GetShortcuts()) { for (const auto& shortcut : shortcut_manager.GetShortcuts()) {
bool keys_pressed = true; bool modifiers_held = true;
// Check for all the keys in the shortcut bool key_pressed = false;
ImGuiKey main_key = ImGuiKey_None;
// Separate modifiers from main key
for (const auto& key : shortcut.second.keys) { for (const auto& key : shortcut.second.keys) {
if (key == ImGuiMod_Ctrl) { if (key == ImGuiMod_Ctrl || key == ImGuiMod_Alt ||
keys_pressed &= ImGui::GetIO().KeyCtrl; key == ImGuiMod_Shift || key == ImGuiMod_Super) {
} else if (key == ImGuiMod_Alt) { // Check if modifier is held
keys_pressed &= ImGui::GetIO().KeyAlt; if (key == ImGuiMod_Ctrl) {
} else if (key == ImGuiMod_Shift) { modifiers_held &= ImGui::GetIO().KeyCtrl;
keys_pressed &= ImGui::GetIO().KeyShift; } else if (key == ImGuiMod_Alt) {
} else if (key == ImGuiMod_Super) { modifiers_held &= ImGui::GetIO().KeyAlt;
keys_pressed &= ImGui::GetIO().KeySuper; } else if (key == ImGuiMod_Shift) {
modifiers_held &= ImGui::GetIO().KeyShift;
} else if (key == ImGuiMod_Super) {
modifiers_held &= ImGui::GetIO().KeySuper;
}
} else { } else {
keys_pressed &= ImGui::IsKeyDown(key); // This is the main key - use IsKeyPressed for single trigger
} main_key = key;
if (!keys_pressed) {
break;
} }
} }
if (keys_pressed) {
// Check if main key was pressed (not just held)
if (main_key != ImGuiKey_None) {
key_pressed = ImGui::IsKeyPressed(main_key, false); // false = no repeat
}
// Execute if modifiers held and key pressed
if (modifiers_held && key_pressed && shortcut.second.callback) {
shortcut.second.callback(); shortcut.second.callback();
} }
} }

View File

@@ -49,46 +49,7 @@ void Emulator::Initialize(gfx::IRenderer* renderer, const std::vector<uint8_t>&
renderer_ = renderer; renderer_ = renderer;
rom_data_ = rom_data; rom_data_ = rom_data;
// Register emulator cards with EditorCardManager // Cards are registered in EditorManager::Initialize() to avoid duplication
auto& card_manager = gui::EditorCardManager::Get();
card_manager.RegisterCard({
.card_id = "emulator.cpu_debugger",
.display_name = "CPU Debugger",
.icon = ICON_MD_BUG_REPORT,
.category = "Emulator",
.visibility_flag = &show_cpu_debugger_,
.priority = 10
});
card_manager.RegisterCard({
.card_id = "emulator.memory_viewer",
.display_name = "Memory Viewer",
.icon = ICON_MD_MEMORY,
.category = "Emulator",
.visibility_flag = &show_memory_viewer_,
.priority = 20
});
card_manager.RegisterCard({
.card_id = "emulator.ppu_viewer",
.display_name = "PPU Viewer",
.icon = ICON_MD_GRID_VIEW,
.category = "Emulator",
.visibility_flag = &show_ppu_viewer_,
.priority = 30
});
card_manager.RegisterCard({
.card_id = "emulator.audio_mixer",
.display_name = "Audio Mixer",
.icon = ICON_MD_AUDIO_FILE,
.category = "Emulator",
.visibility_flag = &show_audio_mixer_,
.priority = 40
});
printf("[Emulator] Registered 4 cards with EditorCardManager\n");
// Reset state for new ROM // Reset state for new ROM
running_ = false; running_ = false;
@@ -211,7 +172,6 @@ void Emulator::Run(Rom* rom) {
running_ = true; running_ = true;
} }
RenderNavBar();
// Auto-pause emulator during window resize to prevent crashes // Auto-pause emulator during window resize to prevent crashes
// MODERN APPROACH: Only pause on actual window resize, not focus loss // MODERN APPROACH: Only pause on actual window resize, not focus loss
@@ -345,142 +305,75 @@ void Emulator::Run(Rom* rom) {
} }
void Emulator::RenderEmulatorInterface() { void Emulator::RenderEmulatorInterface() {
// Apply modern theming with safety checks
try { try {
auto& theme_manager = gui::ThemeManager::Get(); auto& card_manager = gui::EditorCardManager::Get();
const auto& theme = theme_manager.GetCurrentTheme();
// Modern EditorCard-based layout - modular and flexible static gui::EditorCard cpu_card("CPU Debugger", ICON_MD_MEMORY);
static bool show_cpu_debugger_ = true; static gui::EditorCard ppu_card("PPU Viewer", ICON_MD_VIDEOGAME_ASSET);
static bool show_ppu_display_ = true; static gui::EditorCard memory_card("Memory Viewer", ICON_MD_MEMORY);
static bool show_memory_viewer_ = false; static gui::EditorCard breakpoints_card("Breakpoints", ICON_MD_STOP);
static bool show_breakpoints_ = false; static gui::EditorCard performance_card("Performance", ICON_MD_SPEED);
static bool show_performance_ = true; static gui::EditorCard ai_card("AI Agent", ICON_MD_SMART_TOY);
static bool show_ai_agent_ = false; static gui::EditorCard save_states_card("Save States", ICON_MD_SAVE);
static bool show_save_states_ = false; static gui::EditorCard keyboard_card("Keyboard Config", ICON_MD_KEYBOARD);
static bool show_keyboard_config_ = false; static gui::EditorCard apu_card("APU Debugger", ICON_MD_MUSIC_NOTE);
static bool show_apu_debugger_ = true; static gui::EditorCard audio_card("Audio Mixer", ICON_MD_AUDIO_FILE);
// Create session-aware cards cpu_card.SetDefaultSize(400, 500);
static gui::EditorCard cpu_card(ICON_MD_MEMORY " CPU Debugger", ICON_MD_MEMORY); ppu_card.SetDefaultSize(550, 520);
static gui::EditorCard ppu_card(ICON_MD_VIDEOGAME_ASSET " PPU Display", memory_card.SetDefaultSize(800, 600);
ICON_MD_VIDEOGAME_ASSET); breakpoints_card.SetDefaultSize(400, 350);
static gui::EditorCard memory_card(ICON_MD_DATA_ARRAY " Memory Viewer", performance_card.SetDefaultSize(350, 300);
ICON_MD_DATA_ARRAY);
static gui::EditorCard breakpoints_card(ICON_MD_BUG_REPORT " Breakpoints",
ICON_MD_BUG_REPORT);
static gui::EditorCard performance_card(ICON_MD_SPEED " Performance",
ICON_MD_SPEED);
static gui::EditorCard ai_card(ICON_MD_SMART_TOY " AI Agent", ICON_MD_SMART_TOY);
static gui::EditorCard save_states_card(ICON_MD_SAVE " Save States", ICON_MD_SAVE);
static gui::EditorCard keyboard_card(ICON_MD_KEYBOARD " Keyboard Config",
ICON_MD_KEYBOARD);
static gui::EditorCard apu_debug_card(ICON_MD_MUSIC_NOTE " APU Debugger",
ICON_MD_MUSIC_NOTE);
// Configure default positions if (card_manager.IsCardVisible("emulator.cpu_debugger") && cpu_card.Begin()) {
static bool cards_configured = false; RenderModernCpuDebugger();
if (!cards_configured) {
cpu_card.SetDefaultSize(400, 500);
cpu_card.SetPosition(gui::EditorCard::Position::Right);
ppu_card.SetDefaultSize(550, 520);
ppu_card.SetPosition(gui::EditorCard::Position::Floating);
memory_card.SetDefaultSize(800, 600);
memory_card.SetPosition(gui::EditorCard::Position::Floating);
breakpoints_card.SetDefaultSize(400, 350);
breakpoints_card.SetPosition(gui::EditorCard::Position::Right);
performance_card.SetDefaultSize(350, 300);
performance_card.SetPosition(gui::EditorCard::Position::Bottom);
ai_card.SetDefaultSize(500, 450);
ai_card.SetPosition(gui::EditorCard::Position::Floating);
save_states_card.SetDefaultSize(400, 300);
save_states_card.SetPosition(gui::EditorCard::Position::Floating);
keyboard_card.SetDefaultSize(450, 400);
keyboard_card.SetPosition(gui::EditorCard::Position::Floating);
apu_debug_card.SetDefaultSize(500, 400);
apu_debug_card.SetPosition(gui::EditorCard::Position::Floating);
cards_configured = true;
}
// CPU Debugger Card
if (show_cpu_debugger_) {
if (cpu_card.Begin(&show_cpu_debugger_)) {
RenderModernCpuDebugger();
}
cpu_card.End(); cpu_card.End();
} }
// PPU Display Card if (card_manager.IsCardVisible("emulator.ppu_viewer") && ppu_card.Begin()) {
if (show_ppu_display_) { RenderNavBar();
if (ppu_card.Begin(&show_ppu_display_)) { RenderSnesPpu();
RenderSnesPpu();
}
ppu_card.End(); ppu_card.End();
} }
// Memory Viewer Card if (card_manager.IsCardVisible("emulator.memory_viewer") && memory_card.Begin()) {
if (show_memory_viewer_) { RenderMemoryViewer();
if (memory_card.Begin(&show_memory_viewer_)) {
RenderMemoryViewer();
}
memory_card.End(); memory_card.End();
} }
// Breakpoints Card if (card_manager.IsCardVisible("emulator.breakpoints") && breakpoints_card.Begin()) {
if (show_breakpoints_) { RenderBreakpointList();
if (breakpoints_card.Begin(&show_breakpoints_)) {
RenderBreakpointList();
}
breakpoints_card.End(); breakpoints_card.End();
} }
// Performance Monitor Card if (card_manager.IsCardVisible("emulator.performance") && performance_card.Begin()) {
if (show_performance_) { RenderPerformanceMonitor();
if (performance_card.Begin(&show_performance_)) {
RenderPerformanceMonitor();
}
performance_card.End(); performance_card.End();
} }
// AI Agent Card if (card_manager.IsCardVisible("emulator.ai_agent") && ai_card.Begin()) {
if (show_ai_agent_) { RenderAIAgentPanel();
if (ai_card.Begin(&show_ai_agent_)) {
RenderAIAgentPanel();
}
ai_card.End(); ai_card.End();
} }
// Save States Card if (card_manager.IsCardVisible("emulator.save_states") && save_states_card.Begin()) {
if (show_save_states_) { RenderSaveStates();
if (save_states_card.Begin(&show_save_states_)) {
RenderSaveStates();
}
save_states_card.End(); save_states_card.End();
} }
// Keyboard Configuration Card if (card_manager.IsCardVisible("emulator.keyboard_config") && keyboard_card.Begin()) {
if (show_keyboard_config_) { RenderKeyboardConfig();
if (keyboard_card.Begin(&show_keyboard_config_)) {
RenderKeyboardConfig();
}
keyboard_card.End(); keyboard_card.End();
} }
// APU Debugger Card if (card_manager.IsCardVisible("emulator.apu_debugger") && apu_card.Begin()) {
if (show_apu_debugger_) { RenderApuDebugger();
if (apu_debug_card.Begin(&show_apu_debugger_)) { apu_card.End();
RenderApuDebugger(); }
}
apu_debug_card.End(); if (card_manager.IsCardVisible("emulator.audio_mixer") && audio_card.Begin()) {
// RenderAudioMixer();
audio_card.End();
} }
} catch (const std::exception& e) { } catch (const std::exception& e) {

View File

@@ -38,11 +38,7 @@ class Emulator {
void Run(Rom* rom); void Run(Rom* rom);
void Cleanup(); void Cleanup();
// Card visibility for emulator UI panels // Card visibility managed by EditorCardManager
bool& show_cpu_debugger() { return show_cpu_debugger_; }
bool& show_memory_viewer() { return show_memory_viewer_; }
bool& show_ppu_viewer() { return show_ppu_viewer_; }
bool& show_audio_mixer() { return show_audio_mixer_; }
auto snes() -> Snes& { return snes_; } auto snes() -> Snes& { return snes_; }
auto running() const -> bool { return running_; } auto running() const -> bool { return running_; }
@@ -163,11 +159,7 @@ class Emulator {
gfx::IRenderer* renderer_ = nullptr; gfx::IRenderer* renderer_ = nullptr;
void* ppu_texture_ = nullptr; void* ppu_texture_ = nullptr;
// Card visibility states // Card visibility managed by EditorCardManager - no member variables needed!
bool show_cpu_debugger_ = false;
bool show_memory_viewer_ = false;
bool show_ppu_viewer_ = false;
bool show_audio_mixer_ = false;
// Debugger infrastructure // Debugger infrastructure
BreakpointManager breakpoint_manager_; BreakpointManager breakpoint_manager_;

View File

@@ -18,20 +18,23 @@ EditorCardManager& EditorCardManager::Get() {
void EditorCardManager::RegisterCard(const CardInfo& info) { void EditorCardManager::RegisterCard(const CardInfo& info) {
if (info.card_id.empty()) { if (info.card_id.empty()) {
printf("[EditorCardManager] Warning: Attempted to register card with empty ID\n");
return; return;
} }
// Check if already registered to avoid duplicates // Check if already registered to avoid duplicates
if (cards_.find(info.card_id) != cards_.end()) { if (cards_.find(info.card_id) != cards_.end()) {
printf("[EditorCardManager] WARNING: Card '%s' already registered, skipping duplicate\n",
info.card_id.c_str());
return; return;
} }
cards_[info.card_id] = info; CardInfo new_info = info;
printf("[EditorCardManager] Registered card: %s (%s)\n",
info.card_id.c_str(), info.display_name.c_str()); // If no visibility_flag provided, create centralized one
if (!new_info.visibility_flag) {
centralized_visibility_[info.card_id] = false; // Hidden by default
new_info.visibility_flag = &centralized_visibility_[info.card_id];
}
cards_[info.card_id] = new_info;
} }
void EditorCardManager::RegisterCard(const std::string& card_id, void EditorCardManager::RegisterCard(const std::string& card_id,
@@ -161,6 +164,14 @@ bool EditorCardManager::IsCardVisible(const std::string& card_id) const {
return false; return false;
} }
bool* EditorCardManager::GetVisibilityFlag(const std::string& card_id) {
auto it = cards_.find(card_id);
if (it != cards_.end()) {
return it->second.visibility_flag;
}
return nullptr;
}
void EditorCardManager::ShowAllCardsInCategory(const std::string& category) { void EditorCardManager::ShowAllCardsInCategory(const std::string& category) {
for (auto& [id, info] : cards_) { for (auto& [id, info] : cards_) {
if (info.category == category && info.visibility_flag) { if (info.category == category && info.visibility_flag) {
@@ -716,13 +727,13 @@ void EditorCardManager::LoadPresetsFromFile() {
} }
void EditorCardManager::SetActiveCategory(const std::string& category) { void EditorCardManager::SetActiveCategory(const std::string& category) {
if (active_category_ != category) { active_category_ = category;
active_category_ = category;
printf("[EditorCardManager] Active category changed to: %s\n", category.c_str());
}
} }
void EditorCardManager::DrawSidebar(const std::string& category) { void EditorCardManager::DrawSidebar(const std::string& category,
const std::vector<std::string>& active_categories,
std::function<void(const std::string&)> on_category_switch,
std::function<void()> on_collapse) {
// Use ThemeManager for consistent theming // Use ThemeManager for consistent theming
const auto& theme = ThemeManager::Get().GetCurrentTheme(); const auto& theme = ThemeManager::Get().GetCurrentTheme();
@@ -737,58 +748,80 @@ void EditorCardManager::DrawSidebar(const std::string& category) {
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoDocking | // Don't allow docking over sidebar
ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollbar |
ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoScrollWithMouse |
ImGuiWindowFlags_NoBringToFrontOnFocus; ImGuiWindowFlags_NoFocusOnAppearing | // Don't steal focus
ImGuiWindowFlags_NoNavFocus; // Don't participate in nav
// Make sidebar more opaque and visible // Make sidebar VERY visible - fully opaque dark background
ImVec4 sidebar_bg = ConvertColorToImVec4(theme.child_bg); ImVec4 sidebar_bg = ImVec4(0.18f, 0.18f, 0.20f, 1.0f); // Dark opaque gray
sidebar_bg.w = 1.0f; // Full opacity ImVec4 sidebar_border = ImVec4(0.4f, 0.4f, 0.45f, 1.0f); // Visible border
ImGui::PushStyleColor(ImGuiCol_WindowBg, sidebar_bg); ImGui::PushStyleColor(ImGuiCol_WindowBg, sidebar_bg);
ImGui::PushStyleColor(ImGuiCol_Border, ConvertColorToImVec4(theme.border)); ImGui::PushStyleColor(ImGuiCol_Border, sidebar_border);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(4.0f, 8.0f)); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(4.0f, 8.0f));
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 6.0f)); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 6.0f));
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 2.0f); // Thicker border
if (ImGui::Begin("##EditorCardSidebar", nullptr, sidebar_flags)) { if (ImGui::Begin("##EditorCardSidebar", nullptr, sidebar_flags)) {
// Category switcher buttons at top (if multiple editors active)
if (active_categories.size() > 1) {
ImVec4 accent = ConvertColorToImVec4(theme.accent);
ImVec4 inactive = ConvertColorToImVec4(theme.button);
for (const auto& cat : active_categories) {
bool is_current = (cat == category);
if (is_current) {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(accent.x, accent.y, accent.z, 0.8f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(accent.x, accent.y, accent.z, 1.0f));
} else {
ImGui::PushStyleColor(ImGuiCol_Button, inactive);
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ConvertColorToImVec4(theme.button_hovered));
}
// Show first letter of category
std::string btn_label = cat.empty() ? "?" : std::string(1, cat[0]);
if (ImGui::Button(btn_label.c_str(), ImVec2(40.0f, 32.0f))) {
// Call callback to switch editor, not just set category
if (on_category_switch) {
on_category_switch(cat);
} else {
SetActiveCategory(cat);
}
}
ImGui::PopStyleColor(2);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("%s Editor\nClick to switch", cat.c_str());
}
}
ImGui::Dummy(ImVec2(0, 2.0f));
ImGui::Separator();
ImGui::Spacing();
}
// Get cards for this category // Get cards for this category
auto cards = GetCardsInCategory(category); auto cards = GetCardsInCategory(category);
// If no cards available, show editor selection instead // Set this category as active when showing cards
if (cards.empty()) { if (!cards.empty()) {
ImGui::PushStyleColor(ImGuiCol_Text, ConvertColorToImVec4(theme.text_disabled));
ImGui::TextWrapped("No cards");
ImGui::PopStyleColor();
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing();
// Show editor selection buttons
ImGui::TextWrapped("Select Editor:");
ImGui::Dummy(ImVec2(0, 4.0f));
// TODO: Add editor selection buttons here
// For now, just show message
ImGui::PushStyleColor(ImGuiCol_Text, ConvertColorToImVec4(theme.text_secondary));
ImGui::TextWrapped("Open an editor to see cards");
ImGui::PopStyleColor();
} else {
// Set this category as active when cards are present
SetActiveCategory(category); SetActiveCategory(category);
} }
// Close All button at top (only if cards exist) // Close All button (only if cards exist)
if (!cards.empty()) { if (!cards.empty()) {
ImVec4 error_color = ConvertColorToImVec4(theme.error); ImVec4 error_color = ConvertColorToImVec4(theme.error);
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4( ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(
error_color.x * 0.6f, error_color.y * 0.6f, error_color.z * 0.6f, 0.7f)); error_color.x * 0.6f, error_color.y * 0.6f, error_color.z * 0.6f, 0.9f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, error_color); ImGui::PushStyleColor(ImGuiCol_ButtonHovered, error_color);
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4( ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(
error_color.x * 1.2f, error_color.y * 1.2f, error_color.z * 1.2f, 1.0f)); error_color.x * 1.2f, error_color.y * 1.2f, error_color.z * 1.2f, 1.0f));
if (ImGui::Button(ICON_MD_CLOSE, ImVec2(40.0f, 40.0f))) { if (ImGui::Button(ICON_MD_CLOSE, ImVec2(40.0f, 36.0f))) {
HideAllCardsInCategory(category); HideAllCardsInCategory(category);
} }
@@ -798,7 +831,25 @@ void EditorCardManager::DrawSidebar(const std::string& category) {
ImGui::SetTooltip("Close All %s Cards", category.c_str()); ImGui::SetTooltip("Close All %s Cards", category.c_str());
} }
ImGui::Dummy(ImVec2(0, 4.0f)); // Show All button
ImVec4 success_color = ConvertColorToImVec4(theme.success);
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(
success_color.x * 0.6f, success_color.y * 0.6f, success_color.z * 0.6f, 0.7f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, success_color);
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(
success_color.x * 1.2f, success_color.y * 1.2f, success_color.z * 1.2f, 1.0f));
if (ImGui::Button(ICON_MD_DONE_ALL, ImVec2(40.0f, 36.0f))) {
ShowAllCardsInCategory(category);
}
ImGui::PopStyleColor(3);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Show All %s Cards", category.c_str());
}
ImGui::Dummy(ImVec2(0, 2.0f));
// Draw card buttons // Draw card buttons
ImVec4 accent_color = ConvertColorToImVec4(theme.accent); ImVec4 accent_color = ConvertColorToImVec4(theme.accent);
@@ -838,6 +889,27 @@ void EditorCardManager::DrawSidebar(const std::string& category) {
ImGui::PopID(); ImGui::PopID();
} }
} // End if (!cards.empty()) } // End if (!cards.empty())
// Collapse sidebar button at bottom
if (on_collapse) {
ImGui::Dummy(ImVec2(0, 10.0f)); // Add some space
ImGui::Separator();
ImGui::Spacing();
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.2f, 0.2f, 0.22f, 0.9f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.3f, 0.3f, 0.32f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.25f, 0.25f, 0.27f, 1.0f));
if (ImGui::Button(ICON_MD_KEYBOARD_ARROW_LEFT, ImVec2(40.0f, 36.0f))) {
on_collapse();
}
ImGui::PopStyleColor(3);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Hide Sidebar\nCtrl+B");
}
}
} }
ImGui::End(); ImGui::End();

View File

@@ -96,6 +96,9 @@ class EditorCardManager {
bool ToggleCard(const std::string& card_id); bool ToggleCard(const std::string& card_id);
bool IsCardVisible(const std::string& card_id) const; bool IsCardVisible(const std::string& card_id) const;
// Get visibility flag pointer (for passing to EditorCard::Begin)
bool* GetVisibilityFlag(const std::string& card_id);
// Batch operations // Batch operations
void ShowAllCardsInCategory(const std::string& category); void ShowAllCardsInCategory(const std::string& category);
void HideAllCardsInCategory(const std::string& category); void HideAllCardsInCategory(const std::string& category);
@@ -111,7 +114,10 @@ class EditorCardManager {
void DrawViewMenuAll(); // Draw all categories as submenus void DrawViewMenuAll(); // Draw all categories as submenus
// VSCode-style sidebar (replaces Toolset) // VSCode-style sidebar (replaces Toolset)
void DrawSidebar(const std::string& category); // Icon-only sidebar for category void DrawSidebar(const std::string& category,
const std::vector<std::string>& active_categories = {},
std::function<void(const std::string&)> on_category_switch = nullptr,
std::function<void()> on_collapse = nullptr);
static constexpr float GetSidebarWidth() { return 48.0f; } static constexpr float GetSidebarWidth() { return 48.0f; }
// Active editor tracking (based on most recently interacted card) // Active editor tracking (based on most recently interacted card)

View File

@@ -248,6 +248,11 @@ void EditorCard::SetPosition(Position pos) {
} }
bool EditorCard::Begin(bool* p_open) { bool EditorCard::Begin(bool* p_open) {
// Check visibility flag first - if provided and false, don't show the card
if (p_open && !*p_open) {
return false;
}
// Handle icon-collapsed state // Handle icon-collapsed state
if (icon_collapsible_ && collapsed_to_icon_) { if (icon_collapsible_ && collapsed_to_icon_) {
DrawFloatingIconButton(); DrawFloatingIconButton();