diff --git a/src/app/editor/editor_library.cmake b/src/app/editor/editor_library.cmake index d90b6951..fe3cf5ba 100644 --- a/src/app/editor/editor_library.cmake +++ b/src/app/editor/editor_library.cmake @@ -48,6 +48,7 @@ set( app/editor/system/window_delegate.cc app/editor/system/shortcut_configurator.cc app/editor/ui/editor_selection_dialog.cc + app/editor/ui/layout_manager.cc app/editor/ui/menu_builder.cc app/editor/ui/ui_coordinator.cc app/editor/ui/welcome_screen.cc diff --git a/src/app/editor/editor_manager.cc b/src/app/editor/editor_manager.cc index 659448e6..f4319bce 100644 --- a/src/app/editor/editor_manager.cc +++ b/src/app/editor/editor_manager.cc @@ -182,6 +182,9 @@ EditorManager::EditorManager() *session_coordinator_, window_delegate_, toast_manager_, *popup_manager_, shortcut_manager_); + // STEP 4.5: Initialize LayoutManager (DockBuilder layouts for editors) + layout_manager_ = std::make_unique(); + // STEP 5: ShortcutConfigurator created later in Initialize() method // It depends on all above coordinators being available } @@ -1954,6 +1957,12 @@ void EditorManager::SwitchToEditor(EditorType editor_type) { // Editor activated - set its category card_registry_.SetActiveCategory( EditorRegistry::GetEditorCategory(editor_type)); + + // Initialize default layout on first activation + if (layout_manager_ && !layout_manager_->IsLayoutInitialized(editor_type)) { + ImGuiID dockspace_id = ImGui::GetID("MainDockSpace"); + layout_manager_->InitializeEditorLayout(editor_type, dockspace_id); + } } else { // Editor deactivated - switch to another active card-based editor for (auto* other : current_editor_set_->active_editors_) { diff --git a/src/app/editor/editor_manager.h b/src/app/editor/editor_manager.h index ef909ce5..63a87999 100644 --- a/src/app/editor/editor_manager.h +++ b/src/app/editor/editor_manager.h @@ -40,6 +40,7 @@ #include "app/editor/system/toast_manager.h" #include "app/editor/system/window_delegate.h" #include "app/editor/ui/editor_selection_dialog.h" +#include "app/editor/ui/layout_manager.h" #include "app/editor/ui/menu_builder.h" #include "app/editor/ui/ui_coordinator.h" #include "app/editor/ui/welcome_screen.h" @@ -409,6 +410,7 @@ class EditorManager { std::unique_ptr ui_coordinator_; WindowDelegate window_delegate_; std::unique_ptr session_coordinator_; + std::unique_ptr layout_manager_; // DockBuilder layout management float autosave_timer_ = 0.0f; diff --git a/src/app/editor/system/shortcut_configurator.cc b/src/app/editor/system/shortcut_configurator.cc index ac5e3369..5afbdae4 100644 --- a/src/app/editor/system/shortcut_configurator.cc +++ b/src/app/editor/system/shortcut_configurator.cc @@ -218,18 +218,19 @@ void ConfigureEditorShortcuts(const ShortcutDependencies& deps, }); if (card_registry) { + // Note: Using Ctrl+Alt for card shortcuts to avoid conflicts with Save As (Ctrl+Shift+S) RegisterIfValid(shortcut_manager, "Show Dungeon Cards", - {ImGuiMod_Ctrl, ImGuiMod_Shift, ImGuiKey_D}, + {ImGuiMod_Ctrl, ImGuiMod_Alt, ImGuiKey_D}, [card_registry]() { card_registry->ShowAllCardsInCategory("Dungeon"); }); RegisterIfValid(shortcut_manager, "Show Graphics Cards", - {ImGuiMod_Ctrl, ImGuiMod_Shift, ImGuiKey_G}, + {ImGuiMod_Ctrl, ImGuiMod_Alt, ImGuiKey_G}, [card_registry]() { card_registry->ShowAllCardsInCategory("Graphics"); }); RegisterIfValid(shortcut_manager, "Show Screen Cards", - {ImGuiMod_Ctrl, ImGuiMod_Shift, ImGuiKey_S}, + {ImGuiMod_Ctrl, ImGuiMod_Alt, ImGuiKey_S}, [card_registry]() { card_registry->ShowAllCardsInCategory("Screen"); }); @@ -320,8 +321,9 @@ void ConfigureMenuShortcuts(const ShortcutDependencies& deps, } }); + // Note: Changed from Ctrl+Shift+R to Ctrl+Alt+R to avoid conflict with Proposal Drawer RegisterIfValid(shortcut_manager, "Reset Layout", - {ImGuiMod_Ctrl, ImGuiMod_Shift, ImGuiKey_R}, + {ImGuiMod_Ctrl, ImGuiMod_Alt, ImGuiKey_R}, [workspace_manager]() { if (workspace_manager) { workspace_manager->ResetWorkspaceLayout(); diff --git a/src/app/editor/ui/layout_manager.cc b/src/app/editor/ui/layout_manager.cc new file mode 100644 index 00000000..7a531af6 --- /dev/null +++ b/src/app/editor/ui/layout_manager.cc @@ -0,0 +1,413 @@ +#include "app/editor/ui/layout_manager.h" + +#include "imgui/imgui.h" +#include "imgui/imgui_internal.h" +#include "util/log.h" + +namespace yaze { +namespace editor { + +void LayoutManager::InitializeEditorLayout(EditorType type, + ImGuiID dockspace_id) { + // Don't reinitialize if already set up + if (IsLayoutInitialized(type)) { + LOG_INFO("LayoutManager", + "Layout for editor type %d already initialized, skipping", + static_cast(type)); + return; + } + + LOG_INFO("LayoutManager", "Initializing layout for editor type %d", + static_cast(type)); + + // Clear existing layout for this dockspace + ImGui::DockBuilderRemoveNode(dockspace_id); + ImGui::DockBuilderAddNode(dockspace_id, + ImGuiDockNodeFlags_DockSpace); + ImGui::DockBuilderSetNodeSize(dockspace_id, ImGui::GetMainViewport()->Size); + + // Build layout based on editor type + switch (type) { + case EditorType::kOverworld: + BuildOverworldLayout(dockspace_id); + break; + case EditorType::kDungeon: + BuildDungeonLayout(dockspace_id); + break; + case EditorType::kGraphics: + BuildGraphicsLayout(dockspace_id); + break; + case EditorType::kPalette: + BuildPaletteLayout(dockspace_id); + break; + case EditorType::kScreen: + BuildScreenLayout(dockspace_id); + break; + case EditorType::kMusic: + BuildMusicLayout(dockspace_id); + break; + case EditorType::kSprite: + BuildSpriteLayout(dockspace_id); + break; + case EditorType::kMessage: + BuildMessageLayout(dockspace_id); + break; + case EditorType::kAssembly: + BuildAssemblyLayout(dockspace_id); + break; + case EditorType::kSettings: + BuildSettingsLayout(dockspace_id); + break; + default: + LOG_WARN("LayoutManager", "No layout defined for editor type %d", + static_cast(type)); + break; + } + + // Finalize the layout + ImGui::DockBuilderFinish(dockspace_id); + + // Mark as initialized + MarkLayoutInitialized(type); +} + +void LayoutManager::BuildOverworldLayout(ImGuiID dockspace_id) { + // TODO: [EditorManagerRefactor] Implement DockBuilder layout for Overworld + // Editor + // + // Desired layout: + // - Left 25%: Tile16 Selector (top 50%) + Tile8 Selector (bottom 50%) + // - Center 60%: Main Canvas (full height) + // - Right 15%: Area Graphics (top 60%) + Scratch Pad (bottom 40%) + // + // Additional floating cards: + // - Tile16 Editor (floating, 800x600) + // - GFX Groups (floating, 700x550) + // - Usage Stats (floating, 600x500) + // - V3 Settings (floating, 500x600) + + ImGuiID dock_left_id = 0; + ImGuiID dock_center_id = 0; + ImGuiID dock_right_id = 0; + + // Split dockspace: Left 25% | Center 60% | Right 15% + dock_left_id = ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Left, 0.25f, + nullptr, &dockspace_id); + dock_right_id = ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Right, + 0.20f, nullptr, &dockspace_id); + dock_center_id = dockspace_id; // Center is what remains + + // Split left panel: Tile16 (top) and Tile8 (bottom) + ImGuiID dock_left_top = 0; + ImGuiID dock_left_bottom = ImGui::DockBuilderSplitNode( + dock_left_id, ImGuiDir_Down, 0.50f, nullptr, &dock_left_top); + + // Split right panel: Area Graphics (top) and Scratch Pad (bottom) + ImGuiID dock_right_top = 0; + ImGuiID dock_right_bottom = ImGui::DockBuilderSplitNode( + dock_right_id, ImGuiDir_Down, 0.40f, nullptr, &dock_right_top); + + // Dock windows to their designated nodes + ImGui::DockBuilderDockWindow(" Overworld Canvas", dock_center_id); + ImGui::DockBuilderDockWindow(" Tile16 Selector", dock_left_top); + ImGui::DockBuilderDockWindow(" Tile8 Selector", dock_left_bottom); + ImGui::DockBuilderDockWindow(" Area Graphics", dock_right_top); + ImGui::DockBuilderDockWindow(" Scratch Pad", dock_right_bottom); + + // Note: Floating windows (Tile16 Editor, GFX Groups, etc.) are not docked + // They will appear as floating windows with their configured default positions +} + +void LayoutManager::BuildDungeonLayout(ImGuiID dockspace_id) { + // TODO: [EditorManagerRefactor] Implement DockBuilder layout for Dungeon + // Editor + // + // Desired layout: + // - Left 20%: Room Selector (top 60%) + Entrances (bottom 40%) + // - Center 65%: Room Canvas + Tabs for multiple rooms + // - Right 15%: Object Editor (top) + Palette Editor (bottom) + + ImGuiID dock_left_id = 0; + ImGuiID dock_center_id = 0; + ImGuiID dock_right_id = 0; + + // Split dockspace: Left 20% | Center 65% | Right 15% + dock_left_id = ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Left, 0.20f, + nullptr, &dockspace_id); + dock_right_id = ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Right, + 0.19f, nullptr, &dockspace_id); + dock_center_id = dockspace_id; + + // Split left panel: Room Selector (top 60%) and Entrances (bottom 40%) + ImGuiID dock_left_top = 0; + ImGuiID dock_left_bottom = ImGui::DockBuilderSplitNode( + dock_left_id, ImGuiDir_Down, 0.40f, nullptr, &dock_left_top); + + // Split right panel: Object Editor (top 50%) and Palette Editor (bottom 50%) + ImGuiID dock_right_top = 0; + ImGuiID dock_right_bottom = ImGui::DockBuilderSplitNode( + dock_right_id, ImGuiDir_Down, 0.50f, nullptr, &dock_right_top); + + // Dock windows + ImGui::DockBuilderDockWindow(" Rooms List", dock_left_top); + ImGui::DockBuilderDockWindow(" Entrances", dock_left_bottom); + ImGui::DockBuilderDockWindow(" Object Editor", dock_right_top); + ImGui::DockBuilderDockWindow(" Palette Editor", dock_right_bottom); + + // Room tabs and Room Matrix are floating by default + // Individual room windows (###RoomCard*) will dock together due to their + // window class +} + +void LayoutManager::BuildGraphicsLayout(ImGuiID dockspace_id) { + // TODO: [EditorManagerRefactor] Implement DockBuilder layout for Graphics + // Editor + // + // Desired layout: + // - Left 30%: Sheet Browser + // - Center 50%: Sheet Editor + // - Right 20%: Animations (top) + Prototype (bottom) + + ImGuiID dock_left_id = 0; + ImGuiID dock_center_id = 0; + ImGuiID dock_right_id = 0; + + // Split dockspace: Left 30% | Center 50% | Right 20% + dock_left_id = ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Left, 0.30f, + nullptr, &dockspace_id); + dock_right_id = ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Right, + 0.29f, nullptr, &dockspace_id); + dock_center_id = dockspace_id; + + // Split right panel: Animations (top) and Prototype (bottom) + ImGuiID dock_right_top = 0; + ImGuiID dock_right_bottom = ImGui::DockBuilderSplitNode( + dock_right_id, ImGuiDir_Down, 0.50f, nullptr, &dock_right_top); + + // Dock windows + ImGui::DockBuilderDockWindow(" GFX Sheets", dock_left_id); + ImGui::DockBuilderDockWindow(" Sheet Editor", dock_center_id); + ImGui::DockBuilderDockWindow(" Animations", dock_right_top); + ImGui::DockBuilderDockWindow(" Prototype", dock_right_bottom); +} + +void LayoutManager::BuildPaletteLayout(ImGuiID dockspace_id) { + // TODO: [EditorManagerRefactor] Implement DockBuilder layout for Palette + // Editor + // + // Desired layout: + // - Left 25%: Group Manager (top) + ROM Palette Browser (bottom) + // - Center 50%: Main Palette Editor + // - Right 25%: SNES Palette (top) + Color Harmony Tools (bottom) + + ImGuiID dock_left_id = 0; + ImGuiID dock_center_id = 0; + ImGuiID dock_right_id = 0; + + // Split dockspace: Left 25% | Center 50% | Right 25% + dock_left_id = ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Left, 0.25f, + nullptr, &dockspace_id); + dock_right_id = ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Right, + 0.33f, nullptr, &dockspace_id); + dock_center_id = dockspace_id; + + // Split left panel: Group Manager (top) and ROM Browser (bottom) + ImGuiID dock_left_top = 0; + ImGuiID dock_left_bottom = ImGui::DockBuilderSplitNode( + dock_left_id, ImGuiDir_Down, 0.50f, nullptr, &dock_left_top); + + // Split right panel: SNES Palette (top) and Color Tools (bottom) + ImGuiID dock_right_top = 0; + ImGuiID dock_right_bottom = ImGui::DockBuilderSplitNode( + dock_right_id, ImGuiDir_Down, 0.50f, nullptr, &dock_right_top); + + // Dock windows + ImGui::DockBuilderDockWindow(" Group Manager", dock_left_top); + ImGui::DockBuilderDockWindow(" ROM Palette Browser", dock_left_bottom); + ImGui::DockBuilderDockWindow(" Palette Editor", dock_center_id); + ImGui::DockBuilderDockWindow(" SNES Palette", dock_right_top); + ImGui::DockBuilderDockWindow(" Color Harmony", dock_right_bottom); +} + +void LayoutManager::BuildScreenLayout(ImGuiID dockspace_id) { + // TODO: [EditorManagerRefactor] Implement DockBuilder layout for Screen + // Editor + // + // Desired layout: + // - Grid layout with Overworld Map in center (larger) + // - Corners: Dungeon Maps, Title Screen, Inventory Menu, Naming Screen + + ImGuiID dock_top = 0; + ImGuiID dock_bottom = ImGui::DockBuilderSplitNode( + dockspace_id, ImGuiDir_Down, 0.50f, nullptr, &dock_top); + + // Split top: left and right + ImGuiID dock_top_left = 0; + ImGuiID dock_top_right = ImGui::DockBuilderSplitNode( + dock_top, ImGuiDir_Right, 0.50f, nullptr, &dock_top_left); + + // Split bottom: left and right + ImGuiID dock_bottom_left = 0; + ImGuiID dock_bottom_right = ImGui::DockBuilderSplitNode( + dock_bottom, ImGuiDir_Right, 0.50f, nullptr, &dock_bottom_left); + + // Dock windows in grid + ImGui::DockBuilderDockWindow(" Dungeon Map Editor", dock_top_left); + ImGui::DockBuilderDockWindow(" Title Screen", dock_top_right); + ImGui::DockBuilderDockWindow(" Inventory Menu", dock_bottom_left); + ImGui::DockBuilderDockWindow(" Naming Screen", dock_bottom_right); + + // Overworld Map could be floating or in center - let user configure +} + +void LayoutManager::BuildMusicLayout(ImGuiID dockspace_id) { + // TODO: [EditorManagerRefactor] Implement DockBuilder layout for Music Editor + // + // Desired layout: + // - Left 30%: Music Tracker + // - Center 45%: Instrument Editor + // - Right 25%: Assembly/Export + + ImGuiID dock_left_id = 0; + ImGuiID dock_center_id = 0; + ImGuiID dock_right_id = 0; + + // Split dockspace: Left 30% | Center 45% | Right 25% + dock_left_id = ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Left, 0.30f, + nullptr, &dockspace_id); + dock_right_id = ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Right, + 0.36f, nullptr, &dockspace_id); + dock_center_id = dockspace_id; + + // Dock windows + ImGui::DockBuilderDockWindow(" Music Tracker", dock_left_id); + ImGui::DockBuilderDockWindow(" Instrument Editor", dock_center_id); + ImGui::DockBuilderDockWindow(" Music Assembly", dock_right_id); +} + +void LayoutManager::BuildSpriteLayout(ImGuiID dockspace_id) { + // TODO: [EditorManagerRefactor] Implement DockBuilder layout for Sprite + // Editor + // + // Desired layout: + // - Left 50%: Vanilla Sprites + // - Right 50%: Custom Sprites + + ImGuiID dock_left_id = 0; + ImGuiID dock_right_id = ImGui::DockBuilderSplitNode( + dockspace_id, ImGuiDir_Right, 0.50f, nullptr, &dock_left_id); + + // Dock windows + ImGui::DockBuilderDockWindow(" Vanilla Sprites", dock_left_id); + ImGui::DockBuilderDockWindow(" Custom Sprites", dock_right_id); +} + +void LayoutManager::BuildMessageLayout(ImGuiID dockspace_id) { + // TODO: [EditorManagerRefactor] Implement DockBuilder layout for Message + // Editor + // + // Desired layout: + // - Left 25%: Message List + // - Center 50%: Message Editor + // - Right 25%: Font Atlas (top) + Dictionary (bottom) + + ImGuiID dock_left_id = 0; + ImGuiID dock_center_id = 0; + ImGuiID dock_right_id = 0; + + // Split dockspace: Left 25% | Center 50% | Right 25% + dock_left_id = ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Left, 0.25f, + nullptr, &dockspace_id); + dock_right_id = ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Right, + 0.33f, nullptr, &dockspace_id); + dock_center_id = dockspace_id; + + // Split right panel: Font Atlas (top) and Dictionary (bottom) + ImGuiID dock_right_top = 0; + ImGuiID dock_right_bottom = ImGui::DockBuilderSplitNode( + dock_right_id, ImGuiDir_Down, 0.50f, nullptr, &dock_right_top); + + // Dock windows + ImGui::DockBuilderDockWindow(" Message List", dock_left_id); + ImGui::DockBuilderDockWindow(" Message Editor", dock_center_id); + ImGui::DockBuilderDockWindow(" Font Atlas", dock_right_top); + ImGui::DockBuilderDockWindow(" Dictionary", dock_right_bottom); +} + +void LayoutManager::BuildAssemblyLayout(ImGuiID dockspace_id) { + // TODO: [EditorManagerRefactor] Implement DockBuilder layout for Assembly + // Editor + // + // Desired layout: + // - Left 60%: Code Editor + // - Right 40%: Output/Errors (top) + Documentation (bottom) + + ImGuiID dock_left_id = 0; + ImGuiID dock_right_id = ImGui::DockBuilderSplitNode( + dockspace_id, ImGuiDir_Right, 0.40f, nullptr, &dock_left_id); + + // Split right panel: Output (top) and Docs (bottom) + ImGuiID dock_right_top = 0; + ImGuiID dock_right_bottom = ImGui::DockBuilderSplitNode( + dock_right_id, ImGuiDir_Down, 0.50f, nullptr, &dock_right_top); + + // Dock windows + ImGui::DockBuilderDockWindow(" Assembly Editor", dock_left_id); + ImGui::DockBuilderDockWindow(" Assembly Output", dock_right_top); + ImGui::DockBuilderDockWindow(" Assembly Docs", dock_right_bottom); +} + +void LayoutManager::BuildSettingsLayout(ImGuiID dockspace_id) { + // TODO: [EditorManagerRefactor] Implement DockBuilder layout for Settings + // Editor + // + // Desired layout: + // - Left 25%: Category navigation (vertical list) + // - Right 75%: Settings content for selected category + + ImGuiID dock_left_id = 0; + ImGuiID dock_right_id = ImGui::DockBuilderSplitNode( + dockspace_id, ImGuiDir_Right, 0.75f, nullptr, &dock_left_id); + + // Dock windows + ImGui::DockBuilderDockWindow(" Settings Navigation", dock_left_id); + ImGui::DockBuilderDockWindow(" Settings Content", dock_right_id); +} + +void LayoutManager::SaveCurrentLayout(const std::string& name) { + // TODO: [EditorManagerRefactor] Implement layout saving to file + // Use ImGui::SaveIniSettingsToMemory() and write to custom file + LOG_INFO("LayoutManager", "Saving layout: %s", name.c_str()); +} + +void LayoutManager::LoadLayout(const std::string& name) { + // TODO: [EditorManagerRefactor] Implement layout loading from file + // Use ImGui::LoadIniSettingsFromMemory() and read from custom file + LOG_INFO("LayoutManager", "Loading layout: %s", name.c_str()); +} + +void LayoutManager::ResetToDefaultLayout(EditorType type) { + layouts_initialized_[type] = false; + LOG_INFO("LayoutManager", "Reset layout for editor type %d", + static_cast(type)); +} + +bool LayoutManager::IsLayoutInitialized(EditorType type) const { + auto it = layouts_initialized_.find(type); + return it != layouts_initialized_.end() && it->second; +} + +void LayoutManager::MarkLayoutInitialized(EditorType type) { + layouts_initialized_[type] = true; + LOG_INFO("LayoutManager", "Marked layout for editor type %d as initialized", + static_cast(type)); +} + +void LayoutManager::ClearInitializationFlags() { + layouts_initialized_.clear(); + LOG_INFO("LayoutManager", "Cleared all layout initialization flags"); +} + +} // namespace editor +} // namespace yaze + diff --git a/src/app/editor/ui/layout_manager.h b/src/app/editor/ui/layout_manager.h new file mode 100644 index 00000000..6a64d9c1 --- /dev/null +++ b/src/app/editor/ui/layout_manager.h @@ -0,0 +1,96 @@ +#ifndef YAZE_APP_EDITOR_UI_LAYOUT_MANAGER_H_ +#define YAZE_APP_EDITOR_UI_LAYOUT_MANAGER_H_ + +#include +#include + +#include "app/editor/editor.h" +#include "imgui/imgui.h" + +namespace yaze { +namespace editor { + +/** + * @class LayoutManager + * @brief Manages ImGui DockBuilder layouts for each editor type + * + * Provides professional default layouts using ImGui's DockBuilder API, + * similar to VSCode's workspace layouts. Each editor type has a custom + * layout optimized for its workflow. + * + * Features: + * - Per-editor default layouts (Overworld, Dungeon, Graphics, etc.) + * - Layout persistence and restoration + * - Workspace presets (Developer, Designer, Modder) + * - Dynamic layout initialization on first editor switch + */ +class LayoutManager { + public: + LayoutManager() = default; + ~LayoutManager() = default; + + /** + * @brief Initialize the default layout for a specific editor type + * @param type The editor type to initialize + * @param dockspace_id The ImGui dockspace ID to build the layout in + */ + void InitializeEditorLayout(EditorType type, ImGuiID dockspace_id); + + /** + * @brief Save the current layout with a custom name + * @param name The name to save the layout under + */ + void SaveCurrentLayout(const std::string& name); + + /** + * @brief Load a saved layout by name + * @param name The name of the layout to load + */ + void LoadLayout(const std::string& name); + + /** + * @brief Reset the layout for an editor to its default + * @param type The editor type to reset + */ + void ResetToDefaultLayout(EditorType type); + + /** + * @brief Check if a layout has been initialized for an editor + * @param type The editor type to check + * @return True if layout is initialized + */ + bool IsLayoutInitialized(EditorType type) const; + + /** + * @brief Mark a layout as initialized + * @param type The editor type to mark + */ + void MarkLayoutInitialized(EditorType type); + + /** + * @brief Clear all initialization flags (for testing) + */ + void ClearInitializationFlags(); + + private: + // DockBuilder layout implementations for each editor type + void BuildOverworldLayout(ImGuiID dockspace_id); + void BuildDungeonLayout(ImGuiID dockspace_id); + void BuildGraphicsLayout(ImGuiID dockspace_id); + void BuildPaletteLayout(ImGuiID dockspace_id); + void BuildScreenLayout(ImGuiID dockspace_id); + void BuildMusicLayout(ImGuiID dockspace_id); + void BuildSpriteLayout(ImGuiID dockspace_id); + void BuildMessageLayout(ImGuiID dockspace_id); + void BuildAssemblyLayout(ImGuiID dockspace_id); + void BuildSettingsLayout(ImGuiID dockspace_id); + + // Track which layouts have been initialized + std::unordered_map layouts_initialized_; +}; + +} // namespace editor +} // namespace yaze + +#endif // YAZE_APP_EDITOR_UI_LAYOUT_MANAGER_H_ + diff --git a/src/app/editor/ui/ui_coordinator.cc b/src/app/editor/ui/ui_coordinator.cc index 93485240..f4f1f86d 100644 --- a/src/app/editor/ui/ui_coordinator.cc +++ b/src/app/editor/ui/ui_coordinator.cc @@ -107,6 +107,7 @@ void UICoordinator::DrawAllUI() { // Draw UI windows and dialogs // Session dialogs are drawn by SessionCoordinator separately to avoid duplication DrawCommandPalette(); // Ctrl+Shift+P + DrawGlobalSearch(); // Ctrl+Shift+K DrawLayoutPresets(); // Layout preset dialogs DrawWelcomeScreen(); // Welcome screen DrawProjectHelp(); // Project help @@ -304,6 +305,10 @@ void UICoordinator::DrawWelcomeScreen() { // Draw the welcome screen LOG_INFO("UICoordinator", "Rendering welcome screen window"); + // Reset first show flag to override ImGui ini state + // This ensures the window appears even if imgui.ini has it hidden + welcome_screen_->ResetFirstShow(); + // Update recent projects before showing welcome_screen_->RefreshRecentProjects(); @@ -670,6 +675,153 @@ void UICoordinator::DrawCommandPalette() { } } +void UICoordinator::DrawGlobalSearch() { + if (!show_global_search_) return; + + using namespace ImGui; + auto& theme = gui::ThemeManager::Get().GetCurrentTheme(); + + SetNextWindowPos(GetMainViewport()->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); + SetNextWindowSize(ImVec2(900, 700), ImGuiCond_FirstUseEver); + + bool show_search = true; + if (Begin(absl::StrFormat("%s Global Search", ICON_MD_SEARCH).c_str(), + &show_search, ImGuiWindowFlags_NoCollapse)) { + + // Search input with focus management + SetNextItemWidth(-100); + if (IsWindowAppearing()) { + SetKeyboardFocusHere(); + } + + bool input_changed = InputTextWithHint( + "##global_search_query", + absl::StrFormat("%s Search ROM data, cards, editors, resources...", ICON_MD_SEARCH).c_str(), + global_search_query_, IM_ARRAYSIZE(global_search_query_)); + + SameLine(); + if (Button(absl::StrFormat("%s Clear", ICON_MD_CLEAR).c_str())) { + global_search_query_[0] = '\0'; + input_changed = true; + } + + Separator(); + + // Search results organized by category + if (BeginTabBar("SearchCategories")) { + // TODO: [EditorManagerRefactor] Implement actual ROM data searching + // This should search through: + // - Editor cards (all registered cards across all editors) + // - ROM resources (palettes, graphics, sprites, etc.) + // - Text strings (messages, dialogue) + // - Map names, room names, sprite names + // - Memory addresses and labels + + if (BeginTabItem(absl::StrFormat("%s All Results", ICON_MD_LIST).c_str())) { + if (global_search_query_[0] != '\0') { + // Search through editor cards + TextColored(gui::ConvertColorToImVec4(theme.info), + "%s Editor Cards", ICON_MD_DASHBOARD); + Separator(); + + // Get current session ID from editor manager + size_t current_session_id = 0; + if (editor_manager_) { + current_session_id = editor_manager_->GetCurrentSessionId(); + } + + // Get all cards in current session + auto card_ids = card_registry_.GetCardsInSession(current_session_id); + bool found_cards = false; + + for (const auto& card_id : card_ids) { + const auto* card_info = card_registry_.GetCardInfo(current_session_id, card_id); + if (!card_info) continue; + + std::string search_lower = global_search_query_; + std::string card_lower = card_info->display_name; + std::transform(search_lower.begin(), search_lower.end(), + search_lower.begin(), ::tolower); + std::transform(card_lower.begin(), card_lower.end(), + card_lower.begin(), ::tolower); + + if (card_lower.find(search_lower) != std::string::npos) { + if (Selectable(absl::StrFormat("%s %s - %s", + card_info->icon.c_str(), + card_info->display_name.c_str(), + card_info->category.c_str()).c_str())) { + // Show the card when selected + card_registry_.ShowCard(current_session_id, card_id); + show_global_search_ = false; + } + if (IsItemHovered()) { + BeginTooltip(); + Text("Category: %s", card_info->category.c_str()); + Text("Shortcut: %s", card_info->shortcut_hint.c_str()); + Text("Click to open"); + EndTooltip(); + } + found_cards = true; + } + } + + if (!found_cards) { + PushStyleColor(ImGuiCol_Text, gui::ConvertColorToImVec4(theme.text_disabled)); + Text("No cards found matching '%s'", global_search_query_); + PopStyleColor(); + } + + Spacing(); + Spacing(); + + // TODO: [EditorManagerRefactor] Add more search categories: + // - ROM Resources (palettes, graphics, sprites) + // - Text/Messages + // - Map/Room names + // - Memory addresses + } else { + PushStyleColor(ImGuiCol_Text, gui::ConvertColorToImVec4(theme.text_disabled)); + Text("%s Enter search query to find ROM data, cards, and resources", + ICON_MD_INFO); + PopStyleColor(); + } + EndTabItem(); + } + + if (BeginTabItem(absl::StrFormat("%s Cards", ICON_MD_DASHBOARD).c_str())) { + Text("Card-specific search coming soon..."); + EndTabItem(); + } + + if (BeginTabItem(absl::StrFormat("%s ROM Data", ICON_MD_STORAGE).c_str())) { + Text("ROM data search coming soon..."); + EndTabItem(); + } + + if (BeginTabItem(absl::StrFormat("%s Text", ICON_MD_TEXT_FIELDS).c_str())) { + Text("Text search coming soon..."); + EndTabItem(); + } + + EndTabBar(); + } + + // Status bar + Separator(); + Text("%s Global Search", ICON_MD_INFO); + SameLine(); + PushStyleColor(ImGuiCol_Text, gui::ConvertColorToImVec4(theme.text_disabled)); + Text("| Currently searches: Editor Cards | More categories coming soon"); + PopStyleColor(); + } + End(); + + // Update visibility state + if (!show_search) { + show_global_search_ = false; + } +} + } // namespace editor } // namespace yaze diff --git a/src/app/editor/ui/welcome_screen.cc b/src/app/editor/ui/welcome_screen.cc index d71778e2..57139aca 100644 --- a/src/app/editor/ui/welcome_screen.cc +++ b/src/app/editor/ui/welcome_screen.cc @@ -160,6 +160,15 @@ bool WelcomeScreen::Show(bool* p_open) { ImGui::SetNextWindowPos(center, ImGuiCond_Always, ImVec2(0.5f, 0.5f)); ImGui::SetNextWindowSize(ImVec2(width, height), ImGuiCond_Always); + // CRITICAL: Override ImGui's saved window state from imgui.ini + // Without this, ImGui will restore the last saved state (hidden/collapsed) + // even when our logic says the window should be visible + if (first_show_attempt_) { + ImGui::SetNextWindowCollapsed(false); // Force window to be expanded + ImGui::SetNextWindowFocus(); // Bring window to front + first_show_attempt_ = false; + } + ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove; diff --git a/src/app/editor/ui/welcome_screen.h b/src/app/editor/ui/welcome_screen.h index e9427a0a..f29338fb 100644 --- a/src/app/editor/ui/welcome_screen.h +++ b/src/app/editor/ui/welcome_screen.h @@ -79,6 +79,11 @@ class WelcomeScreen { */ void MarkManuallyClosed() { manually_closed_ = true; } + /** + * @brief Reset first show flag (for testing/forcing display) + */ + void ResetFirstShow() { first_show_attempt_ = true; } + private: void DrawHeader(); void DrawQuickActions(); @@ -90,6 +95,7 @@ class WelcomeScreen { std::vector recent_projects_; bool manually_closed_ = false; + bool first_show_attempt_ = true; // Override ImGui ini state on first display // Callbacks std::function open_rom_callback_;