From 219406901d4940e95fcba4beba3c043b5e6d05f1 Mon Sep 17 00:00:00 2001 From: scawful Date: Thu, 9 Oct 2025 09:46:29 -0400 Subject: [PATCH] feat: Enhance editor card management and shortcut functionality - Introduced new shortcut categories for graphics, screen, and sprite editors, improving accessibility and organization. - Updated DungeonEditorV2 and other editor classes to register cards with the EditorCardManager, allowing for better control and visibility management. - Refactored shortcut registration for dungeon, graphics, screen, and sprite editors, ensuring consistent user experience across the application. - Improved initialization processes to handle dynamic card visibility and shortcuts effectively. --- src/app/editor/code/assembly_editor.cc | 13 ++- src/app/editor/code/assembly_editor.h | 1 + .../editor/dungeon/dungeon_canvas_viewer.cc | 4 +- src/app/editor/dungeon/dungeon_editor.cc | 5 +- src/app/editor/dungeon/dungeon_editor_v2.cc | 102 +++++++++--------- src/app/editor/dungeon/dungeon_editor_v2.h | 14 +-- src/app/editor/editor_manager.cc | 41 ++++++- src/app/editor/graphics/graphics_editor.cc | 47 +++++++- src/app/editor/graphics/graphics_editor.h | 6 +- src/app/editor/graphics/screen_editor.cc | 57 +++++++++- src/app/editor/graphics/screen_editor.h | 6 +- src/app/editor/sprite/sprite_editor.cc | 27 ++++- src/app/editor/sprite/sprite_editor.h | 6 +- src/app/gfx/arena.cc | 65 +++++++---- src/app/gui/editor_card_manager.cc | 7 ++ 15 files changed, 315 insertions(+), 86 deletions(-) diff --git a/src/app/editor/code/assembly_editor.cc b/src/app/editor/code/assembly_editor.cc index 622bf057..10b9fb8e 100644 --- a/src/app/editor/code/assembly_editor.cc +++ b/src/app/editor/code/assembly_editor.cc @@ -176,7 +176,18 @@ void AssemblyEditor::Initialize() { text_editor_.SetLanguageDefinition(GetAssemblyLanguageDef()); } -absl::Status AssemblyEditor::Load() { return absl::OkStatus(); } +absl::Status AssemblyEditor::Load() { + // Register cards with EditorCardManager + // Note: Assembly editor uses dynamic file tabs, so we register the main editor window + auto& card_manager = gui::EditorCardManager::Get(); + + // The assembly editor itself acts as a card when shown + // Individual files are tabs within it, not separate cards + + printf("[AssemblyEditor] Assembly editor uses dynamic file tabs\n"); + + return absl::OkStatus(); +} void AssemblyEditor::OpenFolder(const std::string& folder_path) { current_folder_ = LoadFolder(folder_path); diff --git a/src/app/editor/code/assembly_editor.h b/src/app/editor/code/assembly_editor.h index 06eab770..69aca081 100644 --- a/src/app/editor/code/assembly_editor.h +++ b/src/app/editor/code/assembly_editor.h @@ -6,6 +6,7 @@ #include "app/editor/editor.h" #include "app/gui/modules/text_editor.h" #include "app/gui/editor_layout.h" +#include "app/gui/editor_card_manager.h" #include "app/gui/style.h" #include "app/rom.h" diff --git a/src/app/editor/dungeon/dungeon_canvas_viewer.cc b/src/app/editor/dungeon/dungeon_canvas_viewer.cc index a91b2a9c..ec50edff 100644 --- a/src/app/editor/dungeon/dungeon_canvas_viewer.cc +++ b/src/app/editor/dungeon/dungeon_canvas_viewer.cc @@ -157,7 +157,9 @@ void DungeonCanvasViewer::DrawDungeonCanvas(int room_id) { // Process queued texture commands if (rom_ && rom_->is_loaded()) { - gfx::Arena::Get().ProcessTextureQueue(nullptr); // Will use default renderer + // Process texture queue using Arena's stored renderer + // The renderer was initialized in EditorManager::LoadAssets() + gfx::Arena::Get().ProcessTextureQueue(nullptr); } // Draw layer information overlay diff --git a/src/app/editor/dungeon/dungeon_editor.cc b/src/app/editor/dungeon/dungeon_editor.cc index fc1e42a2..10cf847f 100644 --- a/src/app/editor/dungeon/dungeon_editor.cc +++ b/src/app/editor/dungeon/dungeon_editor.cc @@ -881,8 +881,11 @@ absl::Status DungeonEditor::UpdateRoomBackgroundLayers(int /*room_id*/) { void DungeonEditor::ProcessDeferredTextures() { // Process queued texture commands via Arena's deferred system - // Note: Arena will use its stored renderer reference + // Note: Arena uses its stored renderer reference (initialized in EditorManager) + // The parameter is ignored, but we pass nullptr to indicate we're using the stored renderer gfx::Arena::Get().ProcessTextureQueue(nullptr); + + // NOTE: This is deprecated - use DungeonEditorV2 instead } } // namespace yaze::editor diff --git a/src/app/editor/dungeon/dungeon_editor_v2.cc b/src/app/editor/dungeon/dungeon_editor_v2.cc index 0b9c11ae..10940553 100644 --- a/src/app/editor/dungeon/dungeon_editor_v2.cc +++ b/src/app/editor/dungeon/dungeon_editor_v2.cc @@ -24,55 +24,8 @@ void DungeonEditorV2::Initialize(gfx::IRenderer* renderer, Rom* rom) { // Setup docking class for room windows room_window_class_.ClassId = ImGui::GetID("DungeonRoomClass"); room_window_class_.DockingAllowUnclassed = false; // Room windows dock together -} - -void DungeonEditorV2::Initialize() {} - -absl::Status DungeonEditorV2::Load() { - if (!rom_ || !rom_->is_loaded()) { - return absl::FailedPreconditionError("ROM not loaded"); - } - - // Load all rooms using the loader component - DEFERRED for lazy loading - // RETURN_IF_ERROR(room_loader_.LoadAllRooms(rooms_)); - RETURN_IF_ERROR(room_loader_.LoadRoomEntrances(entrances_)); - - // Load palette group - auto dungeon_main_pal_group = rom_->palette_group().dungeon_main; - current_palette_ = dungeon_main_pal_group[current_palette_group_id_]; - ASSIGN_OR_RETURN(current_palette_group_, - gfx::CreatePaletteGroupFromLargePalette(current_palette_)); - - // Initialize components with loaded data - room_selector_.set_rooms(&rooms_); - room_selector_.set_entrances(&entrances_); - room_selector_.set_active_rooms(active_rooms_); - room_selector_.set_room_selected_callback( - [this](int room_id) { OnRoomSelected(room_id); }); - - canvas_viewer_.SetRooms(&rooms_); - canvas_viewer_.SetCurrentPaletteGroup(current_palette_group_); - canvas_viewer_.SetCurrentPaletteId(current_palette_id_); - - object_selector_.SetCurrentPaletteGroup(current_palette_group_); - object_selector_.SetCurrentPaletteId(current_palette_id_); - object_selector_.set_rooms(&rooms_); - - // NOW initialize emulator preview with loaded ROM - object_emulator_preview_.Initialize(renderer_, rom_); - // Initialize palette editor with loaded ROM - palette_editor_.Initialize(rom_); - - // Initialize unified object editor card - object_editor_card_ = std::make_unique(renderer_, rom_, &canvas_viewer_); - - // Initialize manual renderer for debugging (uses canvas from canvas_viewer_) - manual_renderer_ = std::make_unique( - &canvas_viewer_.canvas(), rom_); - printf("[DungeonEditorV2] Manual renderer initialized for debugging\n"); - - // Register all cards with the card manager for unified control + // Register all cards with the card manager (done once during initialization) auto& card_manager = gui::EditorCardManager::Get(); card_manager.RegisterCard({ @@ -146,6 +99,53 @@ absl::Status DungeonEditorV2::Load() { }); printf("[DungeonEditorV2] Registered 7 cards with EditorCardManager\n"); +} + +void DungeonEditorV2::Initialize() {} + +absl::Status DungeonEditorV2::Load() { + if (!rom_ || !rom_->is_loaded()) { + return absl::FailedPreconditionError("ROM not loaded"); + } + + // Load all rooms using the loader component - DEFERRED for lazy loading + // RETURN_IF_ERROR(room_loader_.LoadAllRooms(rooms_)); + RETURN_IF_ERROR(room_loader_.LoadRoomEntrances(entrances_)); + + // Load palette group + auto dungeon_main_pal_group = rom_->palette_group().dungeon_main; + current_palette_ = dungeon_main_pal_group[current_palette_group_id_]; + ASSIGN_OR_RETURN(current_palette_group_, + gfx::CreatePaletteGroupFromLargePalette(current_palette_)); + + // Initialize components with loaded data + room_selector_.set_rooms(&rooms_); + room_selector_.set_entrances(&entrances_); + room_selector_.set_active_rooms(active_rooms_); + room_selector_.set_room_selected_callback( + [this](int room_id) { OnRoomSelected(room_id); }); + + canvas_viewer_.SetRooms(&rooms_); + canvas_viewer_.SetCurrentPaletteGroup(current_palette_group_); + canvas_viewer_.SetCurrentPaletteId(current_palette_id_); + + object_selector_.SetCurrentPaletteGroup(current_palette_group_); + object_selector_.SetCurrentPaletteId(current_palette_id_); + object_selector_.set_rooms(&rooms_); + + // NOW initialize emulator preview with loaded ROM + object_emulator_preview_.Initialize(renderer_, rom_); + + // Initialize palette editor with loaded ROM + palette_editor_.Initialize(rom_); + + // Initialize unified object editor card + object_editor_card_ = std::make_unique(renderer_, rom_, &canvas_viewer_); + + // Initialize manual renderer for debugging (uses canvas from canvas_viewer_) + manual_renderer_ = std::make_unique( + &canvas_viewer_.canvas(), rom_); + printf("[DungeonEditorV2] Manual renderer initialized for debugging\n"); // Wire palette changes to trigger room re-renders palette_editor_.SetOnPaletteChanged([this](int /*palette_id*/) { @@ -267,12 +267,16 @@ void DungeonEditorV2::DrawToolset() { void DungeonEditorV2::DrawControlPanel() { // Small, collapsible control panel for dungeon editor - ImGui::SetNextWindowSize(ImVec2(250, 200), ImGuiCond_FirstUseEver); + 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(); diff --git a/src/app/editor/dungeon/dungeon_editor_v2.h b/src/app/editor/dungeon/dungeon_editor_v2.h index eb2878b9..1233b2cd 100644 --- a/src/app/editor/dungeon/dungeon_editor_v2.h +++ b/src/app/editor/dungeon/dungeon_editor_v2.h @@ -120,14 +120,16 @@ class DungeonEditorV2 : public Editor { std::unordered_map> room_cards_; int current_room_id_ = 0; - // Card visibility flags - bool show_room_selector_ = true; + // Card visibility flags - Start with only control panel visible + // Other cards hidden by default to prevent crash on ROM load + // User can open them via View menu or shortcuts + bool show_room_selector_ = false; bool show_room_matrix_ = false; bool show_entrances_list_ = false; - bool show_room_graphics_ = false; // Room graphics card - bool show_object_editor_ = true; // Unified object editor card - bool show_palette_editor_ = true; - bool show_control_panel_ = true; // Optional control panel + bool show_room_graphics_ = false; + bool show_object_editor_ = false; + bool show_palette_editor_ = false; + bool show_control_panel_ = true; // Only control panel visible on start bool control_panel_minimized_ = false; // Palette management diff --git a/src/app/editor/editor_manager.cc b/src/app/editor/editor_manager.cc index 1fb9cb84..b697d178 100644 --- a/src/app/editor/editor_manager.cc +++ b/src/app/editor/editor_manager.cc @@ -616,7 +616,7 @@ void EditorManager::Initialize(gfx::IRenderer* renderer, const std::string& file "Card Browser", {ImGuiKey_B, ImGuiMod_Ctrl, ImGuiMod_Shift}, [this]() { show_card_browser_ = true; }); - // Dungeon Card shortcuts + // === DUNGEON CARD SHORTCUTS (Ctrl+Shift+Key) === context_.shortcut_manager.RegisterShortcut( "Toggle Dungeon Controls", {ImGuiKey_D, ImGuiMod_Ctrl, ImGuiMod_Shift}, []() { gui::EditorCardManager::Get().ToggleCard("dungeon.control_panel"); }); @@ -638,6 +638,45 @@ void EditorManager::Initialize(gfx::IRenderer* renderer, const std::string& file context_.shortcut_manager.RegisterShortcut( "Toggle Dungeon Palette", {ImGuiKey_P, ImGuiMod_Ctrl, ImGuiMod_Shift}, []() { gui::EditorCardManager::Get().ToggleCard("dungeon.palette_editor"); }); + + // === GRAPHICS CARD SHORTCUTS (Ctrl+Shift+Number) === + context_.shortcut_manager.RegisterShortcut( + "Toggle Sheet Editor", {ImGuiKey_1, ImGuiMod_Ctrl, ImGuiMod_Shift}, + []() { gui::EditorCardManager::Get().ToggleCard("graphics.sheet_editor"); }); + context_.shortcut_manager.RegisterShortcut( + "Toggle Sheet Browser", {ImGuiKey_2, ImGuiMod_Ctrl, ImGuiMod_Shift}, + []() { gui::EditorCardManager::Get().ToggleCard("graphics.sheet_browser"); }); + context_.shortcut_manager.RegisterShortcut( + "Toggle Player Animations", {ImGuiKey_3, ImGuiMod_Ctrl, ImGuiMod_Shift}, + []() { gui::EditorCardManager::Get().ToggleCard("graphics.player_animations"); }); + context_.shortcut_manager.RegisterShortcut( + "Toggle Prototype Viewer", {ImGuiKey_4, ImGuiMod_Ctrl, ImGuiMod_Shift}, + []() { gui::EditorCardManager::Get().ToggleCard("graphics.prototype_viewer"); }); + + // === SCREEN EDITOR SHORTCUTS (Alt+Number) === + context_.shortcut_manager.RegisterShortcut( + "Toggle Dungeon Maps", {ImGuiKey_1, ImGuiMod_Alt}, + []() { gui::EditorCardManager::Get().ToggleCard("screen.dungeon_maps"); }); + context_.shortcut_manager.RegisterShortcut( + "Toggle Inventory Menu", {ImGuiKey_2, ImGuiMod_Alt}, + []() { gui::EditorCardManager::Get().ToggleCard("screen.inventory_menu"); }); + context_.shortcut_manager.RegisterShortcut( + "Toggle Overworld Map Screen", {ImGuiKey_3, ImGuiMod_Alt}, + []() { gui::EditorCardManager::Get().ToggleCard("screen.overworld_map"); }); + context_.shortcut_manager.RegisterShortcut( + "Toggle Title Screen", {ImGuiKey_4, ImGuiMod_Alt}, + []() { gui::EditorCardManager::Get().ToggleCard("screen.title_screen"); }); + context_.shortcut_manager.RegisterShortcut( + "Toggle Naming Screen", {ImGuiKey_5, ImGuiMod_Alt}, + []() { gui::EditorCardManager::Get().ToggleCard("screen.naming_screen"); }); + + // === SPRITE EDITOR SHORTCUTS (Alt+Shift+Number) === + context_.shortcut_manager.RegisterShortcut( + "Toggle Vanilla Sprites", {ImGuiKey_1, ImGuiMod_Alt, ImGuiMod_Shift}, + []() { gui::EditorCardManager::Get().ToggleCard("sprite.vanilla_editor"); }); + context_.shortcut_manager.RegisterShortcut( + "Toggle Custom Sprites", {ImGuiKey_2, ImGuiMod_Alt, ImGuiMod_Shift}, + []() { gui::EditorCardManager::Get().ToggleCard("sprite.custom_editor"); }); #ifdef YAZE_WITH_GRPC // Agent Editor shortcut diff --git a/src/app/editor/graphics/graphics_editor.cc b/src/app/editor/graphics/graphics_editor.cc index fc5d9c04..0f7b2643 100644 --- a/src/app/editor/graphics/graphics_editor.cc +++ b/src/app/editor/graphics/graphics_editor.cc @@ -41,7 +41,52 @@ constexpr ImGuiTableFlags kGfxEditTableFlags = ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_SizingFixedFit; -void GraphicsEditor::Initialize() {} +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_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 + }); + + printf("[GraphicsEditor] Registered 4 cards with EditorCardManager\n"); +} absl::Status GraphicsEditor::Load() { gfx::ScopedTimer timer("GraphicsEditor::Load"); diff --git a/src/app/editor/graphics/graphics_editor.h b/src/app/editor/graphics/graphics_editor.h index 35cb3291..fbb50db2 100644 --- a/src/app/editor/graphics/graphics_editor.h +++ b/src/app/editor/graphics/graphics_editor.h @@ -7,6 +7,7 @@ #include "app/editor/editor.h" #include "app/editor/graphics/palette_editor.h" #include "app/gfx/bitmap.h" +#include "app/gui/editor_card_manager.h" #include "app/gfx/snes_tile.h" #include "app/gui/canvas.h" #include "app/gui/editor_layout.h" @@ -116,8 +117,9 @@ class GraphicsEditor : public Editor { absl::Status DecompressSuperDonkey(); // Member Variables - // Card visibility - bool show_sheet_editor_ = true; + // 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; diff --git a/src/app/editor/graphics/screen_editor.cc b/src/app/editor/graphics/screen_editor.cc index dc154a23..ae7369ac 100644 --- a/src/app/editor/graphics/screen_editor.cc +++ b/src/app/editor/graphics/screen_editor.cc @@ -29,7 +29,62 @@ namespace editor { constexpr uint32_t kRedPen = 0xFF0000FF; -void ScreenEditor::Initialize() {} +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.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 + }); + + printf("[ScreenEditor] Registered 5 cards with EditorCardManager\n"); +} absl::Status ScreenEditor::Load() { gfx::ScopedTimer timer("ScreenEditor::Load"); diff --git a/src/app/editor/graphics/screen_editor.h b/src/app/editor/graphics/screen_editor.h index 4dcc5e46..1a6c31b7 100644 --- a/src/app/editor/graphics/screen_editor.h +++ b/src/app/editor/graphics/screen_editor.h @@ -7,6 +7,7 @@ #include "app/editor/editor.h" #include "app/gfx/bitmap.h" #include "app/gfx/snes_palette.h" +#include "app/gui/editor_card_manager.h" #include "app/gfx/tilemap.h" #include "app/gui/canvas.h" #include "app/rom.h" @@ -77,8 +78,9 @@ class ScreenEditor : public Editor { EditingMode current_mode_ = EditingMode::DRAW; - // Card visibility - bool show_dungeon_maps_ = true; + // 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; diff --git a/src/app/editor/sprite/sprite_editor.cc b/src/app/editor/sprite/sprite_editor.cc index b15f0604..a7c8bbcd 100644 --- a/src/app/editor/sprite/sprite_editor.cc +++ b/src/app/editor/sprite/sprite_editor.cc @@ -24,7 +24,32 @@ using ImGui::TableNextRow; using ImGui::TableSetupColumn; using ImGui::Text; -void SpriteEditor::Initialize() {} +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.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 + }); + + printf("[SpriteEditor] Registered 2 cards with EditorCardManager\n"); +} absl::Status SpriteEditor::Load() { gfx::ScopedTimer timer("SpriteEditor::Load"); diff --git a/src/app/editor/sprite/sprite_editor.h b/src/app/editor/sprite/sprite_editor.h index f58c7912..51ae6696 100644 --- a/src/app/editor/sprite/sprite_editor.h +++ b/src/app/editor/sprite/sprite_editor.h @@ -8,6 +8,7 @@ #include "app/editor/editor.h" #include "app/editor/sprite/zsprite.h" #include "app/gui/canvas.h" +#include "app/gui/editor_card_manager.h" #include "app/gui/editor_layout.h" #include "app/rom.h" @@ -83,8 +84,9 @@ class SpriteEditor : public Editor { void DrawAnimationFrames(); void DrawToolset(); - // Card visibility - bool show_vanilla_editor_ = true; + // 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 active_sprites_; /**< Active sprites. */ diff --git a/src/app/gfx/arena.cc b/src/app/gfx/arena.cc index a31ae093..3da1d25f 100644 --- a/src/app/gfx/arena.cc +++ b/src/app/gfx/arena.cc @@ -33,7 +33,17 @@ void Arena::QueueTextureCommand(TextureCommandType type, Bitmap* bitmap) { } void Arena::ProcessTextureQueue(IRenderer* renderer) { - if (!renderer_ || texture_command_queue_.empty()) return; + // Use provided renderer if available, otherwise use stored renderer + IRenderer* active_renderer = renderer ? renderer : renderer_; + + if (!active_renderer) { + // Arena not initialized yet - defer processing + return; + } + + if (texture_command_queue_.empty()) { + return; + } // Performance optimization: Batch process textures with limits // Process up to 8 texture operations per frame to avoid frame drops @@ -45,40 +55,59 @@ void Arena::ProcessTextureQueue(IRenderer* renderer) { const auto& command = *it; bool should_remove = true; + // CRITICAL: Replicate the exact short-circuit evaluation from working code + // We MUST check command.bitmap AND command.bitmap->surface() in one expression + // to avoid dereferencing invalid pointers + switch (command.type) { case TextureCommandType::CREATE: { // Create a new texture and update it with bitmap data - if (command.bitmap && command.bitmap->surface() && - command.bitmap->surface()->format && + // Use short-circuit evaluation - if bitmap is invalid, never call ->surface() + if (command.bitmap && command.bitmap->surface() && + command.bitmap->surface()->format && command.bitmap->is_active() && command.bitmap->width() > 0 && command.bitmap->height() > 0) { - auto texture = renderer_->CreateTexture(command.bitmap->width(), - command.bitmap->height()); - if (texture) { - command.bitmap->set_texture(texture); - renderer_->UpdateTexture(texture, *command.bitmap); - processed++; - } else { - should_remove = false; // Retry next frame + + try { + auto texture = active_renderer->CreateTexture(command.bitmap->width(), + command.bitmap->height()); + if (texture) { + command.bitmap->set_texture(texture); + active_renderer->UpdateTexture(texture, *command.bitmap); + processed++; + } else { + should_remove = false; // Retry next frame + } + } catch (...) { + printf("[Arena] ERROR: Exception during texture creation\n"); + should_remove = true; // Remove bad command } } break; } case TextureCommandType::UPDATE: { // Update existing texture with current bitmap data - if (command.bitmap && command.bitmap->texture() && + if (command.bitmap->texture() && command.bitmap->surface() && command.bitmap->surface()->format && command.bitmap->is_active()) { - renderer_->UpdateTexture(command.bitmap->texture(), *command.bitmap); - processed++; + try { + active_renderer->UpdateTexture(command.bitmap->texture(), *command.bitmap); + processed++; + } catch (...) { + printf("[Arena] ERROR: Exception during texture update\n"); + } } break; } case TextureCommandType::DESTROY: { - if (command.bitmap && command.bitmap->texture()) { - renderer_->DestroyTexture(command.bitmap->texture()); - command.bitmap->set_texture(nullptr); - processed++; + if (command.bitmap->texture()) { + try { + active_renderer->DestroyTexture(command.bitmap->texture()); + command.bitmap->set_texture(nullptr); + processed++; + } catch (...) { + printf("[Arena] ERROR: Exception during texture destruction\n"); + } } break; } diff --git a/src/app/gui/editor_card_manager.cc b/src/app/gui/editor_card_manager.cc index d93e996f..eee6982c 100644 --- a/src/app/gui/editor_card_manager.cc +++ b/src/app/gui/editor_card_manager.cc @@ -22,6 +22,13 @@ void EditorCardManager::RegisterCard(const CardInfo& info) { return; } + // Check if already registered to avoid duplicates + if (cards_.find(info.card_id) != cards_.end()) { + printf("[EditorCardManager] WARNING: Card '%s' already registered, skipping duplicate\n", + info.card_id.c_str()); + return; + } + cards_[info.card_id] = info; printf("[EditorCardManager] Registered card: %s (%s)\n", info.card_id.c_str(), info.display_name.c_str());