feat(editor): introduce LayoutManager for enhanced editor layouts

- Added LayoutManager to manage ImGui DockBuilder layouts for various editor types, providing professional default layouts similar to VSCode.
- Integrated layout initialization and persistence features, allowing users to save and load custom layouts.
- Updated EditorManager to initialize LayoutManager and set default layouts upon editor activation.

Benefits:
- Improves user experience by offering tailored layouts for different editing tasks.
- Enhances maintainability by centralizing layout management and initialization logic within the new LayoutManager class.
This commit is contained in:
scawful
2025-10-15 16:35:32 -04:00
parent 551b1e5d5a
commit 4de8f711f3
9 changed files with 694 additions and 4 deletions

View File

@@ -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

View File

@@ -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<LayoutManager>();
// 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_) {

View File

@@ -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<UICoordinator> ui_coordinator_;
WindowDelegate window_delegate_;
std::unique_ptr<SessionCoordinator> session_coordinator_;
std::unique_ptr<LayoutManager> layout_manager_; // DockBuilder layout management
float autosave_timer_ = 0.0f;

View File

@@ -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();

View File

@@ -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<int>(type));
return;
}
LOG_INFO("LayoutManager", "Initializing layout for editor type %d",
static_cast<int>(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<int>(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<int>(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<int>(type));
}
void LayoutManager::ClearInitializationFlags() {
layouts_initialized_.clear();
LOG_INFO("LayoutManager", "Cleared all layout initialization flags");
}
} // namespace editor
} // namespace yaze

View File

@@ -0,0 +1,96 @@
#ifndef YAZE_APP_EDITOR_UI_LAYOUT_MANAGER_H_
#define YAZE_APP_EDITOR_UI_LAYOUT_MANAGER_H_
#include <string>
#include <unordered_map>
#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<EditorType, bool> layouts_initialized_;
};
} // namespace editor
} // namespace yaze
#endif // YAZE_APP_EDITOR_UI_LAYOUT_MANAGER_H_

View File

@@ -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

View File

@@ -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;

View File

@@ -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<RecentProject> recent_projects_;
bool manually_closed_ = false;
bool first_show_attempt_ = true; // Override ImGui ini state on first display
// Callbacks
std::function<void()> open_rom_callback_;