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() {
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() {

View File

@@ -108,6 +108,10 @@ void DungeonEditorV2::Initialize(gfx::IRenderer* renderer, Rom* rom) {
.visibility_flag = &show_debug_controls_,
.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() {}
@@ -187,32 +191,6 @@ absl::Status DungeonEditorV2::Update() {
// CARD-BASED EDITOR: All windows are independent top-level cards
// 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();
return absl::OkStatus();
@@ -235,73 +213,6 @@ absl::Status DungeonEditorV2::Save() {
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() {
// NO TABLE LAYOUT - All independent dockable EditorCards
// 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
void DrawLayout();
void DrawRoomTab(int room_id);
void DrawToolset();
void DrawRoomMatrixCard();
void DrawRoomsListCard();
void DrawEntrancesListCard();
void DrawRoomGraphicsCard();
void DrawDebugControlsCard();
void DrawControlPanel();
// Texture processing (critical for rendering)
void ProcessDeferredTextures();

View File

@@ -91,17 +91,22 @@ std::string GetEditorName(EditorType type) {
} // namespace
// 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) {
switch (type) {
case EditorType::kDungeon:
case EditorType::kPalette:
case EditorType::kGraphics:
case EditorType::kScreen:
case EditorType::kSprite:
case EditorType::kMessage:
case EditorType::kOverworld:
case EditorType::kDungeon: // ✅ Full card system
case EditorType::kPalette: // ✅ Full card system
case EditorType::kGraphics: // ✅ EditorCard wrappers + Toolset
case EditorType::kScreen: // ✅ EditorCard wrappers + Toolset
case EditorType::kSprite: // ✅ EditorCard wrappers + Toolset
case EditorType::kOverworld: // ✅ Inline EditorCard + Toolset
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;
// Music: Traditional UI - needs wrapper
default:
return false;
}
@@ -127,11 +132,30 @@ std::string EditorManager::GetEditorCategory(EditorType type) {
return "Screen";
case EditorType::kEmulator:
return "Emulator";
case EditorType::kHex:
return "Memory";
case EditorType::kAssembly:
return "Assembly";
default:
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() {
if (!current_editor_) {
return;
@@ -221,6 +245,37 @@ void EditorManager::Initialize(gfx::IRenderer* renderer, const std::string& file
"global.toggle_sidebar",
{ImGuiKey_LeftCtrl, ImGuiKey_B},
[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
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
editor_selection_dialog_.SetSelectionCallback([this](EditorType type) {
editor_selection_dialog_.MarkRecentlyUsed(type);
// 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;
}
SwitchToEditor(type); // Use centralized method
});
// 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(
"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(
"Overworld Editor", {ImGuiKey_1, ImGuiMod_Ctrl},
[this]() { if (current_editor_set_) current_editor_set_->overworld_editor_.toggle_active(); });
"Overworld Editor", {ImGuiKey_LeftCtrl, ImGuiKey_1},
[this]() { SwitchToEditor(EditorType::kOverworld); });
context_.shortcut_manager.RegisterShortcut(
"Dungeon Editor", {ImGuiKey_2, ImGuiMod_Ctrl},
[this]() { if (current_editor_set_) current_editor_set_->dungeon_editor_.toggle_active(); });
"Dungeon Editor", {ImGuiKey_LeftCtrl, ImGuiKey_2},
[this]() { SwitchToEditor(EditorType::kDungeon); });
context_.shortcut_manager.RegisterShortcut(
"Graphics Editor", {ImGuiKey_3, ImGuiMod_Ctrl},
[this]() { if (current_editor_set_) current_editor_set_->graphics_editor_.toggle_active(); });
"Graphics Editor", {ImGuiKey_LeftCtrl, ImGuiKey_3},
[this]() { SwitchToEditor(EditorType::kGraphics); });
context_.shortcut_manager.RegisterShortcut(
"Sprite Editor", {ImGuiKey_4, ImGuiMod_Ctrl},
[this]() { if (current_editor_set_) current_editor_set_->sprite_editor_.toggle_active(); });
"Sprite Editor", {ImGuiKey_LeftCtrl, ImGuiKey_4},
[this]() { SwitchToEditor(EditorType::kSprite); });
context_.shortcut_manager.RegisterShortcut(
"Message Editor", {ImGuiKey_5, ImGuiMod_Ctrl},
[this]() { if (current_editor_set_) current_editor_set_->message_editor_.toggle_active(); });
"Message Editor", {ImGuiKey_LeftCtrl, ImGuiKey_5},
[this]() { SwitchToEditor(EditorType::kMessage); });
context_.shortcut_manager.RegisterShortcut(
"Music Editor", {ImGuiKey_6, ImGuiMod_Ctrl},
[this]() { if (current_editor_set_) current_editor_set_->music_editor_.toggle_active(); });
"Music Editor", {ImGuiKey_LeftCtrl, ImGuiKey_6},
[this]() { SwitchToEditor(EditorType::kMusic); });
context_.shortcut_manager.RegisterShortcut(
"Palette Editor", {ImGuiKey_7, ImGuiMod_Ctrl},
[this]() { if (current_editor_set_) current_editor_set_->palette_editor_.toggle_active(); });
"Palette Editor", {ImGuiKey_LeftCtrl, ImGuiKey_7},
[this]() { SwitchToEditor(EditorType::kPalette); });
context_.shortcut_manager.RegisterShortcut(
"Screen Editor", {ImGuiKey_8, ImGuiMod_Ctrl},
[this]() { if (current_editor_set_) current_editor_set_->screen_editor_.toggle_active(); });
"Screen Editor", {ImGuiKey_LeftCtrl, ImGuiKey_8},
[this]() { SwitchToEditor(EditorType::kScreen); });
context_.shortcut_manager.RegisterShortcut(
"Assembly Editor", {ImGuiKey_9, ImGuiMod_Ctrl},
[this]() { show_asm_editor_ = true; });
"Assembly Editor", {ImGuiKey_LeftCtrl, ImGuiKey_9},
[this]() { SwitchToEditor(EditorType::kAssembly); });
context_.shortcut_manager.RegisterShortcut(
"Settings Editor", {ImGuiKey_0, ImGuiMod_Ctrl},
[this]() { if (current_editor_set_) current_editor_set_->settings_editor_.toggle_active(); });
"Settings Editor", {ImGuiKey_LeftCtrl, ImGuiKey_0},
[this]() { SwitchToEditor(EditorType::kSettings); });
// Editor Selection Dialog shortcut
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_) {
gfx::PerformanceDashboard::Get().Render();
}
@@ -933,6 +934,59 @@ absl::Status EditorManager::Update() {
}
#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();
}
@@ -1112,23 +1166,23 @@ void EditorManager::BuildModernMenu() {
[this]() { show_editor_selection_ = true; }, "Ctrl+E")
.Separator()
.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,
[this]() { current_editor_set_->dungeon_editor_.set_active(true); }, "Ctrl+2")
[this]() { SwitchToEditor(EditorType::kDungeon); }, "Ctrl+2")
.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,
[this]() { current_editor_set_->sprite_editor_.set_active(true); }, "Ctrl+4")
[this]() { SwitchToEditor(EditorType::kSprite); }, "Ctrl+4")
.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,
[this]() { current_editor_set_->music_editor_.set_active(true); }, "Ctrl+6")
[this]() { SwitchToEditor(EditorType::kMusic); }, "Ctrl+6")
.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,
[this]() { current_editor_set_->screen_editor_.set_active(true); }, "Ctrl+8")
[this]() { SwitchToEditor(EditorType::kScreen); }, "Ctrl+8")
.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
.Item("AI Agent", ICON_MD_SMART_TOY,
[this]() { agent_editor_.set_active(true); }, "Ctrl+Shift+A")
@@ -1388,9 +1442,9 @@ void EditorManager::BuildModernMenu() {
.Separator()
// Development Tools
.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,
[this]() { show_asm_editor_ = true; })
[this]() { gui::EditorCardManager::Get().ShowCard("assembly.editor"); })
.Item("Feature Flags", ICON_MD_FLAG,
[this]() { popup_manager_->Show("Feature Flags"); })
.Separator()
@@ -1512,11 +1566,15 @@ void EditorManager::DrawMenuBar() {
ShowDemoWindow(&show_imgui_demo_);
if (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_) {
current_editor_set_->assembly_editor_.Update(show_asm_editor_);
if (card_manager.IsCardVisible("assembly.editor") && current_editor_set_) {
bool show_asm = true;
current_editor_set_->assembly_editor_.Update(show_asm);
}
// Project file editor
@@ -1551,10 +1609,9 @@ void EditorManager::DrawMenuBar() {
DrawWelcomeScreen();
}
// Emulator is now card-based - it creates its own windows
if (show_emulator_) {
Begin("Emulator", &show_emulator_, ImGuiWindowFlags_MenuBar);
emulator_.Run(current_rom_);
End();
}
// Enhanced Command Palette UI with Fuzzy Search
@@ -2172,6 +2229,11 @@ absl::Status EditorManager::LoadAssets() {
current_editor_set_->overworld_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
current_editor_set_->dungeon_editor_.Initialize(renderer_, current_rom_);
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) {
// Find the editor tab and activate it
for (size_t i = 0; i < current_editor_set_->active_editors_.size(); ++i) {
if (current_editor_set_->active_editors_[i]->type() == editor_type) {
current_editor_set_->active_editors_[i]->set_active(true);
if (!current_editor_set_) return;
// Toggle the editor
for (auto* editor : current_editor_set_->active_editors_) {
if (editor->type() == editor_type) {
editor->toggle_active();
// Set editor as the current/focused one
// This will make it visible when tabs are rendered
break;
if (IsCardBasedEditor(editor_type)) {
auto& card_manager = gui::EditorCardManager::Get();
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
static bool IsCardBasedEditor(EditorType type);
static std::string GetEditorCategory(EditorType type);
static EditorType GetEditorTypeFromCategory(const std::string& category);
bool IsSidebarVisible() const { return show_card_sidebar_; }
void SetSidebarVisible(bool visible) { show_card_sidebar_ = visible; }

View File

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

View File

@@ -107,7 +107,6 @@ class GraphicsEditor : public Editor {
absl::Status DrawTilemapImport();
// Other Functions
void DrawToolset();
absl::Status DrawPaletteControls();
absl::Status DrawClipboardImport();
absl::Status DrawExperimentalFeatures();
@@ -117,12 +116,7 @@ class GraphicsEditor : public Editor {
absl::Status DecompressSuperDonkey();
// Member Variables
// 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_sheet_editor_ = false;
bool show_sheet_browser_ = false;
bool show_player_animations_ = false;
bool show_prototype_viewer_ = false;
// Card visibility managed by EditorCardManager
ImVec4 current_color_;
uint16_t current_sheet_ = 0;

View File

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

View File

@@ -78,14 +78,6 @@ class ScreenEditor : public Editor {
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;
uint8_t selected_room = 0;

View File

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

View File

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

View File

@@ -51,6 +51,17 @@ void OverworldEditor::Initialize() {
// Register cards with EditorCardManager
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_id = "overworld.tile16_selector",
.display_name = "Tile16 Selector",
@@ -193,11 +204,8 @@ absl::Status OverworldEditor::Update() {
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)
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 tile8_card(MakeCardTitle("Tile8 Selector").c_str(), ICON_MD_GRID_4X4);
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;
if (!cards_configured) {
// 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.SetPosition(gui::EditorCard::Position::Right);
@@ -239,7 +250,13 @@ absl::Status OverworldEditor::Update() {
}
// 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)
if (show_tile16_selector_) {

View File

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

View File

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

View File

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

View File

@@ -37,8 +37,8 @@ constexpr ImGuiTableFlags kSpriteTableFlags =
*/
class SpriteEditor : public Editor {
public:
explicit SpriteEditor(Rom* rom = nullptr) : rom_(rom) {
type_ = EditorType::kSprite;
explicit SpriteEditor(Rom* rom = nullptr) : rom_(rom) {
type_ = EditorType::kSprite;
}
void Initialize() override;
@@ -51,10 +51,10 @@ class SpriteEditor : public Editor {
absl::Status Paste() override { return absl::UnimplementedError("Paste"); }
absl::Status Find() override { return absl::UnimplementedError("Find"); }
absl::Status Save() override { return absl::UnimplementedError("Save"); }
// Set the ROM pointer
void set_rom(Rom* rom) { rom_ = rom; }
// Get the ROM pointer
Rom* rom() const { return rom_; }
@@ -84,11 +84,6 @@ class SpriteEditor : public Editor {
void DrawAnimationFrames();
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. */
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) {
// Check for keyboard shortcuts using the shortcut manager
for (const auto& shortcut : shortcut_manager.GetShortcuts()) {
bool keys_pressed = true;
// Check for all the keys in the shortcut
bool modifiers_held = true;
bool key_pressed = false;
ImGuiKey main_key = ImGuiKey_None;
// Separate modifiers from main key
for (const auto& key : shortcut.second.keys) {
if (key == ImGuiMod_Ctrl) {
keys_pressed &= ImGui::GetIO().KeyCtrl;
} else if (key == ImGuiMod_Alt) {
keys_pressed &= ImGui::GetIO().KeyAlt;
} else if (key == ImGuiMod_Shift) {
keys_pressed &= ImGui::GetIO().KeyShift;
} else if (key == ImGuiMod_Super) {
keys_pressed &= ImGui::GetIO().KeySuper;
if (key == ImGuiMod_Ctrl || key == ImGuiMod_Alt ||
key == ImGuiMod_Shift || key == ImGuiMod_Super) {
// Check if modifier is held
if (key == ImGuiMod_Ctrl) {
modifiers_held &= ImGui::GetIO().KeyCtrl;
} else if (key == ImGuiMod_Alt) {
modifiers_held &= ImGui::GetIO().KeyAlt;
} else if (key == ImGuiMod_Shift) {
modifiers_held &= ImGui::GetIO().KeyShift;
} else if (key == ImGuiMod_Super) {
modifiers_held &= ImGui::GetIO().KeySuper;
}
} else {
keys_pressed &= ImGui::IsKeyDown(key);
}
if (!keys_pressed) {
break;
// This is the main key - use IsKeyPressed for single trigger
main_key = key;
}
}
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();
}
}