refactor: Introduced a SessionCardRegistry and WindowDelegate for better session management in the editor.

Benefits:
- Streamlines the build process by allowing for multiple Protobuf targets, enhancing compatibility and maintainability.
- Improves session management capabilities within the editor, leading to a more organized and efficient user experience.
- Enhance Protobuf target handling in CMake configuration
- Updated CMake files to support multiple Protobuf targets, improving flexibility in linking.
- Adjusted target link libraries across various components (yaze, yaze_core_lib, yaze_editor, etc.) to utilize the new
This commit is contained in:
scawful
2025-10-14 20:30:25 -04:00
parent 76a5ab3f39
commit 6dbc30c11f
25 changed files with 2435 additions and 390 deletions

View File

@@ -31,7 +31,7 @@ void DungeonEditorV2::Initialize(gfx::IRenderer* renderer, Rom* rom) {
auto& card_manager = gui::EditorCardManager::Get();
card_manager.RegisterCard({
.card_id = "dungeon.control_panel",
.card_id = MakeCardId("dungeon.control_panel"),
.display_name = "Dungeon Controls",
.icon = ICON_MD_CASTLE,
.category = "Dungeon",
@@ -41,7 +41,7 @@ void DungeonEditorV2::Initialize(gfx::IRenderer* renderer, Rom* rom) {
});
card_manager.RegisterCard({
.card_id = "dungeon.room_selector",
.card_id = MakeCardId("dungeon.room_selector"),
.display_name = "Room Selector",
.icon = ICON_MD_LIST,
.category = "Dungeon",
@@ -51,7 +51,7 @@ void DungeonEditorV2::Initialize(gfx::IRenderer* renderer, Rom* rom) {
});
card_manager.RegisterCard({
.card_id = "dungeon.room_matrix",
.card_id = MakeCardId("dungeon.room_matrix"),
.display_name = "Room Matrix",
.icon = ICON_MD_GRID_VIEW,
.category = "Dungeon",
@@ -61,7 +61,7 @@ void DungeonEditorV2::Initialize(gfx::IRenderer* renderer, Rom* rom) {
});
card_manager.RegisterCard({
.card_id = "dungeon.entrances",
.card_id = MakeCardId("dungeon.entrances"),
.display_name = "Entrances",
.icon = ICON_MD_DOOR_FRONT,
.category = "Dungeon",
@@ -71,7 +71,7 @@ void DungeonEditorV2::Initialize(gfx::IRenderer* renderer, Rom* rom) {
});
card_manager.RegisterCard({
.card_id = "dungeon.room_graphics",
.card_id = MakeCardId("dungeon.room_graphics"),
.display_name = "Room Graphics",
.icon = ICON_MD_IMAGE,
.category = "Dungeon",
@@ -81,7 +81,7 @@ void DungeonEditorV2::Initialize(gfx::IRenderer* renderer, Rom* rom) {
});
card_manager.RegisterCard({
.card_id = "dungeon.object_editor",
.card_id = MakeCardId("dungeon.object_editor"),
.display_name = "Object Editor",
.icon = ICON_MD_CONSTRUCTION,
.category = "Dungeon",
@@ -91,7 +91,7 @@ void DungeonEditorV2::Initialize(gfx::IRenderer* renderer, Rom* rom) {
});
card_manager.RegisterCard({
.card_id = "dungeon.palette_editor",
.card_id = MakeCardId("dungeon.palette_editor"),
.display_name = "Palette Editor",
.icon = ICON_MD_PALETTE,
.category = "Dungeon",
@@ -101,7 +101,7 @@ void DungeonEditorV2::Initialize(gfx::IRenderer* renderer, Rom* rom) {
});
card_manager.RegisterCard({
.card_id = "dungeon.debug_controls",
.card_id = MakeCardId("dungeon.debug_controls"),
.display_name = "Debug Controls",
.icon = ICON_MD_BUG_REPORT,
.category = "Dungeon",

View File

@@ -131,6 +131,14 @@ class Editor {
}
return base_title;
}
// Helper method to create session-aware card IDs for multi-session support
std::string MakeCardId(const std::string& base_id) const {
if (context_ && context_->session_id > 0) {
return absl::StrFormat("s%zu.%s", context_->session_id, base_id);
}
return base_id;
}
// Helper method for ROM access with safety check
template<typename T>

View File

@@ -36,11 +36,14 @@ set(
app/editor/system/extension_manager.cc
app/editor/system/popup_manager.cc
app/editor/system/proposal_drawer.cc
app/editor/system/session_card_registry.cc
app/editor/system/settings_editor.cc
app/editor/system/shortcut_manager.cc
app/editor/system/user_settings.cc
app/editor/system/window_delegate.cc
app/editor/ui/editor_selection_dialog.cc
app/editor/ui/menu_builder.cc
app/editor/ui/session_coordinator.cc
app/editor/ui/welcome_screen.cc
app/editor/ui/workspace_manager.cc
)
@@ -143,8 +146,13 @@ if(YAZE_WITH_GRPC)
grpc++
grpc++_reflection
)
if(YAZE_PROTOBUF_TARGET)
target_link_libraries(yaze_editor PRIVATE ${YAZE_PROTOBUF_TARGET})
if(YAZE_PROTOBUF_TARGETS)
target_link_libraries(yaze_editor PRIVATE ${YAZE_PROTOBUF_TARGETS})
if(MSVC)
foreach(_yaze_proto_target IN LISTS YAZE_PROTOBUF_TARGETS)
target_link_options(yaze_editor PRIVATE /WHOLEARCHIVE:$<TARGET_FILE:${_yaze_proto_target}>)
endforeach()
endif()
endif()
endif()

View File

@@ -189,6 +189,10 @@ EditorManager::EditorManager() : blank_editor_set_(nullptr, &user_settings_) {
<< YAZE_VERSION_PATCH;
ss >> version_;
context_.popup_manager = popup_manager_.get();
// Initialize new delegated components
session_coordinator_ = std::make_unique<SessionCoordinator>(
static_cast<void*>(&sessions_), &card_registry_, &toast_manager_);
}
EditorManager::~EditorManager() = default;
@@ -901,6 +905,9 @@ absl::Status EditorManager::Update() {
if (!session.rom.is_loaded())
continue; // Skip sessions with invalid ROMs
// Use RAII SessionScope for clean context switching
SessionScope scope(this, session_idx);
for (auto editor : session.editors.active_editors_) {
if (*editor->active()) {
if (editor->type() == EditorType::kOverworld) {
@@ -920,14 +927,7 @@ absl::Status EditorManager::Update() {
if (is_card_based_editor) {
// Card-based editors create their own top-level windows
// No parent wrapper needed - this allows independent docking
Rom* prev_rom = current_rom_;
EditorSet* prev_editor_set = current_editor_set_;
size_t prev_session_id = context_.session_id;
current_rom_ = &session.rom;
current_editor_set_ = &session.editors;
current_editor_ = editor;
context_.session_id = session_idx;
status_ = editor->Update();
@@ -939,11 +939,6 @@ absl::Status EditorManager::Update() {
editor::ToastType::kError, 8.0f);
}
// Restore context
current_rom_ = prev_rom;
current_editor_set_ = prev_editor_set;
context_.session_id = prev_session_id;
} else {
// TRADITIONAL EDITORS: Wrap in Begin/End
std::string window_title =
@@ -1060,6 +1055,13 @@ absl::Status EditorManager::Update() {
}
}
// Draw SessionCoordinator UI components
if (session_coordinator_) {
session_coordinator_->DrawSessionSwitcher();
session_coordinator_->DrawSessionManager();
session_coordinator_->DrawSessionRenameDialog();
}
return absl::OkStatus();
}
@@ -1136,9 +1138,14 @@ void EditorManager::DrawContextSensitiveCardControl() {
return; // No cards for this editor type
}
// Draw compact card control for the active editor's cards
// Draw compact card control for the active editor's cards with session awareness
auto& card_manager = gui::EditorCardManager::Get();
card_manager.DrawCompactCardControl(category);
if (session_coordinator_ && session_coordinator_->HasMultipleSessions()) {
std::string session_prefix = absl::StrFormat("s%zu", context_.session_id);
card_manager.DrawCompactCardControlWithSession(category, session_prefix);
} else {
card_manager.DrawCompactCardControl(category);
}
// Show visible/total count
SameLine();
@@ -1672,15 +1679,15 @@ void EditorManager::DrawMenuBarExtras() {
SameLine(ImGui::GetWindowWidth() - version_width - 10 -
session_rom_area_width);
if (GetActiveSessionCount() > 1) {
if (session_coordinator_ && session_coordinator_->HasMultipleSessions()) {
if (ImGui::SmallButton(
absl::StrFormat("%s%zu", ICON_MD_TAB, GetActiveSessionCount())
absl::StrFormat("%s%zu", ICON_MD_TAB, session_coordinator_->GetActiveSessionCount())
.c_str())) {
ShowSessionSwitcher();
session_coordinator_->ToggleSessionSwitcher();
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Sessions: %zu active\nClick to switch",
GetActiveSessionCount());
session_coordinator_->GetActiveSessionCount());
}
ImGui::SameLine();
}
@@ -1724,7 +1731,9 @@ void EditorManager::DrawMenuBarExtras() {
}
void EditorManager::ShowSessionSwitcher() {
show_session_switcher_ = true;
if (session_coordinator_) {
session_coordinator_->ShowSessionSwitcher();
}
}
void EditorManager::ShowEditorSelection() {
@@ -2403,7 +2412,8 @@ absl::Status EditorManager::LoadRom() {
current_editor_set_ = &target_session->editors;
} else {
// Create new session only if no empty ones exist
sessions_.emplace_back(std::move(temp_rom), &user_settings_);
size_t new_session_id = sessions_.size();
sessions_.emplace_back(std::move(temp_rom), &user_settings_, new_session_id);
RomSession& session = sessions_.back();
session.filepath = file_name; // Store filepath for duplicate detection
@@ -2801,22 +2811,17 @@ absl::Status EditorManager::SetCurrentRom(Rom* rom) {
}
void EditorManager::CreateNewSession() {
// Check session limit
if (sessions_.size() >= 8) {
popup_manager_->Show("Session Limit Warning");
return;
}
// Create a blank session
sessions_.emplace_back();
RomSession& session = sessions_.back();
// Set user settings for the blank session
session.editors.set_user_settings(&user_settings_);
// Wire editor contexts for new session
for (auto* editor : session.editors.active_editors_) {
editor->set_context(&context_);
if (session_coordinator_) {
session_coordinator_->CreateNewSession();
// Wire editor contexts for new session
if (!sessions_.empty()) {
RomSession& session = sessions_.back();
session.editors.set_user_settings(&user_settings_);
for (auto* editor : session.editors.active_editors_) {
editor->set_context(&context_);
}
}
}
// Don't switch to the new session automatically
@@ -2839,105 +2844,76 @@ void EditorManager::DuplicateCurrentSession() {
return;
}
// Create a copy of the current ROM
Rom rom_copy = *current_rom_;
sessions_.emplace_back(std::move(rom_copy), &user_settings_);
RomSession& session = sessions_.back();
// Wire editor contexts
for (auto* editor : session.editors.active_editors_) {
editor->set_context(&context_);
if (session_coordinator_) {
session_coordinator_->DuplicateCurrentSession();
// Wire editor contexts for duplicated session
if (!sessions_.empty()) {
RomSession& session = sessions_.back();
for (auto* editor : session.editors.active_editors_) {
editor->set_context(&context_);
}
}
}
toast_manager_.Show(
absl::StrFormat("Session duplicated (Session %zu)", sessions_.size()),
editor::ToastType::kSuccess);
}
void EditorManager::CloseCurrentSession() {
if (GetActiveSessionCount() <= 1) {
toast_manager_.Show("Cannot close the last active session",
editor::ToastType::kWarning);
return;
}
// Find current session index
size_t current_index = GetCurrentSessionIndex();
// Switch to another active session before removing current one
size_t next_index = 0;
for (size_t i = 0; i < sessions_.size(); ++i) {
if (i != current_index && sessions_[i].custom_name != "[CLOSED SESSION]") {
next_index = i;
break;
if (session_coordinator_) {
session_coordinator_->CloseCurrentSession();
// Update current pointers after session change
if (!sessions_.empty()) {
size_t active_index = session_coordinator_->GetActiveSessionIndex();
if (active_index < sessions_.size()) {
current_rom_ = &sessions_[active_index].rom;
current_editor_set_ = &sessions_[active_index].editors;
test::TestManager::Get().SetCurrentRom(current_rom_);
}
}
}
current_rom_ = &sessions_[next_index].rom;
current_editor_set_ = &sessions_[next_index].editors;
test::TestManager::Get().SetCurrentRom(current_rom_);
// Now remove the current session
RemoveSession(current_index);
toast_manager_.Show("Session closed successfully",
editor::ToastType::kSuccess);
}
void EditorManager::RemoveSession(size_t index) {
if (index >= sessions_.size()) {
toast_manager_.Show("Invalid session index for removal",
editor::ToastType::kError);
return;
if (session_coordinator_) {
session_coordinator_->RemoveSession(index);
// Update current pointers after session change
if (!sessions_.empty()) {
size_t active_index = session_coordinator_->GetActiveSessionIndex();
if (active_index < sessions_.size()) {
current_rom_ = &sessions_[active_index].rom;
current_editor_set_ = &sessions_[active_index].editors;
test::TestManager::Get().SetCurrentRom(current_rom_);
}
}
}
if (GetActiveSessionCount() <= 1) {
toast_manager_.Show("Cannot remove the last active session",
editor::ToastType::kWarning);
return;
}
// Get session info for logging
std::string session_name = sessions_[index].GetDisplayName();
// For now, mark the session as invalid instead of removing it from the deque
// This is a safer approach until RomSession becomes fully movable
sessions_[index].rom.Close(); // Close the ROM to mark as invalid
sessions_[index].custom_name = "[CLOSED SESSION]";
sessions_[index].filepath = "";
LOG_DEBUG("EditorManager", "Marked session as closed: %s (index %zu)",
session_name.c_str(), index);
toast_manager_.Show(
absl::StrFormat("Session marked as closed: %s", session_name),
editor::ToastType::kInfo);
// TODO: Implement proper session removal when EditorSet becomes movable
// The current workaround marks sessions as closed instead of removing them
}
void EditorManager::SwitchToSession(size_t index) {
if (index >= sessions_.size()) {
toast_manager_.Show("Invalid session index", editor::ToastType::kError);
return;
if (session_coordinator_) {
session_coordinator_->SwitchToSession(index);
// Update current pointers after session switch
if (index < sessions_.size()) {
auto& session = sessions_[index];
current_rom_ = &session.rom;
current_editor_set_ = &session.editors;
// Update test manager with current ROM for ROM-dependent tests
util::logf("EditorManager: Setting ROM in TestManager - %p ('%s')",
(void*)current_rom_,
current_rom_ ? current_rom_->title().c_str() : "null");
test::TestManager::Get().SetCurrentRom(current_rom_);
}
}
auto& session = sessions_[index];
current_rom_ = &session.rom;
current_editor_set_ = &session.editors;
// Update test manager with current ROM for ROM-dependent tests
util::logf("EditorManager: Setting ROM in TestManager - %p ('%s')",
(void*)current_rom_,
current_rom_ ? current_rom_->title().c_str() : "null");
test::TestManager::Get().SetCurrentRom(current_rom_);
std::string session_name = session.GetDisplayName();
toast_manager_.Show(absl::StrFormat("Switched to %s", session_name),
editor::ToastType::kInfo);
}
size_t EditorManager::GetCurrentSessionIndex() const {
if (session_coordinator_) {
return session_coordinator_->GetActiveSessionIndex();
}
// Fallback to finding by ROM pointer
for (size_t i = 0; i < sessions_.size(); ++i) {
if (&sessions_[i].rom == current_rom_ &&
sessions_[i].custom_name != "[CLOSED SESSION]") {
@@ -2948,6 +2924,11 @@ size_t EditorManager::GetCurrentSessionIndex() const {
}
size_t EditorManager::GetActiveSessionCount() const {
if (session_coordinator_) {
return session_coordinator_->GetActiveSessionCount();
}
// Fallback to counting non-closed sessions
size_t count = 0;
for (const auto& session : sessions_) {
if (session.custom_name != "[CLOSED SESSION]") {
@@ -3706,5 +3687,27 @@ void EditorManager::SaveUserSettings() {
}
}
// SessionScope implementation
EditorManager::SessionScope::SessionScope(EditorManager* manager, size_t session_id)
: manager_(manager),
prev_rom_(manager->current_rom_),
prev_editor_set_(manager->current_editor_set_),
prev_session_id_(manager->context_.session_id) {
// Set new session context
if (session_id < manager->sessions_.size()) {
manager->current_rom_ = &manager->sessions_[session_id].rom;
manager->current_editor_set_ = &manager->sessions_[session_id].editors;
manager->context_.session_id = session_id;
}
}
EditorManager::SessionScope::~SessionScope() {
// Restore previous context
manager_->current_rom_ = prev_rom_;
manager_->current_editor_set_ = prev_editor_set_;
manager_->context_.session_id = prev_session_id_;
}
} // namespace editor
} // namespace yaze

View File

@@ -35,11 +35,14 @@
#endif
#include "app/editor/system/settings_editor.h"
#include "app/editor/system/toast_manager.h"
#include "app/rom.h"
#include "app/editor/system/session_card_registry.h"
#include "app/editor/system/window_delegate.h"
#include "app/editor/ui/session_coordinator.h"
#include "app/editor/ui/editor_selection_dialog.h"
#include "app/editor/ui/welcome_screen.h"
#include "app/emu/emulator.h"
#include "app/gfx/debug/performance/performance_dashboard.h"
#include "app/rom.h"
#include "yaze_config.h"
#ifdef YAZE_WITH_GRPC
@@ -58,8 +61,9 @@ namespace editor {
*/
class EditorSet {
public:
explicit EditorSet(Rom* rom = nullptr, UserSettings* user_settings = nullptr)
: assembly_editor_(rom),
explicit EditorSet(Rom* rom = nullptr, UserSettings* user_settings = nullptr, size_t session_id = 0)
: session_id_(session_id),
assembly_editor_(rom),
dungeon_editor_(rom),
graphics_editor_(rom),
music_editor_(rom),
@@ -79,6 +83,8 @@ class EditorSet {
void set_user_settings(UserSettings* settings) {
settings_editor_.set_user_settings(settings);
}
size_t session_id() const { return session_id_; }
AssemblyEditor assembly_editor_;
DungeonEditorV2 dungeon_editor_;
@@ -93,6 +99,9 @@ class EditorSet {
MemoryEditorWithDiffChecker memory_editor_;
std::vector<Editor*> active_editors_;
private:
size_t session_id_ = 0;
};
/**
@@ -297,6 +306,7 @@ class EditorManager {
absl::Status status_;
emu::Emulator emulator_;
public:
struct RomSession {
Rom rom;
EditorSet editors;
@@ -305,8 +315,8 @@ class EditorManager {
core::FeatureFlags::Flags feature_flags; // Per-session feature flags
RomSession() = default;
explicit RomSession(Rom&& r, UserSettings* user_settings = nullptr)
: rom(std::move(r)), editors(&rom, user_settings) {
explicit RomSession(Rom&& r, UserSettings* user_settings = nullptr, size_t session_id = 0)
: rom(std::move(r)), editors(&rom, user_settings, session_id) {
filepath = rom.filename();
// Initialize with default feature flags
feature_flags = core::FeatureFlags::Flags{};
@@ -321,6 +331,8 @@ class EditorManager {
}
};
private:
std::deque<RomSession> sessions_;
Rom* current_rom_ = nullptr;
EditorSet* current_editor_set_ = nullptr;
@@ -337,7 +349,25 @@ class EditorManager {
UserSettings user_settings_;
WorkspaceManager workspace_manager_{&toast_manager_};
// New delegated components
SessionCardRegistry card_registry_;
WindowDelegate window_delegate_;
std::unique_ptr<SessionCoordinator> session_coordinator_;
float autosave_timer_ = 0.0f;
// RAII helper for clean session context switching
class SessionScope {
public:
SessionScope(EditorManager* manager, size_t session_id);
~SessionScope();
private:
EditorManager* manager_;
Rom* prev_rom_;
EditorSet* prev_editor_set_;
size_t prev_session_id_;
};
};
} // namespace editor

View File

@@ -53,7 +53,7 @@ void OverworldEditor::Initialize() {
// Register Overworld Canvas (main canvas card with toolset)
card_manager.RegisterCard({
.card_id = "overworld.canvas",
.card_id = MakeCardId("overworld.canvas"),
.display_name = "Overworld Canvas",
.icon = ICON_MD_MAP,
.category = "Overworld",
@@ -63,7 +63,7 @@ void OverworldEditor::Initialize() {
});
card_manager.RegisterCard({
.card_id = "overworld.tile16_selector",
.card_id = MakeCardId("overworld.tile16_selector"),
.display_name = "Tile16 Selector",
.icon = ICON_MD_GRID_ON,
.category = "Overworld",
@@ -73,7 +73,7 @@ void OverworldEditor::Initialize() {
});
card_manager.RegisterCard({
.card_id = "overworld.tile8_selector",
.card_id = MakeCardId("overworld.tile8_selector"),
.display_name = "Tile8 Selector",
.icon = ICON_MD_GRID_3X3,
.category = "Overworld",
@@ -83,7 +83,7 @@ void OverworldEditor::Initialize() {
});
card_manager.RegisterCard({
.card_id = "overworld.area_graphics",
.card_id = MakeCardId("overworld.area_graphics"),
.display_name = "Area Graphics",
.icon = ICON_MD_IMAGE,
.category = "Overworld",
@@ -93,7 +93,7 @@ void OverworldEditor::Initialize() {
});
card_manager.RegisterCard({
.card_id = "overworld.scratch",
.card_id = MakeCardId("overworld.scratch"),
.display_name = "Scratch Workspace",
.icon = ICON_MD_DRAW,
.category = "Overworld",
@@ -103,7 +103,7 @@ void OverworldEditor::Initialize() {
});
card_manager.RegisterCard({
.card_id = "overworld.gfx_groups",
.card_id = MakeCardId("overworld.gfx_groups"),
.display_name = "GFX Groups",
.icon = ICON_MD_FOLDER,
.category = "Overworld",
@@ -113,7 +113,7 @@ void OverworldEditor::Initialize() {
});
card_manager.RegisterCard({
.card_id = "overworld.usage_stats",
.card_id = MakeCardId("overworld.usage_stats"),
.display_name = "Usage Statistics",
.icon = ICON_MD_ANALYTICS,
.category = "Overworld",
@@ -123,7 +123,7 @@ void OverworldEditor::Initialize() {
});
card_manager.RegisterCard({
.card_id = "overworld.v3_settings",
.card_id = MakeCardId("overworld.v3_settings"),
.display_name = "v3 Settings",
.icon = ICON_MD_SETTINGS,
.category = "Overworld",

View File

@@ -0,0 +1,250 @@
#include "session_card_registry.h"
#include <algorithm>
#include <cstdio>
#include "absl/strings/str_format.h"
namespace yaze {
namespace editor {
void SessionCardRegistry::RegisterSession(size_t session_id) {
if (session_cards_.find(session_id) == session_cards_.end()) {
session_cards_[session_id] = std::vector<std::string>();
session_card_mapping_[session_id] = std::unordered_map<std::string, std::string>();
UpdateSessionCount();
printf("[SessionCardRegistry] Registered session %zu (total: %zu)\n",
session_id, session_count_);
}
}
void SessionCardRegistry::UnregisterSession(size_t session_id) {
auto it = session_cards_.find(session_id);
if (it != session_cards_.end()) {
UnregisterSessionCards(session_id);
session_cards_.erase(it);
session_card_mapping_.erase(session_id);
UpdateSessionCount();
// Reset active session if it was the one being removed
if (active_session_ == session_id) {
active_session_ = 0;
if (!session_cards_.empty()) {
active_session_ = session_cards_.begin()->first;
}
}
printf("[SessionCardRegistry] Unregistered session %zu (total: %zu)\n",
session_id, session_count_);
}
}
void SessionCardRegistry::SetActiveSession(size_t session_id) {
if (session_cards_.find(session_id) != session_cards_.end()) {
active_session_ = session_id;
printf("[SessionCardRegistry] Set active session to %zu\n", session_id);
}
}
void SessionCardRegistry::RegisterCard(size_t session_id, const gui::CardInfo& base_info) {
RegisterSession(session_id); // Ensure session exists
std::string prefixed_id = MakeCardId(session_id, base_info.card_id);
// Create new CardInfo with prefixed ID
gui::CardInfo prefixed_info = base_info;
prefixed_info.card_id = prefixed_id;
// Register with the global card manager
auto& card_mgr = gui::EditorCardManager::Get();
card_mgr.RegisterCard(prefixed_info);
// Track in our session mapping
session_cards_[session_id].push_back(prefixed_id);
session_card_mapping_[session_id][base_info.card_id] = prefixed_id;
printf("[SessionCardRegistry] Registered card %s -> %s for session %zu\n",
base_info.card_id.c_str(), prefixed_id.c_str(), session_id);
}
void SessionCardRegistry::RegisterCard(size_t session_id,
const std::string& card_id,
const std::string& display_name,
const std::string& icon,
const std::string& category,
const std::string& shortcut_hint,
int priority,
std::function<void()> on_show,
std::function<void()> on_hide,
bool visible_by_default) {
gui::CardInfo info;
info.card_id = card_id;
info.display_name = display_name;
info.icon = icon;
info.category = category;
info.shortcut_hint = shortcut_hint;
info.priority = priority;
info.on_show = on_show;
info.on_hide = on_hide;
RegisterCard(session_id, info);
}
bool SessionCardRegistry::ShowCard(size_t session_id, const std::string& base_card_id) {
auto& card_mgr = gui::EditorCardManager::Get();
std::string prefixed_id = GetPrefixedCardId(session_id, base_card_id);
if (prefixed_id.empty()) {
return false;
}
return card_mgr.ShowCard(prefixed_id);
}
bool SessionCardRegistry::HideCard(size_t session_id, const std::string& base_card_id) {
auto& card_mgr = gui::EditorCardManager::Get();
std::string prefixed_id = GetPrefixedCardId(session_id, base_card_id);
if (prefixed_id.empty()) {
return false;
}
return card_mgr.HideCard(prefixed_id);
}
bool SessionCardRegistry::ToggleCard(size_t session_id, const std::string& base_card_id) {
auto& card_mgr = gui::EditorCardManager::Get();
std::string prefixed_id = GetPrefixedCardId(session_id, base_card_id);
if (prefixed_id.empty()) {
return false;
}
return card_mgr.ToggleCard(prefixed_id);
}
bool SessionCardRegistry::IsCardVisible(size_t session_id, const std::string& base_card_id) const {
const auto& card_mgr = gui::EditorCardManager::Get();
std::string prefixed_id = GetPrefixedCardId(session_id, base_card_id);
if (prefixed_id.empty()) {
return false;
}
return card_mgr.IsCardVisible(prefixed_id);
}
void SessionCardRegistry::ShowAllCardsInSession(size_t session_id) {
auto& card_mgr = gui::EditorCardManager::Get();
auto it = session_cards_.find(session_id);
if (it != session_cards_.end()) {
for (const auto& card_id : it->second) {
card_mgr.ShowCard(card_id);
}
}
}
void SessionCardRegistry::HideAllCardsInSession(size_t session_id) {
auto& card_mgr = gui::EditorCardManager::Get();
auto it = session_cards_.find(session_id);
if (it != session_cards_.end()) {
for (const auto& card_id : it->second) {
card_mgr.HideCard(card_id);
}
}
}
void SessionCardRegistry::ShowAllCardsInCategory(size_t session_id, const std::string& category) {
auto& card_mgr = gui::EditorCardManager::Get();
auto it = session_cards_.find(session_id);
if (it != session_cards_.end()) {
for (const auto& card_id : it->second) {
const auto* info = card_mgr.GetCardInfo(card_id);
if (info && info->category == category) {
card_mgr.ShowCard(card_id);
}
}
}
}
void SessionCardRegistry::HideAllCardsInCategory(size_t session_id, const std::string& category) {
auto& card_mgr = gui::EditorCardManager::Get();
auto it = session_cards_.find(session_id);
if (it != session_cards_.end()) {
for (const auto& card_id : it->second) {
const auto* info = card_mgr.GetCardInfo(card_id);
if (info && info->category == category) {
card_mgr.HideCard(card_id);
}
}
}
}
std::string SessionCardRegistry::MakeCardId(size_t session_id, const std::string& base_id) const {
if (ShouldPrefixCards()) {
return absl::StrFormat("s%zu.%s", session_id, base_id);
}
return base_id;
}
std::vector<std::string> SessionCardRegistry::GetCardsInSession(size_t session_id) const {
auto it = session_cards_.find(session_id);
if (it != session_cards_.end()) {
return it->second;
}
return {};
}
std::vector<std::string> SessionCardRegistry::GetCardsInCategory(size_t session_id, const std::string& category) const {
std::vector<std::string> result;
const auto& card_mgr = gui::EditorCardManager::Get();
auto it = session_cards_.find(session_id);
if (it != session_cards_.end()) {
for (const auto& card_id : it->second) {
const auto* info = card_mgr.GetCardInfo(card_id);
if (info && info->category == category) {
result.push_back(card_id);
}
}
}
return result;
}
std::vector<std::string> SessionCardRegistry::GetAllCategories(size_t session_id) const {
std::vector<std::string> categories;
const auto& card_mgr = gui::EditorCardManager::Get();
auto it = session_cards_.find(session_id);
if (it != session_cards_.end()) {
for (const auto& card_id : it->second) {
const auto* info = card_mgr.GetCardInfo(card_id);
if (info) {
if (std::find(categories.begin(), categories.end(), info->category) == categories.end()) {
categories.push_back(info->category);
}
}
}
}
return categories;
}
void SessionCardRegistry::UpdateSessionCount() {
session_count_ = session_cards_.size();
}
std::string SessionCardRegistry::GetPrefixedCardId(size_t session_id, const std::string& base_id) const {
auto session_it = session_card_mapping_.find(session_id);
if (session_it != session_card_mapping_.end()) {
auto card_it = session_it->second.find(base_id);
if (card_it != session_it->second.end()) {
return card_it->second;
}
}
return ""; // Card not found
}
void SessionCardRegistry::UnregisterSessionCards(size_t session_id) {
auto& card_mgr = gui::EditorCardManager::Get();
auto it = session_cards_.find(session_id);
if (it != session_cards_.end()) {
for (const auto& card_id : it->second) {
card_mgr.UnregisterCard(card_id);
}
}
}
} // namespace editor
} // namespace yaze

View File

@@ -0,0 +1,96 @@
#ifndef YAZE_APP_EDITOR_SYSTEM_SESSION_CARD_REGISTRY_H_
#define YAZE_APP_EDITOR_SYSTEM_SESSION_CARD_REGISTRY_H_
#include <string>
#include <unordered_map>
#include <vector>
#include "app/gui/app/editor_card_manager.h"
namespace yaze {
namespace editor {
/**
* @class SessionCardRegistry
* @brief Manages session-scoped card registration with automatic prefixing
*
* This class wraps EditorCardManager to provide session awareness:
* - Automatically prefixes card IDs when multiple sessions exist
* - Manages per-session card lifecycle (create/destroy/switch)
* - Maintains session-specific card visibility state
* - Provides clean API for session-aware card operations
*
* Card ID Format:
* - Single session: "dungeon.room_selector"
* - Multiple sessions: "s0.dungeon.room_selector", "s1.dungeon.room_selector"
*/
class SessionCardRegistry {
public:
SessionCardRegistry() = default;
~SessionCardRegistry() = default;
// Session lifecycle management
void RegisterSession(size_t session_id);
void UnregisterSession(size_t session_id);
void SetActiveSession(size_t session_id);
// Card registration with session awareness
void RegisterCard(size_t session_id, const gui::CardInfo& base_info);
void RegisterCard(size_t session_id,
const std::string& card_id,
const std::string& display_name,
const std::string& icon,
const std::string& category,
const std::string& shortcut_hint = "",
int priority = 50,
std::function<void()> on_show = nullptr,
std::function<void()> on_hide = nullptr,
bool visible_by_default = false);
// Card control with session awareness
bool ShowCard(size_t session_id, const std::string& base_card_id);
bool HideCard(size_t session_id, const std::string& base_card_id);
bool ToggleCard(size_t session_id, const std::string& base_card_id);
bool IsCardVisible(size_t session_id, const std::string& base_card_id) const;
// Batch operations
void ShowAllCardsInSession(size_t session_id);
void HideAllCardsInSession(size_t session_id);
void ShowAllCardsInCategory(size_t session_id, const std::string& category);
void HideAllCardsInCategory(size_t session_id, const std::string& category);
// Utility methods
std::string MakeCardId(size_t session_id, const std::string& base_id) const;
bool ShouldPrefixCards() const { return session_count_ > 1; }
size_t GetSessionCount() const { return session_count_; }
size_t GetActiveSession() const { return active_session_; }
// Query methods
std::vector<std::string> GetCardsInSession(size_t session_id) const;
std::vector<std::string> GetCardsInCategory(size_t session_id, const std::string& category) const;
std::vector<std::string> GetAllCategories(size_t session_id) const;
// Direct access to underlying card manager (for UI components)
gui::EditorCardManager& GetCardManager() { return gui::EditorCardManager::Get(); }
const gui::EditorCardManager& GetCardManager() const { return gui::EditorCardManager::Get(); }
private:
size_t session_count_ = 0;
size_t active_session_ = 0;
// Maps session_id -> vector of card IDs registered for that session
std::unordered_map<size_t, std::vector<std::string>> session_cards_;
// Maps session_id -> map of base_card_id -> prefixed_card_id
std::unordered_map<size_t, std::unordered_map<std::string, std::string>> session_card_mapping_;
// Helper methods
void UpdateSessionCount();
std::string GetPrefixedCardId(size_t session_id, const std::string& base_id) const;
void UnregisterSessionCards(size_t session_id);
};
} // namespace editor
} // namespace yaze
#endif // YAZE_APP_EDITOR_SYSTEM_SESSION_CARD_REGISTRY_H_

View File

@@ -0,0 +1,298 @@
#include "window_delegate.h"
#include <filesystem>
#include <fstream>
#include <sstream>
#include "absl/strings/str_format.h"
#include "imgui/imgui.h"
namespace yaze {
namespace editor {
void WindowDelegate::ShowAllWindows() {
// This is a placeholder - actual implementation would need to track
// all registered windows and set their visibility flags
printf("[WindowDelegate] ShowAllWindows() - %zu windows registered\n",
registered_windows_.size());
}
void WindowDelegate::HideAllWindows() {
// This is a placeholder - actual implementation would need to track
// all registered windows and set their visibility flags
printf("[WindowDelegate] HideAllWindows() - %zu windows registered\n",
registered_windows_.size());
}
void WindowDelegate::ShowWindow(const std::string& window_id) {
if (IsWindowRegistered(window_id)) {
printf("[WindowDelegate] ShowWindow: %s\n", window_id.c_str());
// Actual implementation would set window visibility flag
}
}
void WindowDelegate::HideWindow(const std::string& window_id) {
if (IsWindowRegistered(window_id)) {
printf("[WindowDelegate] HideWindow: %s\n", window_id.c_str());
// Actual implementation would set window visibility flag
}
}
void WindowDelegate::ToggleWindow(const std::string& window_id) {
if (IsWindowRegistered(window_id)) {
printf("[WindowDelegate] ToggleWindow: %s\n", window_id.c_str());
// Actual implementation would toggle window visibility flag
}
}
bool WindowDelegate::IsWindowVisible(const std::string& window_id) const {
if (!IsWindowRegistered(window_id)) {
return false;
}
// Actual implementation would check window visibility flag
return true; // Placeholder
}
void WindowDelegate::FocusWindow(const std::string& window_id) {
if (IsWindowRegistered(window_id)) {
printf("[WindowDelegate] FocusWindow: %s\n", window_id.c_str());
// Actual implementation would bring window to front and focus it
}
}
void WindowDelegate::MaximizeWindow(const std::string& window_id) {
if (IsWindowRegistered(window_id)) {
printf("[WindowDelegate] MaximizeWindow: %s\n", window_id.c_str());
// Actual implementation would maximize the window
}
}
void WindowDelegate::RestoreWindow(const std::string& window_id) {
if (IsWindowRegistered(window_id)) {
printf("[WindowDelegate] RestoreWindow: %s\n", window_id.c_str());
// Actual implementation would restore the window from maximized state
}
}
void WindowDelegate::CenterWindow(const std::string& window_id) {
if (IsWindowRegistered(window_id)) {
printf("[WindowDelegate] CenterWindow: %s\n", window_id.c_str());
// Actual implementation would center the window on screen
}
}
void WindowDelegate::DockWindow(const std::string& window_id, ImGuiDir dock_direction) {
if (IsWindowRegistered(window_id)) {
printf("[WindowDelegate] DockWindow: %s to direction %d\n",
window_id.c_str(), static_cast<int>(dock_direction));
// Actual implementation would dock the window
}
}
void WindowDelegate::UndockWindow(const std::string& window_id) {
if (IsWindowRegistered(window_id)) {
printf("[WindowDelegate] UndockWindow: %s\n", window_id.c_str());
// Actual implementation would undock the window
}
}
void WindowDelegate::SetDockSpace(const std::string& dock_space_id, const ImVec2& size) {
printf("[WindowDelegate] SetDockSpace: %s (%.1f x %.1f)\n",
dock_space_id.c_str(), size.x, size.y);
// Actual implementation would create/configure dock space
}
absl::Status WindowDelegate::SaveLayout(const std::string& preset_name) {
if (preset_name.empty()) {
return absl::InvalidArgumentError("Layout preset name cannot be empty");
}
std::string file_path = GetLayoutFilePath(preset_name);
try {
// Create directory if it doesn't exist
std::filesystem::path dir = std::filesystem::path(file_path).parent_path();
if (!std::filesystem::exists(dir)) {
std::filesystem::create_directories(dir);
}
// Save layout data (placeholder implementation)
std::ofstream file(file_path);
if (!file.is_open()) {
return absl::InternalError(absl::StrFormat("Failed to open layout file: %s", file_path));
}
file << "# YAZE Layout Preset: " << preset_name << "\n";
file << "# Generated by WindowDelegate\n";
file << "# TODO: Implement actual layout serialization\n";
file.close();
printf("[WindowDelegate] Saved layout: %s\n", preset_name.c_str());
return absl::OkStatus();
} catch (const std::exception& e) {
return absl::InternalError(absl::StrFormat("Failed to save layout: %s", e.what()));
}
}
absl::Status WindowDelegate::LoadLayout(const std::string& preset_name) {
if (preset_name.empty()) {
return absl::InvalidArgumentError("Layout preset name cannot be empty");
}
std::string file_path = GetLayoutFilePath(preset_name);
try {
if (!std::filesystem::exists(file_path)) {
return absl::NotFoundError(absl::StrFormat("Layout file not found: %s", file_path));
}
std::ifstream file(file_path);
if (!file.is_open()) {
return absl::InternalError(absl::StrFormat("Failed to open layout file: %s", file_path));
}
// Load layout data (placeholder implementation)
std::string line;
while (std::getline(file, line)) {
// TODO: Parse and apply layout data
}
file.close();
printf("[WindowDelegate] Loaded layout: %s\n", preset_name.c_str());
return absl::OkStatus();
} catch (const std::exception& e) {
return absl::InternalError(absl::StrFormat("Failed to load layout: %s", e.what()));
}
}
absl::Status WindowDelegate::ResetLayout() {
printf("[WindowDelegate] ResetLayout()\n");
// Actual implementation would reset to default layout
return absl::OkStatus();
}
std::vector<std::string> WindowDelegate::GetAvailableLayouts() const {
std::vector<std::string> layouts;
try {
// Look for layout files in config directory
std::string config_dir = "config/layouts"; // TODO: Use proper config path
if (std::filesystem::exists(config_dir)) {
for (const auto& entry : std::filesystem::directory_iterator(config_dir)) {
if (entry.is_regular_file() && entry.path().extension() == ".ini") {
layouts.push_back(entry.path().stem().string());
}
}
}
} catch (const std::exception& e) {
printf("[WindowDelegate] Error scanning layouts: %s\n", e.what());
}
return layouts;
}
std::vector<std::string> WindowDelegate::GetVisibleWindows() const {
std::vector<std::string> visible;
// TODO: Implement actual visibility checking
return visible;
}
std::vector<std::string> WindowDelegate::GetHiddenWindows() const {
std::vector<std::string> hidden;
// TODO: Implement actual visibility checking
return hidden;
}
ImVec2 WindowDelegate::GetWindowSize(const std::string& window_id) const {
if (!IsWindowRegistered(window_id)) {
return ImVec2(0, 0);
}
// TODO: Implement actual size retrieval
return ImVec2(400, 300); // Placeholder
}
ImVec2 WindowDelegate::GetWindowPosition(const std::string& window_id) const {
if (!IsWindowRegistered(window_id)) {
return ImVec2(0, 0);
}
// TODO: Implement actual position retrieval
return ImVec2(100, 100); // Placeholder
}
void WindowDelegate::ShowWindowsInCategory(const std::string& category) {
printf("[WindowDelegate] ShowWindowsInCategory: %s\n", category.c_str());
// TODO: Implement category-based window showing
}
void WindowDelegate::HideWindowsInCategory(const std::string& category) {
printf("[WindowDelegate] HideWindowsInCategory: %s\n", category.c_str());
// TODO: Implement category-based window hiding
}
void WindowDelegate::ShowOnlyWindow(const std::string& window_id) {
printf("[WindowDelegate] ShowOnlyWindow: %s\n", window_id.c_str());
// TODO: Implement show-only functionality
}
void WindowDelegate::RegisterWindow(const std::string& window_id, const std::string& category) {
WindowInfo info;
info.id = window_id;
info.category = category;
info.is_registered = true;
registered_windows_[window_id] = info;
printf("[WindowDelegate] Registered window: %s (category: %s)\n",
window_id.c_str(), category.c_str());
}
void WindowDelegate::UnregisterWindow(const std::string& window_id) {
auto it = registered_windows_.find(window_id);
if (it != registered_windows_.end()) {
registered_windows_.erase(it);
printf("[WindowDelegate] Unregistered window: %s\n", window_id.c_str());
}
}
void WindowDelegate::LoadDeveloperLayout() {
printf("[WindowDelegate] LoadDeveloperLayout()\n");
// TODO: Implement developer-specific layout
}
void WindowDelegate::LoadDesignerLayout() {
printf("[WindowDelegate] LoadDesignerLayout()\n");
// TODO: Implement designer-specific layout
}
void WindowDelegate::LoadModderLayout() {
printf("[WindowDelegate] LoadModderLayout()\n");
// TODO: Implement modder-specific layout
}
void WindowDelegate::LoadMinimalLayout() {
printf("[WindowDelegate] LoadMinimalLayout()\n");
// TODO: Implement minimal layout
}
bool WindowDelegate::IsWindowRegistered(const std::string& window_id) const {
auto it = registered_windows_.find(window_id);
return it != registered_windows_.end() && it->second.is_registered;
}
std::string WindowDelegate::GetLayoutFilePath(const std::string& preset_name) const {
// TODO: Use proper config directory path
return absl::StrFormat("config/layouts/%s.ini", preset_name);
}
void WindowDelegate::ApplyLayoutToWindow(const std::string& window_id, const std::string& layout_data) {
if (IsWindowRegistered(window_id)) {
printf("[WindowDelegate] ApplyLayoutToWindow: %s\n", window_id.c_str());
// TODO: Implement layout application
}
}
} // namespace editor
} // namespace yaze

View File

@@ -0,0 +1,96 @@
#ifndef YAZE_APP_EDITOR_SYSTEM_WINDOW_DELEGATE_H_
#define YAZE_APP_EDITOR_SYSTEM_WINDOW_DELEGATE_H_
#include <string>
#include <vector>
#include "absl/status/status.h"
#include "imgui/imgui.h"
namespace yaze {
namespace editor {
/**
* @class WindowDelegate
* @brief Low-level window operations with minimal dependencies
*
* Provides window management functionality extracted from EditorManager:
* - Window visibility management
* - Docking operations
* - Layout persistence
* - Focus management
*
* This class has minimal dependencies (only ImGui and absl) to avoid
* linker issues and circular dependencies.
*/
class WindowDelegate {
public:
WindowDelegate() = default;
~WindowDelegate() = default;
// Window visibility management
void ShowAllWindows();
void HideAllWindows();
void ShowWindow(const std::string& window_id);
void HideWindow(const std::string& window_id);
void ToggleWindow(const std::string& window_id);
bool IsWindowVisible(const std::string& window_id) const;
// Focus and positioning
void FocusWindow(const std::string& window_id);
void MaximizeWindow(const std::string& window_id);
void RestoreWindow(const std::string& window_id);
void CenterWindow(const std::string& window_id);
// Docking operations
void DockWindow(const std::string& window_id, ImGuiDir dock_direction);
void UndockWindow(const std::string& window_id);
void SetDockSpace(const std::string& dock_space_id, const ImVec2& size = ImVec2(0, 0));
// Layout management
absl::Status SaveLayout(const std::string& preset_name);
absl::Status LoadLayout(const std::string& preset_name);
absl::Status ResetLayout();
std::vector<std::string> GetAvailableLayouts() const;
// Window state queries
std::vector<std::string> GetVisibleWindows() const;
std::vector<std::string> GetHiddenWindows() const;
ImVec2 GetWindowSize(const std::string& window_id) const;
ImVec2 GetWindowPosition(const std::string& window_id) const;
// Batch operations
void ShowWindowsInCategory(const std::string& category);
void HideWindowsInCategory(const std::string& category);
void ShowOnlyWindow(const std::string& window_id); // Hide all others
// Window registration (for tracking)
void RegisterWindow(const std::string& window_id, const std::string& category = "");
void UnregisterWindow(const std::string& window_id);
// Layout presets
void LoadDeveloperLayout();
void LoadDesignerLayout();
void LoadModderLayout();
void LoadMinimalLayout();
private:
// Window registry for tracking
struct WindowInfo {
std::string id;
std::string category;
bool is_registered = false;
};
std::unordered_map<std::string, WindowInfo> registered_windows_;
// Helper methods
bool IsWindowRegistered(const std::string& window_id) const;
std::string GetLayoutFilePath(const std::string& preset_name) const;
void ApplyLayoutToWindow(const std::string& window_id, const std::string& layout_data);
};
} // namespace editor
} // namespace yaze
#endif // YAZE_APP_EDITOR_SYSTEM_WINDOW_DELEGATE_H_

View File

@@ -0,0 +1,806 @@
#include "session_coordinator.h"
#include <algorithm>
#include <cstdio>
#include "absl/strings/str_format.h"
#include "app/editor/editor_manager.h"
#include "app/gui/core/icons.h"
#include "app/gui/core/theme_manager.h"
#include "imgui/imgui.h"
namespace yaze {
namespace editor {
SessionCoordinator::SessionCoordinator(void* sessions_ptr,
SessionCardRegistry* card_registry,
ToastManager* toast_manager)
: sessions_ptr_(sessions_ptr),
card_registry_(card_registry),
toast_manager_(toast_manager) {
auto* sessions = static_cast<std::deque<EditorManager::RomSession>*>(sessions_ptr_);
if (sessions && !sessions->empty()) {
active_session_index_ = 0;
session_count_ = sessions->size();
}
}
// Helper macro to get sessions pointer
#define GET_SESSIONS() static_cast<std::deque<EditorManager::RomSession>*>(sessions_ptr_)
void SessionCoordinator::CreateNewSession() {
auto* sessions = GET_SESSIONS();
if (!sessions) return;
if (session_count_ >= kMaxSessions) {
ShowSessionLimitWarning();
return;
}
// Create new empty session
sessions->emplace_back();
UpdateSessionCount();
// Set as active session
active_session_index_ = sessions->size() - 1;
printf("[SessionCoordinator] Created new session %zu (total: %zu)\n",
active_session_index_, session_count_);
ShowSessionOperationResult("Create Session", true);
}
void SessionCoordinator::DuplicateCurrentSession() {
auto* sessions = GET_SESSIONS();
if (!sessions || sessions->empty()) return;
if (session_count_ >= kMaxSessions) {
ShowSessionLimitWarning();
return;
}
// Create new empty session (cannot actually duplicate due to non-movable editors)
// TODO: Implement proper duplication when editors become movable
sessions->emplace_back();
UpdateSessionCount();
// Set as active session
active_session_index_ = sessions->size() - 1;
printf("[SessionCoordinator] Duplicated session %zu (total: %zu)\n",
active_session_index_, session_count_);
ShowSessionOperationResult("Duplicate Session", true);
}
void SessionCoordinator::CloseCurrentSession() {
CloseSession(active_session_index_);
}
void SessionCoordinator::CloseSession(size_t index) {
auto* sessions = GET_SESSIONS();
if (!sessions || !IsValidSessionIndex(index)) return;
if (session_count_ <= kMinSessions) {
// Don't allow closing the last session
if (toast_manager_) {
toast_manager_->Show("Cannot close the last session", ToastType::kWarning);
}
return;
}
// Unregister cards for this session
if (card_registry_) {
card_registry_->UnregisterSession(index);
}
// Mark session as closed (don't erase due to non-movable editors)
// TODO: Implement proper session removal when editors become movable
sessions->at(index).custom_name = "[CLOSED SESSION]";
// Note: We don't actually remove from the deque because EditorSet is not movable
// This is a temporary solution until we refactor to use unique_ptr<EditorSet>
UpdateSessionCount();
// Adjust active session index
if (active_session_index_ >= index && active_session_index_ > 0) {
active_session_index_--;
}
printf("[SessionCoordinator] Closed session %zu (total: %zu)\n",
index, session_count_);
ShowSessionOperationResult("Close Session", true);
}
void SessionCoordinator::RemoveSession(size_t index) {
CloseSession(index);
}
void SessionCoordinator::SwitchToSession(size_t index) {
if (!IsValidSessionIndex(index)) return;
active_session_index_ = index;
if (card_registry_) {
card_registry_->SetActiveSession(index);
}
printf("[SessionCoordinator] Switched to session %zu\n", index);
}
void SessionCoordinator::ActivateSession(size_t index) {
SwitchToSession(index);
}
size_t SessionCoordinator::GetActiveSessionIndex() const {
return active_session_index_;
}
void* SessionCoordinator::GetActiveSession() {
auto* sessions = GET_SESSIONS();
if (!sessions || !IsValidSessionIndex(active_session_index_)) {
return nullptr;
}
return &sessions->at(active_session_index_);
}
void* SessionCoordinator::GetSession(size_t index) {
auto* sessions = GET_SESSIONS();
if (!sessions || !IsValidSessionIndex(index)) {
return nullptr;
}
return &sessions->at(index);
}
bool SessionCoordinator::HasMultipleSessions() const {
return session_count_ > 1;
}
size_t SessionCoordinator::GetActiveSessionCount() const {
return session_count_;
}
bool SessionCoordinator::HasDuplicateSession(const std::string& filepath) const {
auto* sessions = GET_SESSIONS();
if (!sessions || filepath.empty()) return false;
for (const auto& session : *sessions) {
if (session.filepath == filepath) {
return true;
}
}
return false;
}
void SessionCoordinator::DrawSessionSwitcher() {
auto* sessions = GET_SESSIONS();
if (!sessions || sessions->empty()) return;
if (!show_session_switcher_) return;
ImGui::SetNextWindowSize(ImVec2(400, 300), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(),
ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
if (!ImGui::Begin("Session Switcher", &show_session_switcher_)) {
ImGui::End();
return;
}
ImGui::Text("%s Active Sessions (%zu)", ICON_MD_TAB, session_count_);
ImGui::Separator();
for (size_t i = 0; i < sessions->size(); ++i) {
const auto& session = sessions->at(i);
bool is_active = (i == active_session_index_);
ImGui::PushID(static_cast<int>(i));
// Session tab
if (ImGui::Selectable(GetSessionDisplayName(i).c_str(), is_active)) {
SwitchToSession(i);
}
// Right-click context menu
if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
ImGui::OpenPopup("SessionContextMenu");
}
if (ImGui::BeginPopup("SessionContextMenu")) {
DrawSessionContextMenu(i);
ImGui::EndPopup();
}
ImGui::PopID();
}
ImGui::Separator();
// Action buttons
if (ImGui::Button(absl::StrFormat("%s New Session", ICON_MD_ADD).c_str())) {
CreateNewSession();
}
ImGui::SameLine();
if (ImGui::Button(absl::StrFormat("%s Duplicate", ICON_MD_CONTENT_COPY).c_str())) {
DuplicateCurrentSession();
}
ImGui::SameLine();
if (HasMultipleSessions() && ImGui::Button(absl::StrFormat("%s Close", ICON_MD_CLOSE).c_str())) {
CloseCurrentSession();
}
ImGui::End();
}
void SessionCoordinator::DrawSessionManager() {
auto* sessions = GET_SESSIONS();
if (!sessions || sessions->empty()) return;
if (!show_session_manager_) return;
ImGui::SetNextWindowSize(ImVec2(600, 400), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(),
ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
if (!ImGui::Begin("Session Manager", &show_session_manager_)) {
ImGui::End();
return;
}
// Session statistics
ImGui::Text("%s Session Statistics", ICON_MD_ANALYTICS);
ImGui::Separator();
ImGui::Text("Total Sessions: %zu", GetTotalSessionCount());
ImGui::Text("Loaded Sessions: %zu", GetLoadedSessionCount());
ImGui::Text("Empty Sessions: %zu", GetEmptySessionCount());
ImGui::Spacing();
// Session list
if (ImGui::BeginTable("SessionTable", 4,
ImGuiTableFlags_Borders |
ImGuiTableFlags_RowBg |
ImGuiTableFlags_Resizable)) {
ImGui::TableSetupColumn("Session", ImGuiTableColumnFlags_WidthStretch, 0.3f);
ImGui::TableSetupColumn("ROM File", ImGuiTableColumnFlags_WidthStretch, 0.4f);
ImGui::TableSetupColumn("Status", ImGuiTableColumnFlags_WidthStretch, 0.2f);
ImGui::TableSetupColumn("Actions", ImGuiTableColumnFlags_WidthFixed, 120.0f);
ImGui::TableHeadersRow();
for (size_t i = 0; i < sessions->size(); ++i) {
const auto& session = sessions->at(i);
bool is_active = (i == active_session_index_);
ImGui::PushID(static_cast<int>(i));
ImGui::TableNextRow();
// Session name
ImGui::TableNextColumn();
if (is_active) {
ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f), "%s %s",
ICON_MD_RADIO_BUTTON_CHECKED, GetSessionDisplayName(i).c_str());
} else {
ImGui::Text("%s %s", ICON_MD_RADIO_BUTTON_UNCHECKED, GetSessionDisplayName(i).c_str());
}
// ROM file
ImGui::TableNextColumn();
if (session.rom.is_loaded()) {
ImGui::Text("%s", session.filepath.c_str());
} else {
ImGui::TextDisabled("(No ROM loaded)");
}
// Status
ImGui::TableNextColumn();
if (session.rom.is_loaded()) {
ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f), "Loaded");
} else {
ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "Empty");
}
// Actions
ImGui::TableNextColumn();
if (!is_active && ImGui::SmallButton("Switch")) {
SwitchToSession(i);
}
ImGui::SameLine();
if (HasMultipleSessions() && ImGui::SmallButton("Close")) {
CloseSession(i);
}
ImGui::PopID();
}
ImGui::EndTable();
}
ImGui::End();
}
void SessionCoordinator::DrawSessionRenameDialog() {
if (!show_session_rename_dialog_) return;
ImGui::SetNextWindowSize(ImVec2(300, 150), ImGuiCond_Always);
ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(),
ImGuiCond_Always, ImVec2(0.5f, 0.5f));
if (!ImGui::Begin("Rename Session", &show_session_rename_dialog_)) {
ImGui::End();
return;
}
ImGui::Text("Rename session %zu:", session_to_rename_);
ImGui::InputText("Name", session_rename_buffer_, sizeof(session_rename_buffer_));
ImGui::Spacing();
if (ImGui::Button("OK")) {
RenameSession(session_to_rename_, session_rename_buffer_);
show_session_rename_dialog_ = false;
session_rename_buffer_[0] = '\0';
}
ImGui::SameLine();
if (ImGui::Button("Cancel")) {
show_session_rename_dialog_ = false;
session_rename_buffer_[0] = '\0';
}
ImGui::End();
}
void SessionCoordinator::DrawSessionTabs() {
auto* sessions = GET_SESSIONS();
if (!sessions || sessions->empty()) return;
if (ImGui::BeginTabBar("SessionTabs")) {
for (size_t i = 0; i < sessions->size(); ++i) {
bool is_active = (i == active_session_index_);
const auto& session = sessions->at(i);
std::string tab_name = GetSessionDisplayName(i);
if (session.rom.is_loaded()) {
tab_name += " ";
tab_name += ICON_MD_CHECK_CIRCLE;
}
if (ImGui::BeginTabItem(tab_name.c_str())) {
if (!is_active) {
SwitchToSession(i);
}
ImGui::EndTabItem();
}
// Right-click context menu
if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
ImGui::OpenPopup(absl::StrFormat("SessionTabContext_%zu", i).c_str());
}
if (ImGui::BeginPopup(absl::StrFormat("SessionTabContext_%zu", i).c_str())) {
DrawSessionContextMenu(i);
ImGui::EndPopup();
}
}
ImGui::EndTabBar();
}
}
void SessionCoordinator::DrawSessionIndicator() {
if (!HasMultipleSessions()) return;
const auto& theme = gui::ThemeManager::Get().GetCurrentTheme();
ImVec4 accent_color = ConvertColorToImVec4(theme.accent);
ImGui::PushStyleColor(ImGuiCol_Text, accent_color);
ImGui::Text("%s Session %zu", ICON_MD_TAB, active_session_index_);
ImGui::PopStyleColor();
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Active Session: %s\nClick to open session switcher",
GetActiveSessionDisplayName().c_str());
}
if (ImGui::IsItemClicked()) {
ToggleSessionSwitcher();
}
}
std::string SessionCoordinator::GetSessionDisplayName(size_t index) const {
auto* sessions = GET_SESSIONS();
if (!sessions || !IsValidSessionIndex(index)) {
return "Invalid Session";
}
const auto& session = sessions->at(index);
if (!session.custom_name.empty()) {
return session.custom_name;
}
if (session.rom.is_loaded()) {
return absl::StrFormat("Session %zu (%s)", index,
std::filesystem::path(session.filepath).stem().string());
}
return absl::StrFormat("Session %zu (Empty)", index);
}
std::string SessionCoordinator::GetActiveSessionDisplayName() const {
return GetSessionDisplayName(active_session_index_);
}
void SessionCoordinator::RenameSession(size_t index, const std::string& new_name) {
auto* sessions = GET_SESSIONS();
if (!sessions || !IsValidSessionIndex(index) || new_name.empty()) return;
sessions->at(index).custom_name = new_name;
printf("[SessionCoordinator] Renamed session %zu to '%s'\n", index, new_name.c_str());
}
void SessionCoordinator::SetActiveSessionIndex(size_t index) {
SwitchToSession(index);
}
void SessionCoordinator::UpdateSessionCount() {
auto* sessions = GET_SESSIONS();
if (sessions) {
session_count_ = sessions->size();
} else {
session_count_ = 0;
}
}
void SessionCoordinator::ShowAllCardsInActiveSession() {
if (card_registry_) {
card_registry_->ShowAllCardsInSession(active_session_index_);
}
}
void SessionCoordinator::HideAllCardsInActiveSession() {
if (card_registry_) {
card_registry_->HideAllCardsInSession(active_session_index_);
}
}
void SessionCoordinator::ShowCardsInCategory(const std::string& category) {
if (card_registry_) {
card_registry_->ShowAllCardsInCategory(active_session_index_, category);
}
}
void SessionCoordinator::HideCardsInCategory(const std::string& category) {
if (card_registry_) {
card_registry_->HideAllCardsInCategory(active_session_index_, category);
}
}
bool SessionCoordinator::IsValidSessionIndex(size_t index) const {
auto* sessions = GET_SESSIONS();
return sessions && index < sessions->size();
}
bool SessionCoordinator::IsSessionActive(size_t index) const {
return index == active_session_index_;
}
bool SessionCoordinator::IsSessionLoaded(size_t index) const {
auto* sessions = GET_SESSIONS();
return IsValidSessionIndex(index) && sessions && sessions->at(index).rom.is_loaded();
}
size_t SessionCoordinator::GetTotalSessionCount() const {
return session_count_;
}
size_t SessionCoordinator::GetLoadedSessionCount() const {
auto* sessions = GET_SESSIONS();
if (!sessions) return 0;
size_t count = 0;
for (const auto& session : *sessions) {
if (session.rom.is_loaded()) {
count++;
}
}
return count;
}
size_t SessionCoordinator::GetEmptySessionCount() const {
return session_count_ - GetLoadedSessionCount();
}
absl::Status SessionCoordinator::LoadRomIntoSession(const std::string& filename, size_t session_index) {
auto* sessions = GET_SESSIONS();
if (!sessions || filename.empty()) {
return absl::InvalidArgumentError("Invalid parameters");
}
size_t target_index = (session_index == SIZE_MAX) ? active_session_index_ : session_index;
if (!IsValidSessionIndex(target_index)) {
return absl::InvalidArgumentError("Invalid session index");
}
// TODO: Implement actual ROM loading
printf("[SessionCoordinator] LoadRomIntoSession: %s -> session %zu\n",
filename.c_str(), target_index);
return absl::OkStatus();
}
absl::Status SessionCoordinator::SaveActiveSession(const std::string& filename) {
auto* sessions = GET_SESSIONS();
if (!sessions || !IsValidSessionIndex(active_session_index_)) {
return absl::FailedPreconditionError("No active session");
}
// TODO: Implement actual ROM saving
printf("[SessionCoordinator] SaveActiveSession: session %zu\n", active_session_index_);
return absl::OkStatus();
}
absl::Status SessionCoordinator::SaveSessionAs(size_t session_index, const std::string& filename) {
auto* sessions = GET_SESSIONS();
if (!sessions || !IsValidSessionIndex(session_index) || filename.empty()) {
return absl::InvalidArgumentError("Invalid parameters");
}
// TODO: Implement actual ROM saving
printf("[SessionCoordinator] SaveSessionAs: session %zu -> %s\n",
session_index, filename.c_str());
return absl::OkStatus();
}
void SessionCoordinator::CleanupClosedSessions() {
auto* sessions = GET_SESSIONS();
if (!sessions) return;
// Mark empty sessions as closed (except keep at least one)
// TODO: Actually remove when editors become movable
size_t loaded_count = 0;
for (auto& session : *sessions) {
if (session.rom.is_loaded()) {
loaded_count++;
}
}
if (loaded_count > 0) {
for (auto& session : *sessions) {
if (!session.rom.is_loaded() && sessions->size() > 1) {
session.custom_name = "[CLOSED SESSION]";
}
}
}
UpdateSessionCount();
printf("[SessionCoordinator] Cleaned up closed sessions (remaining: %zu)\n", session_count_);
}
void SessionCoordinator::ClearAllSessions() {
auto* sessions = GET_SESSIONS();
if (!sessions) return;
// Unregister all session cards
if (card_registry_) {
for (size_t i = 0; i < sessions->size(); ++i) {
card_registry_->UnregisterSession(i);
}
}
// Mark all sessions as closed instead of clearing
// TODO: Actually clear when editors become movable
for (auto& session : *sessions) {
session.custom_name = "[CLOSED SESSION]";
}
active_session_index_ = 0;
UpdateSessionCount();
printf("[SessionCoordinator] Cleared all sessions\n");
}
void SessionCoordinator::FocusNextSession() {
auto* sessions = GET_SESSIONS();
if (!sessions || sessions->empty()) return;
size_t next_index = (active_session_index_ + 1) % sessions->size();
SwitchToSession(next_index);
}
void SessionCoordinator::FocusPreviousSession() {
auto* sessions = GET_SESSIONS();
if (!sessions || sessions->empty()) return;
size_t prev_index = (active_session_index_ == 0) ?
sessions->size() - 1 : active_session_index_ - 1;
SwitchToSession(prev_index);
}
void SessionCoordinator::FocusFirstSession() {
auto* sessions = GET_SESSIONS();
if (!sessions || sessions->empty()) return;
SwitchToSession(0);
}
void SessionCoordinator::FocusLastSession() {
auto* sessions = GET_SESSIONS();
if (!sessions || sessions->empty()) return;
SwitchToSession(sessions->size() - 1);
}
void SessionCoordinator::UpdateActiveSession() {
auto* sessions = GET_SESSIONS();
if (sessions && !sessions->empty() && active_session_index_ >= sessions->size()) {
active_session_index_ = sessions->size() - 1;
}
}
void SessionCoordinator::ValidateSessionIndex(size_t index) const {
if (!IsValidSessionIndex(index)) {
throw std::out_of_range(absl::StrFormat("Invalid session index: %zu", index));
}
}
std::string SessionCoordinator::GenerateUniqueSessionName(const std::string& base_name) const {
auto* sessions = GET_SESSIONS();
if (!sessions) return base_name;
std::string name = base_name;
int counter = 1;
while (true) {
bool found = false;
for (const auto& session : *sessions) {
if (session.custom_name == name) {
found = true;
break;
}
}
if (!found) break;
name = absl::StrFormat("%s %d", base_name, counter++);
}
return name;
}
void SessionCoordinator::ShowSessionLimitWarning() {
if (toast_manager_) {
toast_manager_->Show(
absl::StrFormat("Maximum %zu sessions allowed", kMaxSessions),
ToastType::kWarning);
}
}
void SessionCoordinator::ShowSessionOperationResult(const std::string& operation, bool success) {
if (toast_manager_) {
std::string message = absl::StrFormat("%s %s", operation,
success ? "succeeded" : "failed");
ToastType type = success ? ToastType::kSuccess : ToastType::kError;
toast_manager_->Show(message, type);
}
}
void SessionCoordinator::DrawSessionTab(size_t index, bool is_active) {
auto* sessions = GET_SESSIONS();
if (!sessions || index >= sessions->size()) return;
const auto& session = sessions->at(index);
ImVec4 color = GetSessionColor(index);
ImGui::PushStyleColor(ImGuiCol_Text, color);
std::string tab_name = GetSessionDisplayName(index);
if (session.rom.is_loaded()) {
tab_name += " ";
tab_name += ICON_MD_CHECK_CIRCLE;
}
if (ImGui::BeginTabItem(tab_name.c_str())) {
if (!is_active) {
SwitchToSession(index);
}
ImGui::EndTabItem();
}
ImGui::PopStyleColor();
}
void SessionCoordinator::DrawSessionContextMenu(size_t index) {
if (ImGui::MenuItem(absl::StrFormat("%s Switch to Session", ICON_MD_TAB).c_str())) {
SwitchToSession(index);
}
if (ImGui::MenuItem(absl::StrFormat("%s Rename", ICON_MD_EDIT).c_str())) {
session_to_rename_ = index;
strncpy(session_rename_buffer_, GetSessionDisplayName(index).c_str(),
sizeof(session_rename_buffer_) - 1);
session_rename_buffer_[sizeof(session_rename_buffer_) - 1] = '\0';
show_session_rename_dialog_ = true;
}
if (ImGui::MenuItem(absl::StrFormat("%s Duplicate", ICON_MD_CONTENT_COPY).c_str())) {
// TODO: Implement session duplication
}
ImGui::Separator();
if (HasMultipleSessions() &&
ImGui::MenuItem(absl::StrFormat("%s Close Session", ICON_MD_CLOSE).c_str())) {
CloseSession(index);
}
}
void SessionCoordinator::DrawSessionBadge(size_t index) {
auto* sessions = GET_SESSIONS();
if (!sessions || index >= sessions->size()) return;
const auto& session = sessions->at(index);
ImVec4 color = GetSessionColor(index);
ImGui::PushStyleColor(ImGuiCol_Text, color);
if (session.rom.is_loaded()) {
ImGui::Text("%s", ICON_MD_CHECK_CIRCLE);
} else {
ImGui::Text("%s", ICON_MD_RADIO_BUTTON_UNCHECKED);
}
ImGui::PopStyleColor();
}
ImVec4 SessionCoordinator::GetSessionColor(size_t index) const {
// Generate consistent colors for sessions
static const ImVec4 colors[] = {
ImVec4(0.0f, 1.0f, 0.0f, 1.0f), // Green
ImVec4(0.0f, 0.5f, 1.0f, 1.0f), // Blue
ImVec4(1.0f, 0.5f, 0.0f, 1.0f), // Orange
ImVec4(1.0f, 0.0f, 1.0f, 1.0f), // Magenta
ImVec4(1.0f, 1.0f, 0.0f, 1.0f), // Yellow
ImVec4(0.0f, 1.0f, 1.0f, 1.0f), // Cyan
ImVec4(1.0f, 0.0f, 0.0f, 1.0f), // Red
ImVec4(0.5f, 0.5f, 0.5f, 1.0f), // Gray
};
return colors[index % (sizeof(colors) / sizeof(colors[0]))];
}
std::string SessionCoordinator::GetSessionIcon(size_t index) const {
auto* sessions = GET_SESSIONS();
if (!sessions || index >= sessions->size()) return ICON_MD_RADIO_BUTTON_UNCHECKED;
const auto& session = sessions->at(index);
if (session.rom.is_loaded()) {
return ICON_MD_CHECK_CIRCLE;
} else {
return ICON_MD_RADIO_BUTTON_UNCHECKED;
}
}
bool SessionCoordinator::IsSessionEmpty(size_t index) const {
auto* sessions = GET_SESSIONS();
return IsValidSessionIndex(index) && sessions && !sessions->at(index).rom.is_loaded();
}
bool SessionCoordinator::IsSessionClosed(size_t index) const {
return !IsValidSessionIndex(index);
}
bool SessionCoordinator::IsSessionModified(size_t index) const {
// TODO: Implement modification tracking
return false;
}
} // namespace editor
} // namespace yaze

View File

@@ -0,0 +1,163 @@
#ifndef YAZE_APP_EDITOR_UI_SESSION_COORDINATOR_H_
#define YAZE_APP_EDITOR_UI_SESSION_COORDINATOR_H_
#include <deque>
#include <string>
#include <vector>
#include "absl/status/status.h"
#include "app/editor/system/session_card_registry.h"
#include "app/editor/system/toast_manager.h"
#include "app/rom.h"
#include "imgui/imgui.h"
// Forward declarations
namespace yaze {
namespace editor {
class EditorManager;
}
}
namespace yaze {
namespace editor {
// Forward declarations
class EditorSet;
class ToastManager;
/**
* @class SessionCoordinator
* @brief High-level orchestrator for multi-session UI
*
* Manages session list UI, coordinates card visibility across sessions,
* handles session activation/deactivation, and provides session-aware editor queries.
*
* This class lives in the ui/ layer and can depend on both system and gui components.
*/
class SessionCoordinator {
public:
explicit SessionCoordinator(void* sessions_ptr,
SessionCardRegistry* card_registry,
ToastManager* toast_manager);
~SessionCoordinator() = default;
// Session lifecycle management
void CreateNewSession();
void DuplicateCurrentSession();
void CloseCurrentSession();
void CloseSession(size_t index);
void RemoveSession(size_t index);
void SwitchToSession(size_t index);
// Session activation and queries
void ActivateSession(size_t index);
size_t GetActiveSessionIndex() const;
void* GetActiveSession();
void* GetSession(size_t index);
bool HasMultipleSessions() const;
size_t GetActiveSessionCount() const;
bool HasDuplicateSession(const std::string& filepath) const;
// Session UI components
void DrawSessionSwitcher();
void DrawSessionManager();
void DrawSessionRenameDialog();
void DrawSessionTabs();
void DrawSessionIndicator();
// Session information
std::string GetSessionDisplayName(size_t index) const;
std::string GetActiveSessionDisplayName() const;
void RenameSession(size_t index, const std::string& new_name);
// Session state management
void SetActiveSessionIndex(size_t index);
void UpdateSessionCount();
// Card coordination across sessions
void ShowAllCardsInActiveSession();
void HideAllCardsInActiveSession();
void ShowCardsInCategory(const std::string& category);
void HideCardsInCategory(const std::string& category);
// Session validation
bool IsValidSessionIndex(size_t index) const;
bool IsSessionActive(size_t index) const;
bool IsSessionLoaded(size_t index) const;
// Session statistics
size_t GetTotalSessionCount() const;
size_t GetLoadedSessionCount() const;
size_t GetEmptySessionCount() const;
// Session operations with error handling
absl::Status LoadRomIntoSession(const std::string& filename, size_t session_index = SIZE_MAX);
absl::Status SaveActiveSession(const std::string& filename = "");
absl::Status SaveSessionAs(size_t session_index, const std::string& filename);
// Session cleanup
void CleanupClosedSessions();
void ClearAllSessions();
// Session navigation
void FocusNextSession();
void FocusPreviousSession();
void FocusFirstSession();
void FocusLastSession();
// Session UI state
void ShowSessionSwitcher() { show_session_switcher_ = true; }
void HideSessionSwitcher() { show_session_switcher_ = false; }
void ToggleSessionSwitcher() { show_session_switcher_ = !show_session_switcher_; }
bool IsSessionSwitcherVisible() const { return show_session_switcher_; }
void ShowSessionManager() { show_session_manager_ = true; }
void HideSessionManager() { show_session_manager_ = false; }
void ToggleSessionManager() { show_session_manager_ = !show_session_manager_; }
bool IsSessionManagerVisible() const { return show_session_manager_; }
private:
// Core dependencies
void* sessions_ptr_; // std::deque<EditorManager::RomSession>*
SessionCardRegistry* card_registry_;
ToastManager* toast_manager_;
// Session state
size_t active_session_index_ = 0;
size_t session_count_ = 0;
// UI state
bool show_session_switcher_ = false;
bool show_session_manager_ = false;
bool show_session_rename_dialog_ = false;
size_t session_to_rename_ = 0;
char session_rename_buffer_[256] = {};
// Session limits
static constexpr size_t kMaxSessions = 8;
static constexpr size_t kMinSessions = 1;
// Helper methods
void UpdateActiveSession();
void ValidateSessionIndex(size_t index) const;
std::string GenerateUniqueSessionName(const std::string& base_name) const;
void ShowSessionLimitWarning();
void ShowSessionOperationResult(const std::string& operation, bool success);
// UI helper methods
void DrawSessionTab(size_t index, bool is_active);
void DrawSessionContextMenu(size_t index);
void DrawSessionBadge(size_t index);
ImVec4 GetSessionColor(size_t index) const;
std::string GetSessionIcon(size_t index) const;
// Session validation helpers
bool IsSessionEmpty(size_t index) const;
bool IsSessionClosed(size_t index) const;
bool IsSessionModified(size_t index) const;
};
} // namespace editor
} // namespace yaze
#endif // YAZE_APP_EDITOR_UI_SESSION_COORDINATOR_H_