refactor(editor): extract editor management responsibilities into dedicated classes

- Introduced EditorRegistry, ProjectManager, and RomFileManager to streamline editor operations and improve code organization.
- Refactored EditorManager to delegate responsibilities to the new classes, enhancing maintainability and clarity.
- Updated CMake configuration to include new source files for the extracted components.

Benefits:
- Improves separation of concerns within the editor, leading to a more modular and manageable codebase.
- Enhances the overall architecture by clearly defining roles for editor management, project handling, and ROM file operations.
This commit is contained in:
scawful
2025-10-14 22:01:07 -04:00
parent 6dbc30c11f
commit 0113d78978
13 changed files with 1092 additions and 96 deletions

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,250 @@
#include "editor_registry.h"
#include "absl/strings/str_format.h"
#include "app/editor/editor.h"
#include <unordered_set>
namespace yaze {
namespace editor {
// Static mappings for editor types
const std::unordered_map<EditorType, std::string> 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<EditorType, std::string> 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<EditorType, bool> 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<EditorType> EditorRegistry::GetEditorsInCategory(const std::string& category) const {
std::vector<EditorType> editors;
for (const auto& [type, cat] : kEditorCategories) {
if (cat == category) {
editors.push_back(type);
}
}
return editors;
}
std::vector<std::string> EditorRegistry::GetAvailableCategories() const {
std::vector<std::string> categories;
std::unordered_set<std::string> 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<int>(type)));
}
}
} // namespace editor
} // namespace yaze

View File

@@ -0,0 +1,75 @@
#ifndef YAZE_APP_EDITOR_SYSTEM_EDITOR_REGISTRY_H_
#define YAZE_APP_EDITOR_SYSTEM_EDITOR_REGISTRY_H_
#include <string>
#include <unordered_map>
#include <vector>
#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<EditorType> GetEditorsInCategory(const std::string& category) const;
std::vector<std::string> 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<EditorType, std::string> kEditorCategories;
static const std::unordered_map<EditorType, std::string> kEditorNames;
static const std::unordered_map<EditorType, bool> kCardBasedEditors;
// Registered editors
std::unordered_map<EditorType, Editor*> 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_

View File

@@ -0,0 +1,280 @@
#include "project_manager.h"
#include <filesystem>
#include <fstream>
#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<std::string> 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

View File

@@ -0,0 +1,70 @@
#ifndef YAZE_APP_EDITOR_SYSTEM_PROJECT_MANAGER_H_
#define YAZE_APP_EDITOR_SYSTEM_PROJECT_MANAGER_H_
#include <string>
#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<std::string> 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_

View File

@@ -0,0 +1,252 @@
#include "rom_file_manager.h"
#include <filesystem>
#include <fstream>
#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

View File

@@ -0,0 +1,64 @@
#ifndef YAZE_APP_EDITOR_SYSTEM_ROM_FILE_MANAGER_H_
#define YAZE_APP_EDITOR_SYSTEM_ROM_FILE_MANAGER_H_
#include <string>
#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_

View File

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

View File

@@ -53,6 +53,11 @@ class WindowDelegate {
absl::Status ResetLayout();
std::vector<std::string> GetAvailableLayouts() const;
// Workspace-specific layout methods (match EditorManager API)
void SaveWorkspaceLayout();
void LoadWorkspaceLayout();
void ResetWorkspaceLayout();
// Window state queries
std::vector<std::string> GetVisibleWindows() const;
std::vector<std::string> GetHiddenWindows() const;

View File

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

View File

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