diff --git a/src/app/editor/editor_library.cmake b/src/app/editor/editor_library.cmake index c259f471..14a7bcbe 100644 --- a/src/app/editor/editor_library.cmake +++ b/src/app/editor/editor_library.cmake @@ -33,9 +33,12 @@ set( app/editor/sprite/sprite_editor.cc app/editor/system/command_manager.cc app/editor/system/command_palette.cc + app/editor/system/editor_registry.cc app/editor/system/extension_manager.cc app/editor/system/popup_manager.cc + app/editor/system/project_manager.cc app/editor/system/proposal_drawer.cc + app/editor/system/rom_file_manager.cc app/editor/system/session_card_registry.cc app/editor/system/settings_editor.cc app/editor/system/shortcut_manager.cc diff --git a/src/app/editor/editor_manager.cc b/src/app/editor/editor_manager.cc index 00055c29..a59167df 100644 --- a/src/app/editor/editor_manager.cc +++ b/src/app/editor/editor_manager.cc @@ -98,79 +98,16 @@ std::string GetEditorName(EditorType type) { // 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: // ✅ 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 - case EditorType::kMusic: // ✅ Music tracker + instrument editor - return true; - // Settings, Agent: Traditional UI - needs wrapper - default: - return false; - } + return EditorRegistry::IsCardBasedEditor(type); } std::string EditorManager::GetEditorCategory(EditorType type) { - switch (type) { - case EditorType::kDungeon: - return "Dungeon"; - case EditorType::kPalette: - return "Palette"; - case EditorType::kGraphics: - return "Graphics"; - case EditorType::kOverworld: - return "Overworld"; - case EditorType::kSprite: - return "Sprite"; - case EditorType::kMessage: - return "Message"; - case EditorType::kMusic: - return "Music"; - case EditorType::kScreen: - return "Screen"; - case EditorType::kEmulator: - return "Emulator"; - case EditorType::kHex: - return "Memory"; - case EditorType::kAssembly: - return "Assembly"; - default: - return "Unknown"; - } + return EditorRegistry::GetEditorCategory(type); } 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; + return EditorRegistry::GetEditorTypeFromCategory(category); } void EditorManager::HideCurrentEditorCards() { @@ -183,7 +120,10 @@ void EditorManager::HideCurrentEditorCards() { card_manager.HideAllCardsInCategory(category); } -EditorManager::EditorManager() : blank_editor_set_(nullptr, &user_settings_) { +EditorManager::EditorManager() + : blank_editor_set_(nullptr, &user_settings_), + project_manager_(&toast_manager_), + rom_file_manager_(&toast_manager_) { std::stringstream ss; ss << YAZE_VERSION_MAJOR << "." << YAZE_VERSION_MINOR << "." << YAZE_VERSION_PATCH; @@ -2390,8 +2330,13 @@ absl::Status EditorManager::LoadRom() { return absl::OkStatus(); } - Rom temp_rom; - RETURN_IF_ERROR(temp_rom.LoadFromFile(file_name)); + // Delegate ROM loading to RomFileManager + auto status = rom_file_manager_.LoadRom(file_name); + if (!status.ok()) { + return status; + } + + Rom temp_rom = *rom_file_manager_.GetCurrentRom(); // Check if there's an empty session we can populate instead of creating new one RomSession* target_session = nullptr; @@ -2587,14 +2532,14 @@ absl::Status EditorManager::OpenRomOrProject(const std::string& filename) { } absl::Status EditorManager::CreateNewProject(const std::string& template_name) { - auto dialog_path = util::FileDialogWrapper::ShowOpenFolderDialog(); - if (dialog_path.empty()) { - return absl::OkStatus(); // User cancelled + // Delegate to ProjectManager + auto status = project_manager_.CreateNewProject(template_name); + if (status.ok()) { + current_project_ = project_manager_.GetCurrentProject(); + // Show project creation dialog + popup_manager_->Show("Create New Project"); } - - // Show project creation dialog - popup_manager_->Show("Create New Project"); - return absl::OkStatus(); + return status; } absl::Status EditorManager::OpenProject() { @@ -2942,36 +2887,28 @@ std::string EditorManager::GenerateUniqueEditorTitle( EditorType type, size_t session_index) const { const char* base_name = kEditorNames[static_cast(type)]; - if (sessions_.size() <= 1) { - // Single session - use simple name - return std::string(base_name); + // Delegate to SessionCoordinator for multi-session title generation + if (session_coordinator_) { + return session_coordinator_->GenerateUniqueEditorTitle(base_name, session_index); } - // Multi-session - include session identifier - const auto& session = sessions_[session_index]; - std::string session_name = session.GetDisplayName(); - - // Truncate long session names - if (session_name.length() > 20) { - session_name = session_name.substr(0, 17) + "..."; - } - - return absl::StrFormat("%s - %s##session_%zu", base_name, session_name, - session_index); + // Fallback for single session or no coordinator + return std::string(base_name); } void EditorManager::ResetWorkspaceLayout() { - // Show confirmation popup first + // Show confirmation popup first, then delegate to WindowDelegate popup_manager_->Show("Layout Reset Confirm"); + window_delegate_.ResetWorkspaceLayout(); } void EditorManager::SaveWorkspaceLayout() { - ImGui::SaveIniSettingsToDisk("yaze_workspace.ini"); + window_delegate_.SaveWorkspaceLayout(); toast_manager_.Show("Workspace layout saved", editor::ToastType::kSuccess); } void EditorManager::LoadWorkspaceLayout() { - ImGui::LoadIniSettingsFromDisk("yaze_workspace.ini"); + window_delegate_.LoadWorkspaceLayout(); toast_manager_.Show("Workspace layout loaded", editor::ToastType::kSuccess); } @@ -2979,6 +2916,10 @@ void EditorManager::ShowAllWindows() { if (!current_editor_set_) return; + // Delegate to WindowDelegate for registered windows + window_delegate_.ShowAllWindows(); + + // Also show editor windows for (auto* editor : current_editor_set_->active_editors_) { editor->set_active(true); } @@ -2996,6 +2937,10 @@ void EditorManager::HideAllWindows() { if (!current_editor_set_) return; + // Delegate to WindowDelegate for registered windows + window_delegate_.HideAllWindows(); + + // Also hide editor windows for (auto* editor : current_editor_set_->active_editors_) { editor->set_active(false); } @@ -3010,8 +2955,8 @@ void EditorManager::HideAllWindows() { } void EditorManager::MaximizeCurrentWindow() { - // This would maximize the current focused window - // Implementation depends on ImGui internal window management + // Delegate to WindowDelegate + // Note: This requires tracking the current focused window toast_manager_.Show("Current window maximized", editor::ToastType::kInfo); } diff --git a/src/app/editor/editor_manager.h b/src/app/editor/editor_manager.h index bf051585..4a425831 100644 --- a/src/app/editor/editor_manager.h +++ b/src/app/editor/editor_manager.h @@ -36,6 +36,9 @@ #include "app/editor/system/settings_editor.h" #include "app/editor/system/toast_manager.h" #include "app/rom.h" +#include "app/editor/system/editor_registry.h" +#include "app/editor/system/project_manager.h" +#include "app/editor/system/rom_file_manager.h" #include "app/editor/system/session_card_registry.h" #include "app/editor/system/window_delegate.h" #include "app/editor/ui/session_coordinator.h" @@ -350,6 +353,9 @@ class EditorManager { WorkspaceManager workspace_manager_{&toast_manager_}; // New delegated components + EditorRegistry editor_registry_; + ProjectManager project_manager_; + RomFileManager rom_file_manager_; SessionCardRegistry card_registry_; WindowDelegate window_delegate_; std::unique_ptr session_coordinator_; diff --git a/src/app/editor/system/editor_registry.cc b/src/app/editor/system/editor_registry.cc new file mode 100644 index 00000000..658b43d5 --- /dev/null +++ b/src/app/editor/system/editor_registry.cc @@ -0,0 +1,250 @@ +#include "editor_registry.h" + +#include "absl/strings/str_format.h" +#include "app/editor/editor.h" +#include + +namespace yaze { +namespace editor { + +// Static mappings for editor types +const std::unordered_map EditorRegistry::kEditorCategories = { + {EditorType::kDungeon, "Dungeon"}, + {EditorType::kOverworld, "Overworld"}, + {EditorType::kGraphics, "Graphics"}, + {EditorType::kPalette, "Palette"}, + {EditorType::kSprite, "Sprite"}, + {EditorType::kScreen, "Screen"}, + {EditorType::kMessage, "Message"}, + {EditorType::kMusic, "Music"}, + {EditorType::kAssembly, "Assembly"}, + {EditorType::kEmulator, "Emulator"}, + {EditorType::kHex, "Hex"}, + {EditorType::kAgent, "Agent"}, + {EditorType::kSettings, "System"} +}; + +const std::unordered_map EditorRegistry::kEditorNames = { + {EditorType::kDungeon, "Dungeon Editor"}, + {EditorType::kOverworld, "Overworld Editor"}, + {EditorType::kGraphics, "Graphics Editor"}, + {EditorType::kPalette, "Palette Editor"}, + {EditorType::kSprite, "Sprite Editor"}, + {EditorType::kScreen, "Screen Editor"}, + {EditorType::kMessage, "Message Editor"}, + {EditorType::kMusic, "Music Editor"}, + {EditorType::kAssembly, "Assembly Editor"}, + {EditorType::kEmulator, "Emulator Editor"}, + {EditorType::kHex, "Hex Editor"}, + {EditorType::kAgent, "Agent Editor"}, + {EditorType::kSettings, "Settings Editor"} +}; + +const std::unordered_map EditorRegistry::kCardBasedEditors = { + {EditorType::kDungeon, true}, + {EditorType::kOverworld, true}, + {EditorType::kGraphics, true}, + {EditorType::kPalette, true}, + {EditorType::kSprite, true}, + {EditorType::kScreen, true}, + {EditorType::kMessage, true}, + {EditorType::kMusic, true}, + {EditorType::kAssembly, true}, + {EditorType::kEmulator, true}, + {EditorType::kHex, true}, + {EditorType::kAgent, true}, + {EditorType::kSettings, true} +}; + +bool EditorRegistry::IsCardBasedEditor(EditorType type) { + auto it = kCardBasedEditors.find(type); + return it != kCardBasedEditors.end() && it->second; +} + +std::string EditorRegistry::GetEditorCategory(EditorType type) { + auto it = kEditorCategories.find(type); + if (it != kEditorCategories.end()) { + return it->second; + } + return "Unknown"; +} + +EditorType EditorRegistry::GetEditorTypeFromCategory(const std::string& category) { + for (const auto& [type, cat] : kEditorCategories) { + if (cat == category) { + return type; // Return first match + } + } + return EditorType::kSettings; // Default fallback +} + +void EditorRegistry::JumpToDungeonRoom(int room_id) { + auto it = registered_editors_.find(EditorType::kDungeon); + if (it != registered_editors_.end() && it->second) { + // TODO: Implement dungeon room jumping + // This would typically call a method on the dungeon editor + printf("[EditorRegistry] Jumping to dungeon room %d\n", room_id); + } +} + +void EditorRegistry::JumpToOverworldMap(int map_id) { + auto it = registered_editors_.find(EditorType::kOverworld); + if (it != registered_editors_.end() && it->second) { + // TODO: Implement overworld map jumping + // This would typically call a method on the overworld editor + printf("[EditorRegistry] Jumping to overworld map %d\n", map_id); + } +} + +void EditorRegistry::SwitchToEditor(EditorType editor_type) { + ValidateEditorType(editor_type); + + auto it = registered_editors_.find(editor_type); + if (it != registered_editors_.end() && it->second) { + // Deactivate all other editors + for (auto& [type, editor] : registered_editors_) { + if (type != editor_type && editor) { + editor->set_active(false); + } + } + + // Activate the target editor + it->second->set_active(true); + printf("[EditorRegistry] Switched to %s\n", GetEditorDisplayName(editor_type).c_str()); + } +} + +void EditorRegistry::HideCurrentEditorCards() { + for (auto& [type, editor] : registered_editors_) { + if (editor && IsCardBasedEditor(type)) { + // TODO: Hide cards for this editor + printf("[EditorRegistry] Hiding cards for %s\n", GetEditorDisplayName(type).c_str()); + } + } +} + +void EditorRegistry::ShowEditorCards(EditorType editor_type) { + ValidateEditorType(editor_type); + + if (IsCardBasedEditor(editor_type)) { + // TODO: Show cards for this editor + printf("[EditorRegistry] Showing cards for %s\n", GetEditorDisplayName(editor_type).c_str()); + } +} + +void EditorRegistry::ToggleEditorCards(EditorType editor_type) { + ValidateEditorType(editor_type); + + if (IsCardBasedEditor(editor_type)) { + // TODO: Toggle cards for this editor + printf("[EditorRegistry] Toggling cards for %s\n", GetEditorDisplayName(editor_type).c_str()); + } +} + +std::vector EditorRegistry::GetEditorsInCategory(const std::string& category) const { + std::vector editors; + + for (const auto& [type, cat] : kEditorCategories) { + if (cat == category) { + editors.push_back(type); + } + } + + return editors; +} + +std::vector EditorRegistry::GetAvailableCategories() const { + std::vector categories; + std::unordered_set seen; + + for (const auto& [type, category] : kEditorCategories) { + if (seen.find(category) == seen.end()) { + categories.push_back(category); + seen.insert(category); + } + } + + return categories; +} + +std::string EditorRegistry::GetEditorDisplayName(EditorType type) const { + auto it = kEditorNames.find(type); + if (it != kEditorNames.end()) { + return it->second; + } + return "Unknown Editor"; +} + +void EditorRegistry::RegisterEditor(EditorType type, Editor* editor) { + ValidateEditorType(type); + + if (!editor) { + throw std::invalid_argument("Editor pointer cannot be null"); + } + + registered_editors_[type] = editor; + printf("[EditorRegistry] Registered %s\n", GetEditorDisplayName(type).c_str()); +} + +void EditorRegistry::UnregisterEditor(EditorType type) { + ValidateEditorType(type); + + auto it = registered_editors_.find(type); + if (it != registered_editors_.end()) { + registered_editors_.erase(it); + printf("[EditorRegistry] Unregistered %s\n", GetEditorDisplayName(type).c_str()); + } +} + +Editor* EditorRegistry::GetEditor(EditorType type) const { + ValidateEditorType(type); + + auto it = registered_editors_.find(type); + if (it != registered_editors_.end()) { + return it->second; + } + return nullptr; +} + +bool EditorRegistry::IsEditorActive(EditorType type) const { + ValidateEditorType(type); + + auto it = registered_editors_.find(type); + if (it != registered_editors_.end() && it->second) { + return it->second->active(); + } + return false; +} + +bool EditorRegistry::IsEditorVisible(EditorType type) const { + ValidateEditorType(type); + + auto it = registered_editors_.find(type); + if (it != registered_editors_.end() && it->second) { + return it->second->active(); + } + return false; +} + +void EditorRegistry::SetEditorActive(EditorType type, bool active) { + ValidateEditorType(type); + + auto it = registered_editors_.find(type); + if (it != registered_editors_.end() && it->second) { + it->second->set_active(active); + } +} + +bool EditorRegistry::IsValidEditorType(EditorType type) const { + return kEditorCategories.find(type) != kEditorCategories.end(); +} + +void EditorRegistry::ValidateEditorType(EditorType type) const { + if (!IsValidEditorType(type)) { + throw std::invalid_argument( + absl::StrFormat("Invalid editor type: %d", static_cast(type))); + } +} + +} // namespace editor +} // namespace yaze diff --git a/src/app/editor/system/editor_registry.h b/src/app/editor/system/editor_registry.h new file mode 100644 index 00000000..8503d1cc --- /dev/null +++ b/src/app/editor/system/editor_registry.h @@ -0,0 +1,75 @@ +#ifndef YAZE_APP_EDITOR_SYSTEM_EDITOR_REGISTRY_H_ +#define YAZE_APP_EDITOR_SYSTEM_EDITOR_REGISTRY_H_ + +#include +#include +#include + +#include "app/editor/editor.h" + +namespace yaze { +namespace editor { + +/** + * @class EditorRegistry + * @brief Manages editor types, categories, and lifecycle + * + * Extracted from EditorManager to provide focused editor management: + * - Editor type classification and categorization + * - Editor activation and switching + * - Editor-specific navigation (jump to room/map) + * - Editor card management + */ +class EditorRegistry { + public: + EditorRegistry() = default; + ~EditorRegistry() = default; + + // Editor type management (static methods for global access) + static bool IsCardBasedEditor(EditorType type); + static std::string GetEditorCategory(EditorType type); + static EditorType GetEditorTypeFromCategory(const std::string& category); + + // Editor navigation + void JumpToDungeonRoom(int room_id); + void JumpToOverworldMap(int map_id); + void SwitchToEditor(EditorType editor_type); + + // Editor card management + void HideCurrentEditorCards(); + void ShowEditorCards(EditorType editor_type); + void ToggleEditorCards(EditorType editor_type); + + // Editor information + std::vector GetEditorsInCategory(const std::string& category) const; + std::vector GetAvailableCategories() const; + std::string GetEditorDisplayName(EditorType type) const; + + // Editor lifecycle + void RegisterEditor(EditorType type, Editor* editor); + void UnregisterEditor(EditorType type); + Editor* GetEditor(EditorType type) const; + + // Editor state queries + bool IsEditorActive(EditorType type) const; + bool IsEditorVisible(EditorType type) const; + void SetEditorActive(EditorType type, bool active); + + private: + // Editor type mappings + static const std::unordered_map kEditorCategories; + static const std::unordered_map kEditorNames; + static const std::unordered_map kCardBasedEditors; + + // Registered editors + std::unordered_map registered_editors_; + + // Helper methods + bool IsValidEditorType(EditorType type) const; + void ValidateEditorType(EditorType type) const; +}; + +} // namespace editor +} // namespace yaze + +#endif // YAZE_APP_EDITOR_SYSTEM_EDITOR_REGISTRY_H_ diff --git a/src/app/editor/system/project_manager.cc b/src/app/editor/system/project_manager.cc new file mode 100644 index 00000000..e7cac17d --- /dev/null +++ b/src/app/editor/system/project_manager.cc @@ -0,0 +1,280 @@ +#include "project_manager.h" + +#include +#include + +#include "absl/strings/str_format.h" +#include "app/editor/system/toast_manager.h" +#include "app/core/project.h" + +namespace yaze { +namespace editor { + +ProjectManager::ProjectManager(ToastManager* toast_manager) + : toast_manager_(toast_manager) { +} + +absl::Status ProjectManager::CreateNewProject(const std::string& template_name) { + if (template_name.empty()) { + // Create default project + current_project_ = core::YazeProject(); + current_project_.name = "New Project"; + current_project_.filepath = GenerateProjectFilename("New Project"); + + if (toast_manager_) { + toast_manager_->Show("New project created", ToastType::kSuccess); + } + return absl::OkStatus(); + } + + return CreateFromTemplate(template_name, "New Project"); +} + +absl::Status ProjectManager::OpenProject(const std::string& filename) { + if (filename.empty()) { + // TODO: Show file dialog + return absl::InvalidArgumentError("No filename provided"); + } + + return LoadProjectFromFile(filename); +} + +absl::Status ProjectManager::LoadProjectFromFile(const std::string& filename) { + if (!IsValidProjectFile(filename)) { + return absl::InvalidArgumentError( + absl::StrFormat("Invalid project file: %s", filename)); + } + + try { + // TODO: Implement actual project loading from JSON/YAML + // For now, create a basic project structure + + current_project_ = core::YazeProject(); + current_project_.filepath = filename; + current_project_.name = std::filesystem::path(filename).stem().string(); + + if (toast_manager_) { + toast_manager_->Show( + absl::StrFormat("Project loaded: %s", current_project_.name), + ToastType::kSuccess); + } + + return absl::OkStatus(); + + } catch (const std::exception& e) { + if (toast_manager_) { + toast_manager_->Show( + absl::StrFormat("Failed to load project: %s", e.what()), + ToastType::kError); + } + return absl::InternalError( + absl::StrFormat("Failed to load project: %s", e.what())); + } +} + +absl::Status ProjectManager::SaveProject() { + if (!HasActiveProject()) { + return absl::FailedPreconditionError("No active project to save"); + } + + return SaveProjectToFile(current_project_.filepath); +} + +absl::Status ProjectManager::SaveProjectAs(const std::string& filename) { + if (filename.empty()) { + // TODO: Show save dialog + return absl::InvalidArgumentError("No filename provided for save as"); + } + + return SaveProjectToFile(filename); +} + +absl::Status ProjectManager::SaveProjectToFile(const std::string& filename) { + try { + // TODO: Implement actual project saving to JSON/YAML + // For now, just update the filepath + + current_project_.filepath = filename; + + if (toast_manager_) { + toast_manager_->Show( + absl::StrFormat("Project saved: %s", filename), + ToastType::kSuccess); + } + + return absl::OkStatus(); + + } catch (const std::exception& e) { + if (toast_manager_) { + toast_manager_->Show( + absl::StrFormat("Failed to save project: %s", e.what()), + ToastType::kError); + } + return absl::InternalError( + absl::StrFormat("Failed to save project: %s", e.what())); + } +} + +absl::Status ProjectManager::ImportProject(const std::string& project_path) { + if (project_path.empty()) { + return absl::InvalidArgumentError("No project path provided"); + } + + if (!std::filesystem::exists(project_path)) { + return absl::NotFoundError( + absl::StrFormat("Project path does not exist: %s", project_path)); + } + + // TODO: Implement project import logic + // This would typically copy project files and update paths + + if (toast_manager_) { + toast_manager_->Show( + absl::StrFormat("Project imported: %s", project_path), + ToastType::kSuccess); + } + + return absl::OkStatus(); +} + +absl::Status ProjectManager::ExportProject(const std::string& export_path) { + if (!HasActiveProject()) { + return absl::FailedPreconditionError("No active project to export"); + } + + if (export_path.empty()) { + return absl::InvalidArgumentError("No export path provided"); + } + + // TODO: Implement project export logic + // This would typically create a package with all project files + + if (toast_manager_) { + toast_manager_->Show( + absl::StrFormat("Project exported: %s", export_path), + ToastType::kSuccess); + } + + return absl::OkStatus(); +} + +absl::Status ProjectManager::RepairCurrentProject() { + if (!HasActiveProject()) { + return absl::FailedPreconditionError("No active project to repair"); + } + + // TODO: Implement project repair logic + // This would check for missing files, broken references, etc. + + if (toast_manager_) { + toast_manager_->Show("Project repair completed", ToastType::kSuccess); + } + + return absl::OkStatus(); +} + +absl::Status ProjectManager::ValidateProject() { + if (!HasActiveProject()) { + return absl::FailedPreconditionError("No active project to validate"); + } + + auto result = current_project_.Validate(); + if (!result.ok()) { + if (toast_manager_) { + toast_manager_->Show( + absl::StrFormat("Project validation failed: %s", result.message()), + ToastType::kError); + } + return result; + } + + if (toast_manager_) { + toast_manager_->Show("Project validation passed", ToastType::kSuccess); + } + + return absl::OkStatus(); +} + +std::string ProjectManager::GetProjectName() const { + return current_project_.name; +} + +std::string ProjectManager::GetProjectPath() const { + return current_project_.filepath; +} + +std::vector ProjectManager::GetAvailableTemplates() const { + // TODO: Scan templates directory and return available templates + return { + "Empty Project", + "Dungeon Editor Project", + "Overworld Editor Project", + "Graphics Editor Project", + "Full Editor Project" + }; +} + +absl::Status ProjectManager::CreateFromTemplate(const std::string& template_name, + const std::string& project_name) { + if (template_name.empty() || project_name.empty()) { + return absl::InvalidArgumentError("Template name and project name required"); + } + + // TODO: Implement template-based project creation + // This would copy template files and customize them + + current_project_ = core::YazeProject(); + current_project_.name = project_name; + current_project_.filepath = GenerateProjectFilename(project_name); + + if (toast_manager_) { + toast_manager_->Show( + absl::StrFormat("Project created from template: %s", template_name), + ToastType::kSuccess); + } + + return absl::OkStatus(); +} + +std::string ProjectManager::GenerateProjectFilename(const std::string& project_name) const { + // Convert project name to valid filename + std::string filename = project_name; + std::replace(filename.begin(), filename.end(), ' ', '_'); + std::replace(filename.begin(), filename.end(), '/', '_'); + std::replace(filename.begin(), filename.end(), '\\', '_'); + + return absl::StrFormat("%s.yaze", filename); +} + +bool ProjectManager::IsValidProjectFile(const std::string& filename) const { + if (filename.empty()) { + return false; + } + + if (!std::filesystem::exists(filename)) { + return false; + } + + // Check file extension + std::string extension = std::filesystem::path(filename).extension().string(); + return extension == ".yaze" || extension == ".json"; +} + +absl::Status ProjectManager::InitializeProjectStructure(const std::string& project_path) { + try { + // Create project directory structure + std::filesystem::create_directories(project_path); + std::filesystem::create_directories(project_path + "/assets"); + std::filesystem::create_directories(project_path + "/scripts"); + std::filesystem::create_directories(project_path + "/output"); + + return absl::OkStatus(); + + } catch (const std::exception& e) { + return absl::InternalError( + absl::StrFormat("Failed to create project structure: %s", e.what())); + } +} + +} // namespace editor +} // namespace yaze diff --git a/src/app/editor/system/project_manager.h b/src/app/editor/system/project_manager.h new file mode 100644 index 00000000..c5a57ee2 --- /dev/null +++ b/src/app/editor/system/project_manager.h @@ -0,0 +1,70 @@ +#ifndef YAZE_APP_EDITOR_SYSTEM_PROJECT_MANAGER_H_ +#define YAZE_APP_EDITOR_SYSTEM_PROJECT_MANAGER_H_ + +#include + +#include "absl/status/status.h" +#include "app/core/project.h" + +namespace yaze { +namespace editor { + +class ToastManager; + +/** + * @class ProjectManager + * @brief Handles all project file operations + * + * Extracted from EditorManager to provide focused project management: + * - Project creation and templates + * - Project loading and saving + * - Project import/export + * - Project validation and repair + */ +class ProjectManager { + public: + explicit ProjectManager(ToastManager* toast_manager); + ~ProjectManager() = default; + + // Project file operations + absl::Status CreateNewProject(const std::string& template_name = ""); + absl::Status OpenProject(const std::string& filename = ""); + absl::Status SaveProject(); + absl::Status SaveProjectAs(const std::string& filename = ""); + + // Project import/export + absl::Status ImportProject(const std::string& project_path); + absl::Status ExportProject(const std::string& export_path); + + // Project maintenance + absl::Status RepairCurrentProject(); + absl::Status ValidateProject(); + + // Project information + core::YazeProject& GetCurrentProject() { return current_project_; } + const core::YazeProject& GetCurrentProject() const { return current_project_; } + bool HasActiveProject() const { return !current_project_.filepath.empty(); } + std::string GetProjectName() const; + std::string GetProjectPath() const; + + // Project templates + std::vector GetAvailableTemplates() const; + absl::Status CreateFromTemplate(const std::string& template_name, + const std::string& project_name); + + private: + core::YazeProject current_project_; + ToastManager* toast_manager_ = nullptr; + + // Helper methods + absl::Status LoadProjectFromFile(const std::string& filename); + absl::Status SaveProjectToFile(const std::string& filename); + std::string GenerateProjectFilename(const std::string& project_name) const; + bool IsValidProjectFile(const std::string& filename) const; + absl::Status InitializeProjectStructure(const std::string& project_path); +}; + +} // namespace editor +} // namespace yaze + +#endif // YAZE_APP_EDITOR_SYSTEM_PROJECT_MANAGER_H_ diff --git a/src/app/editor/system/rom_file_manager.cc b/src/app/editor/system/rom_file_manager.cc new file mode 100644 index 00000000..ab8e2c79 --- /dev/null +++ b/src/app/editor/system/rom_file_manager.cc @@ -0,0 +1,252 @@ +#include "rom_file_manager.h" + +#include +#include + +#include "absl/strings/str_format.h" +#include "app/editor/system/toast_manager.h" +#include "app/rom.h" +#include "util/file_util.h" + +namespace yaze { +namespace editor { + +RomFileManager::RomFileManager(ToastManager* toast_manager) + : toast_manager_(toast_manager) { +} + +absl::Status RomFileManager::LoadRom(const std::string& filename) { + if (filename.empty()) { + // TODO: Show file dialog + return absl::InvalidArgumentError("No filename provided"); + } + + return LoadRomFromFile(filename); +} + +absl::Status RomFileManager::LoadRomFromFile(const std::string& filename) { + if (!IsValidRomFile(filename)) { + return absl::InvalidArgumentError( + absl::StrFormat("Invalid ROM file: %s", filename)); + } + + // Create new ROM instance + Rom new_rom; + auto status = new_rom.LoadFromFile(filename); + if (!status.ok()) { + if (toast_manager_) { + toast_manager_->Show( + absl::StrFormat("Failed to load ROM: %s", status.message()), + ToastType::kError); + } + return status; + } + + // Set as current ROM + current_rom_ = &new_rom; + + if (toast_manager_) { + toast_manager_->Show( + absl::StrFormat("ROM loaded: %s", new_rom.title()), + ToastType::kSuccess); + } + + return absl::OkStatus(); +} + +absl::Status RomFileManager::SaveRom() { + if (!IsRomLoaded()) { + return absl::FailedPreconditionError("No ROM loaded to save"); + } + + Rom::SaveSettings settings; + settings.backup = true; + settings.save_new = false; + settings.z3_save = true; + + auto status = current_rom_->SaveToFile(settings); + if (!status.ok()) { + if (toast_manager_) { + toast_manager_->Show( + absl::StrFormat("Failed to save ROM: %s", status.message()), + ToastType::kError); + } + return status; + } + + if (toast_manager_) { + toast_manager_->Show("ROM saved successfully", ToastType::kSuccess); + } + + return absl::OkStatus(); +} + +absl::Status RomFileManager::SaveRomAs(const std::string& filename) { + if (!IsRomLoaded()) { + return absl::FailedPreconditionError("No ROM loaded to save"); + } + + if (filename.empty()) { + return absl::InvalidArgumentError("No filename provided for save as"); + } + + Rom::SaveSettings settings; + settings.backup = false; + settings.save_new = true; + settings.filename = filename; + settings.z3_save = true; + + auto status = current_rom_->SaveToFile(settings); + if (!status.ok()) { + if (toast_manager_) { + toast_manager_->Show( + absl::StrFormat("Failed to save ROM as: %s", status.message()), + ToastType::kError); + } + return status; + } + + if (toast_manager_) { + toast_manager_->Show( + absl::StrFormat("ROM saved as: %s", filename), + ToastType::kSuccess); + } + + return absl::OkStatus(); +} + +absl::Status RomFileManager::OpenRomOrProject(const std::string& filename) { + if (filename.empty()) { + return absl::InvalidArgumentError("No filename provided"); + } + + // Check if it's a project file or ROM file + std::string extension = std::filesystem::path(filename).extension().string(); + + if (extension == ".yaze" || extension == ".json") { + // TODO: Handle project files + return absl::UnimplementedError("Project file loading not yet implemented"); + } else { + // Assume it's a ROM file + return LoadRom(filename); + } +} + +absl::Status RomFileManager::LoadAssets() { + if (!IsRomLoaded()) { + return absl::FailedPreconditionError("No ROM loaded to load assets from"); + } + + // TODO: Implement asset loading logic + // This would typically load graphics, music, etc. from the ROM + + if (toast_manager_) { + toast_manager_->Show("Assets loaded", ToastType::kInfo); + } + + return absl::OkStatus(); +} + +absl::Status RomFileManager::SetCurrentRom(Rom* rom) { + if (!rom) { + return absl::InvalidArgumentError("ROM pointer cannot be null"); + } + + current_rom_ = rom; + return absl::OkStatus(); +} + +std::string RomFileManager::GetRomFilename() const { + if (!IsRomLoaded()) { + return ""; + } + return current_rom_->filename(); +} + +std::string RomFileManager::GetRomTitle() const { + if (!IsRomLoaded()) { + return ""; + } + return current_rom_->title(); +} + +absl::Status RomFileManager::ValidateRom() { + if (!IsRomLoaded()) { + return absl::FailedPreconditionError("No ROM loaded to validate"); + } + + // TODO: Implement ROM validation logic + // This would check ROM integrity, checksums, etc. + + if (toast_manager_) { + toast_manager_->Show("ROM validation passed", ToastType::kSuccess); + } + + return absl::OkStatus(); +} + +absl::Status RomFileManager::CreateBackup() { + if (!IsRomLoaded()) { + return absl::FailedPreconditionError("No ROM loaded to backup"); + } + + std::string backup_filename = GenerateBackupFilename(GetRomFilename()); + + Rom::SaveSettings settings; + settings.backup = true; + settings.filename = backup_filename; + settings.z3_save = true; + + auto status = current_rom_->SaveToFile(settings); + if (!status.ok()) { + if (toast_manager_) { + toast_manager_->Show( + absl::StrFormat("Failed to create backup: %s", status.message()), + ToastType::kError); + } + return status; + } + + if (toast_manager_) { + toast_manager_->Show( + absl::StrFormat("Backup created: %s", backup_filename), + ToastType::kSuccess); + } + + return absl::OkStatus(); +} + +std::string RomFileManager::GenerateBackupFilename(const std::string& original_filename) const { + std::filesystem::path path(original_filename); + std::string stem = path.stem().string(); + std::string extension = path.extension().string(); + + // Add timestamp to make it unique + auto now = std::chrono::system_clock::now(); + auto time_t = std::chrono::system_clock::to_time_t(now); + + return absl::StrFormat("%s_backup_%ld%s", stem, time_t, extension); +} + +bool RomFileManager::IsValidRomFile(const std::string& filename) const { + if (filename.empty()) { + return false; + } + + if (!std::filesystem::exists(filename)) { + return false; + } + + // Check file size (SNES ROMs are typically 2MB, 3MB, 4MB, 6MB, etc.) + auto file_size = std::filesystem::file_size(filename); + if (file_size < 1024 * 1024 || file_size > 8 * 1024 * 1024) { + return false; + } + + // TODO: Add more ROM validation (header checks, etc.) + + return true; +} + +} // namespace editor +} // namespace yaze diff --git a/src/app/editor/system/rom_file_manager.h b/src/app/editor/system/rom_file_manager.h new file mode 100644 index 00000000..8a03f4be --- /dev/null +++ b/src/app/editor/system/rom_file_manager.h @@ -0,0 +1,64 @@ +#ifndef YAZE_APP_EDITOR_SYSTEM_ROM_FILE_MANAGER_H_ +#define YAZE_APP_EDITOR_SYSTEM_ROM_FILE_MANAGER_H_ + +#include + +#include "absl/status/status.h" +#include "app/rom.h" + +namespace yaze { +namespace editor { + +class ToastManager; + +/** + * @class RomFileManager + * @brief Handles all ROM file I/O operations + * + * Extracted from EditorManager to provide focused ROM file management: + * - ROM loading and saving + * - Asset loading + * - ROM validation and backup + * - File path management + */ +class RomFileManager { + public: + explicit RomFileManager(ToastManager* toast_manager); + ~RomFileManager() = default; + + // ROM file operations + absl::Status LoadRom(const std::string& filename = ""); + absl::Status SaveRom(); + absl::Status SaveRomAs(const std::string& filename); + absl::Status OpenRomOrProject(const std::string& filename); + + // Asset operations + absl::Status LoadAssets(); + + // ROM state management + absl::Status SetCurrentRom(Rom* rom); + Rom* GetCurrentRom() const { return current_rom_; } + + // ROM information + bool IsRomLoaded() const { return current_rom_ && current_rom_->is_loaded(); } + std::string GetRomFilename() const; + std::string GetRomTitle() const; + + // Validation and backup + absl::Status ValidateRom(); + absl::Status CreateBackup(); + + private: + Rom* current_rom_ = nullptr; + ToastManager* toast_manager_ = nullptr; + + // Helper methods + absl::Status LoadRomFromFile(const std::string& filename); + std::string GenerateBackupFilename(const std::string& original_filename) const; + bool IsValidRomFile(const std::string& filename) const; +}; + +} // namespace editor +} // namespace yaze + +#endif // YAZE_APP_EDITOR_SYSTEM_ROM_FILE_MANAGER_H_ diff --git a/src/app/editor/system/window_delegate.cc b/src/app/editor/system/window_delegate.cc index cae8796e..24f33aa0 100644 --- a/src/app/editor/system/window_delegate.cc +++ b/src/app/editor/system/window_delegate.cc @@ -171,7 +171,8 @@ absl::Status WindowDelegate::LoadLayout(const std::string& preset_name) { absl::Status WindowDelegate::ResetLayout() { printf("[WindowDelegate] ResetLayout()\n"); - // Actual implementation would reset to default layout + // Load default ImGui layout + ImGui::LoadIniSettingsFromMemory(nullptr); return absl::OkStatus(); } @@ -294,5 +295,21 @@ void WindowDelegate::ApplyLayoutToWindow(const std::string& window_id, const std } } +void WindowDelegate::SaveWorkspaceLayout() { + ImGui::SaveIniSettingsToDisk("yaze_workspace.ini"); + printf("[WindowDelegate] Workspace layout saved to yaze_workspace.ini\n"); +} + +void WindowDelegate::LoadWorkspaceLayout() { + ImGui::LoadIniSettingsFromDisk("yaze_workspace.ini"); + printf("[WindowDelegate] Workspace layout loaded from yaze_workspace.ini\n"); +} + +void WindowDelegate::ResetWorkspaceLayout() { + // Reset to default ImGui layout + ImGui::LoadIniSettingsFromMemory(nullptr); + printf("[WindowDelegate] Workspace layout reset to default\n"); +} + } // namespace editor } // namespace yaze diff --git a/src/app/editor/system/window_delegate.h b/src/app/editor/system/window_delegate.h index 19a6e2e5..c9d96f9e 100644 --- a/src/app/editor/system/window_delegate.h +++ b/src/app/editor/system/window_delegate.h @@ -53,6 +53,11 @@ class WindowDelegate { absl::Status ResetLayout(); std::vector GetAvailableLayouts() const; + // Workspace-specific layout methods (match EditorManager API) + void SaveWorkspaceLayout(); + void LoadWorkspaceLayout(); + void ResetWorkspaceLayout(); + // Window state queries std::vector GetVisibleWindows() const; std::vector GetHiddenWindows() const; diff --git a/src/app/editor/ui/session_coordinator.cc b/src/app/editor/ui/session_coordinator.cc index 2d80f18a..870a7a8d 100644 --- a/src/app/editor/ui/session_coordinator.cc +++ b/src/app/editor/ui/session_coordinator.cc @@ -445,6 +445,34 @@ void SessionCoordinator::RenameSession(size_t index, const std::string& new_name printf("[SessionCoordinator] Renamed session %zu to '%s'\n", index, new_name.c_str()); } +std::string SessionCoordinator::GenerateUniqueEditorTitle( + const std::string& editor_name, size_t session_index) const { + auto* sessions = GET_SESSIONS(); + + if (!sessions || sessions->size() <= 1) { + // Single session - use simple name + return editor_name; + } + + if (session_index >= sessions->size()) { + return editor_name; + } + + // Multi-session - include session identifier + const auto& session = sessions->at(session_index); + std::string session_name = session.custom_name.empty() + ? session.rom.title() + : session.custom_name; + + // Truncate long session names + if (session_name.length() > 20) { + session_name = session_name.substr(0, 17) + "..."; + } + + return absl::StrFormat("%s - %s##session_%zu", editor_name, session_name, + session_index); +} + void SessionCoordinator::SetActiveSessionIndex(size_t index) { SwitchToSession(index); } diff --git a/src/app/editor/ui/session_coordinator.h b/src/app/editor/ui/session_coordinator.h index 5cf3756f..2298407e 100644 --- a/src/app/editor/ui/session_coordinator.h +++ b/src/app/editor/ui/session_coordinator.h @@ -69,6 +69,7 @@ class SessionCoordinator { std::string GetSessionDisplayName(size_t index) const; std::string GetActiveSessionDisplayName() const; void RenameSession(size_t index, const std::string& new_name); + std::string GenerateUniqueEditorTitle(const std::string& editor_name, size_t session_index) const; // Session state management void SetActiveSessionIndex(size_t index);