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:
@@ -85,10 +85,22 @@ if(NOT YAZE_WITH_GRPC)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(YAZE_PROTOBUF_TARGETS)
|
||||
|
||||
if(TARGET protobuf::libprotobuf)
|
||||
set(YAZE_PROTOBUF_TARGET protobuf::libprotobuf)
|
||||
list(APPEND YAZE_PROTOBUF_TARGETS protobuf::libprotobuf)
|
||||
elseif(TARGET libprotobuf)
|
||||
set(YAZE_PROTOBUF_TARGET libprotobuf)
|
||||
list(APPEND YAZE_PROTOBUF_TARGETS libprotobuf)
|
||||
endif()
|
||||
|
||||
if(TARGET protobuf::libprotobuf-lite)
|
||||
list(APPEND YAZE_PROTOBUF_TARGETS protobuf::libprotobuf-lite)
|
||||
elseif(TARGET libprotobuf-lite)
|
||||
list(APPEND YAZE_PROTOBUF_TARGETS libprotobuf-lite)
|
||||
endif()
|
||||
|
||||
if(YAZE_PROTOBUF_TARGETS)
|
||||
list(GET YAZE_PROTOBUF_TARGETS 0 YAZE_PROTOBUF_TARGET)
|
||||
else()
|
||||
set(YAZE_PROTOBUF_TARGET "")
|
||||
endif()
|
||||
|
||||
@@ -40,8 +40,13 @@ target_link_libraries(yaze PRIVATE
|
||||
absl::flags
|
||||
absl::flags_parse
|
||||
)
|
||||
if(YAZE_WITH_GRPC AND YAZE_PROTOBUF_TARGET)
|
||||
target_link_libraries(yaze PRIVATE ${YAZE_PROTOBUF_TARGET})
|
||||
if(YAZE_WITH_GRPC AND YAZE_PROTOBUF_TARGETS)
|
||||
target_link_libraries(yaze PRIVATE ${YAZE_PROTOBUF_TARGETS})
|
||||
if(MSVC)
|
||||
foreach(_yaze_proto_target IN LISTS YAZE_PROTOBUF_TARGETS)
|
||||
target_link_options(yaze PRIVATE /WHOLEARCHIVE:$<TARGET_FILE:${_yaze_proto_target}>)
|
||||
endforeach()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Link test support library (yaze_editor needs TestManager)
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "app/core/window.h"
|
||||
#include "app/rom.h"
|
||||
#include "app/editor/editor_manager.h"
|
||||
#include "app/gfx/backend/irenderer.h"
|
||||
|
||||
|
||||
@@ -145,10 +145,12 @@ if(YAZE_WITH_GRPC)
|
||||
grpc++
|
||||
grpc++_reflection
|
||||
)
|
||||
if(YAZE_PROTOBUF_TARGET)
|
||||
target_link_libraries(yaze_core_lib PUBLIC ${YAZE_PROTOBUF_TARGET})
|
||||
if(MSVC AND CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
target_link_options(yaze_core_lib PUBLIC /WHOLEARCHIVE:$<TARGET_FILE:${YAZE_PROTOBUF_TARGET}>)
|
||||
if(YAZE_PROTOBUF_TARGETS)
|
||||
target_link_libraries(yaze_core_lib PUBLIC ${YAZE_PROTOBUF_TARGETS})
|
||||
if(MSVC)
|
||||
foreach(_yaze_proto_target IN LISTS YAZE_PROTOBUF_TARGETS)
|
||||
target_link_options(yaze_core_lib PUBLIC /WHOLEARCHIVE:$<TARGET_FILE:${_yaze_proto_target}>)
|
||||
endforeach()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
250
src/app/editor/system/session_card_registry.cc
Normal file
250
src/app/editor/system/session_card_registry.cc
Normal 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
|
||||
96
src/app/editor/system/session_card_registry.h
Normal file
96
src/app/editor/system/session_card_registry.h
Normal 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_
|
||||
298
src/app/editor/system/window_delegate.cc
Normal file
298
src/app/editor/system/window_delegate.cc
Normal 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
|
||||
96
src/app/editor/system/window_delegate.h
Normal file
96
src/app/editor/system/window_delegate.h
Normal 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_
|
||||
806
src/app/editor/ui/session_coordinator.cc
Normal file
806
src/app/editor/ui/session_coordinator.cc
Normal 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
|
||||
163
src/app/editor/ui/session_coordinator.h
Normal file
163
src/app/editor/ui/session_coordinator.h
Normal 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_
|
||||
@@ -88,6 +88,25 @@ void EditorCardManager::UnregisterCard(const std::string& card_id) {
|
||||
}
|
||||
}
|
||||
|
||||
void EditorCardManager::UnregisterCardsWithPrefix(const std::string& prefix) {
|
||||
std::vector<std::string> to_remove;
|
||||
|
||||
// Find all cards with the given prefix
|
||||
for (const auto& [card_id, card_info] : cards_) {
|
||||
if (card_id.find(prefix) == 0) { // Starts with prefix
|
||||
to_remove.push_back(card_id);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove all found cards
|
||||
for (const auto& card_id : to_remove) {
|
||||
UnregisterCard(card_id);
|
||||
}
|
||||
|
||||
printf("[EditorCardManager] Unregistered %zu cards with prefix: %s\n",
|
||||
to_remove.size(), prefix.c_str());
|
||||
}
|
||||
|
||||
void EditorCardManager::ClearAllCards() {
|
||||
printf("[EditorCardManager] Clearing all %zu registered cards\n", cards_.size());
|
||||
cards_.clear();
|
||||
@@ -230,6 +249,24 @@ std::vector<CardInfo> EditorCardManager::GetCardsInCategory(const std::string& c
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<CardInfo> EditorCardManager::GetCardsWithPrefix(const std::string& prefix) const {
|
||||
std::vector<CardInfo> result;
|
||||
|
||||
for (const auto& [id, info] : cards_) {
|
||||
if (id.find(prefix) == 0) { // Starts with prefix
|
||||
result.push_back(info);
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by priority
|
||||
std::sort(result.begin(), result.end(),
|
||||
[](const CardInfo& a, const CardInfo& b) {
|
||||
return a.priority < b.priority;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::string> EditorCardManager::GetAllCategories() const {
|
||||
std::vector<std::string> categories;
|
||||
|
||||
@@ -319,7 +356,23 @@ void EditorCardManager::DrawViewMenuAll() {
|
||||
}
|
||||
|
||||
void EditorCardManager::DrawCompactCardControl(const std::string& category) {
|
||||
auto cards_in_category = GetCardsInCategory(category);
|
||||
DrawCompactCardControlWithSession(category, "");
|
||||
}
|
||||
|
||||
void EditorCardManager::DrawCompactCardControlWithSession(const std::string& category, const std::string& session_prefix) {
|
||||
std::vector<CardInfo> cards_in_category;
|
||||
|
||||
if (session_prefix.empty()) {
|
||||
cards_in_category = GetCardsInCategory(category);
|
||||
} else {
|
||||
// Filter cards by session prefix
|
||||
auto all_cards = GetCardsWithPrefix(session_prefix);
|
||||
for (const auto& card : all_cards) {
|
||||
if (card.category == category) {
|
||||
cards_in_category.push_back(card);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cards_in_category.empty()) {
|
||||
return; // Nothing to show
|
||||
@@ -337,13 +390,21 @@ void EditorCardManager::DrawCompactCardControl(const std::string& category) {
|
||||
ImGui::PopStyleColor(3);
|
||||
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("%s Card Controls", category.c_str());
|
||||
std::string tooltip = category + " Card Controls";
|
||||
if (!session_prefix.empty()) {
|
||||
tooltip += " (" + session_prefix + ")";
|
||||
}
|
||||
ImGui::SetTooltip("%s", tooltip.c_str());
|
||||
}
|
||||
|
||||
// Compact popup with checkboxes
|
||||
if (ImGui::BeginPopup("CardControlPopup")) {
|
||||
ImGui::TextColored(ImVec4(0.7f, 0.9f, 1.0f, 1.0f), "%s %s Cards",
|
||||
ICON_MD_DASHBOARD, category.c_str());
|
||||
std::string title = category + " Cards";
|
||||
if (!session_prefix.empty()) {
|
||||
title += " (" + session_prefix + ")";
|
||||
}
|
||||
ImGui::TextColored(ImVec4(0.7f, 0.9f, 1.0f, 1.0f), "%s %s",
|
||||
ICON_MD_DASHBOARD, title.c_str());
|
||||
ImGui::Separator();
|
||||
|
||||
for (const auto& info : cards_in_category) {
|
||||
@@ -743,9 +804,17 @@ void EditorCardManager::SetActiveCategory(const std::string& category) {
|
||||
}
|
||||
|
||||
void EditorCardManager::DrawSidebar(const std::string& category,
|
||||
const std::vector<std::string>& active_categories,
|
||||
std::function<void(const std::string&)> on_category_switch,
|
||||
std::function<void()> on_collapse) {
|
||||
const std::vector<std::string>& active_categories,
|
||||
std::function<void(const std::string&)> on_category_switch,
|
||||
std::function<void()> on_collapse) {
|
||||
DrawSidebarWithSessionFilter(category, "", active_categories, on_category_switch, on_collapse);
|
||||
}
|
||||
|
||||
void EditorCardManager::DrawSidebarWithSessionFilter(const std::string& category,
|
||||
const std::string& session_prefix,
|
||||
const std::vector<std::string>& active_categories,
|
||||
std::function<void(const std::string&)> on_category_switch,
|
||||
std::function<void()> on_collapse) {
|
||||
// Use ThemeManager for consistent theming
|
||||
const auto& theme = ThemeManager::Get().GetCurrentTheme();
|
||||
|
||||
|
||||
@@ -88,6 +88,7 @@ class EditorCardManager {
|
||||
bool visible_by_default = false);
|
||||
|
||||
void UnregisterCard(const std::string& card_id);
|
||||
void UnregisterCardsWithPrefix(const std::string& prefix);
|
||||
void ClearAllCards();
|
||||
|
||||
// Card control (programmatic, no GUI)
|
||||
@@ -106,6 +107,7 @@ class EditorCardManager {
|
||||
|
||||
// Query
|
||||
std::vector<CardInfo> GetCardsInCategory(const std::string& category) const;
|
||||
std::vector<CardInfo> GetCardsWithPrefix(const std::string& prefix) const;
|
||||
std::vector<std::string> GetAllCategories() const;
|
||||
const CardInfo* GetCardInfo(const std::string& card_id) const;
|
||||
|
||||
@@ -118,6 +120,11 @@ class EditorCardManager {
|
||||
const std::vector<std::string>& active_categories = {},
|
||||
std::function<void(const std::string&)> on_category_switch = nullptr,
|
||||
std::function<void()> on_collapse = nullptr);
|
||||
void DrawSidebarWithSessionFilter(const std::string& category,
|
||||
const std::string& session_prefix = "",
|
||||
const std::vector<std::string>& active_categories = {},
|
||||
std::function<void(const std::string&)> on_category_switch = nullptr,
|
||||
std::function<void()> on_collapse = nullptr);
|
||||
static constexpr float GetSidebarWidth() { return 48.0f; }
|
||||
|
||||
// Active editor tracking (based on most recently interacted card)
|
||||
@@ -127,6 +134,7 @@ class EditorCardManager {
|
||||
|
||||
// Compact inline card control for menu bar
|
||||
void DrawCompactCardControl(const std::string& category); // Shows only active editor's cards
|
||||
void DrawCompactCardControlWithSession(const std::string& category, const std::string& session_prefix = "");
|
||||
void DrawInlineCardToggles(const std::string& category); // Minimal inline checkboxes
|
||||
|
||||
// Card browser UI
|
||||
|
||||
@@ -84,10 +84,12 @@ if(YAZE_WITH_GRPC)
|
||||
grpc++
|
||||
grpc++_reflection
|
||||
)
|
||||
if(YAZE_PROTOBUF_TARGET)
|
||||
target_link_libraries(yaze_net PUBLIC ${YAZE_PROTOBUF_TARGET})
|
||||
if(MSVC AND CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
target_link_options(yaze_net PUBLIC /WHOLEARCHIVE:$<TARGET_FILE:${YAZE_PROTOBUF_TARGET}>)
|
||||
if(YAZE_PROTOBUF_TARGETS)
|
||||
target_link_libraries(yaze_net PUBLIC ${YAZE_PROTOBUF_TARGETS})
|
||||
if(MSVC)
|
||||
foreach(_yaze_proto_target IN LISTS YAZE_PROTOBUF_TARGETS)
|
||||
target_link_options(yaze_net PUBLIC /WHOLEARCHIVE:$<TARGET_FILE:${_yaze_proto_target}>)
|
||||
endforeach()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
@@ -161,10 +161,12 @@ if(YAZE_WITH_GRPC)
|
||||
grpc++
|
||||
grpc++_reflection
|
||||
)
|
||||
if(YAZE_PROTOBUF_TARGET)
|
||||
target_link_libraries(yaze_agent PUBLIC ${YAZE_PROTOBUF_TARGET})
|
||||
if(MSVC AND CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
target_link_options(yaze_agent PUBLIC /WHOLEARCHIVE:$<TARGET_FILE:${YAZE_PROTOBUF_TARGET}>)
|
||||
if(YAZE_PROTOBUF_TARGETS)
|
||||
target_link_libraries(yaze_agent PUBLIC ${YAZE_PROTOBUF_TARGETS})
|
||||
if(MSVC)
|
||||
foreach(_yaze_proto_target IN LISTS YAZE_PROTOBUF_TARGETS)
|
||||
target_link_options(yaze_agent PUBLIC /WHOLEARCHIVE:$<TARGET_FILE:${_yaze_proto_target}>)
|
||||
endforeach()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
@@ -40,10 +40,12 @@ endif()
|
||||
if(YAZE_WITH_GRPC)
|
||||
message(STATUS "Adding gRPC support to z3ed CLI")
|
||||
target_link_libraries(z3ed PRIVATE grpc++ grpc++_reflection)
|
||||
if(YAZE_PROTOBUF_TARGET)
|
||||
target_link_libraries(z3ed PRIVATE ${YAZE_PROTOBUF_TARGET})
|
||||
if(MSVC AND CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
target_link_options(z3ed PRIVATE /WHOLEARCHIVE:$<TARGET_FILE:${YAZE_PROTOBUF_TARGET}>)
|
||||
if(YAZE_PROTOBUF_TARGETS)
|
||||
target_link_libraries(z3ed PRIVATE ${YAZE_PROTOBUF_TARGETS})
|
||||
if(MSVC)
|
||||
foreach(_yaze_proto_target IN LISTS YAZE_PROTOBUF_TARGETS)
|
||||
target_link_options(z3ed PRIVATE /WHOLEARCHIVE:$<TARGET_FILE:${_yaze_proto_target}>)
|
||||
endforeach()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -1,78 +1,82 @@
|
||||
#include "zelda3/palette_constants.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace yaze {
|
||||
namespace zelda3 {
|
||||
namespace yaze::zelda3 {
|
||||
|
||||
const PaletteGroupMetadata* GetPaletteGroupMetadata(const char* group_id) {
|
||||
std::string id_str(group_id);
|
||||
|
||||
|
||||
if (id_str == PaletteGroupName::kOverworldMain) {
|
||||
return &PaletteMetadata::kOverworldMain;
|
||||
} else if (id_str == PaletteGroupName::kOverworldAux) {
|
||||
}
|
||||
if (id_str == PaletteGroupName::kOverworldAux) {
|
||||
return &PaletteMetadata::kOverworldAux;
|
||||
} else if (id_str == PaletteGroupName::kOverworldAnimated) {
|
||||
}
|
||||
if (id_str == PaletteGroupName::kOverworldAnimated) {
|
||||
return &PaletteMetadata::kOverworldAnimated;
|
||||
} else if (id_str == PaletteGroupName::kDungeonMain) {
|
||||
}
|
||||
if (id_str == PaletteGroupName::kDungeonMain) {
|
||||
return &PaletteMetadata::kDungeonMain;
|
||||
} else if (id_str == PaletteGroupName::kGlobalSprites) {
|
||||
}
|
||||
if (id_str == PaletteGroupName::kGlobalSprites) {
|
||||
return &PaletteMetadata::kGlobalSprites;
|
||||
} else if (id_str == PaletteGroupName::kSpritesAux1) {
|
||||
}
|
||||
if (id_str == PaletteGroupName::kSpritesAux1) {
|
||||
return &PaletteMetadata::kSpritesAux1;
|
||||
} else if (id_str == PaletteGroupName::kSpritesAux2) {
|
||||
}
|
||||
if (id_str == PaletteGroupName::kSpritesAux2) {
|
||||
return &PaletteMetadata::kSpritesAux2;
|
||||
} else if (id_str == PaletteGroupName::kSpritesAux3) {
|
||||
}
|
||||
if (id_str == PaletteGroupName::kSpritesAux3) {
|
||||
return &PaletteMetadata::kSpritesAux3;
|
||||
} else if (id_str == PaletteGroupName::kArmor) {
|
||||
}
|
||||
if (id_str == PaletteGroupName::kArmor) {
|
||||
return &PaletteMetadata::kArmor;
|
||||
} else if (id_str == PaletteGroupName::kSwords) {
|
||||
}
|
||||
if (id_str == PaletteGroupName::kSwords) {
|
||||
return &PaletteMetadata::kSwords;
|
||||
} else if (id_str == PaletteGroupName::kShields) {
|
||||
}
|
||||
if (id_str == PaletteGroupName::kShields) {
|
||||
return &PaletteMetadata::kShields;
|
||||
} else if (id_str == PaletteGroupName::kHud) {
|
||||
}
|
||||
if (id_str == PaletteGroupName::kHud) {
|
||||
return &PaletteMetadata::kHud;
|
||||
} else if (id_str == PaletteGroupName::kGrass) {
|
||||
}
|
||||
if (id_str == PaletteGroupName::kGrass) {
|
||||
return &PaletteMetadata::kGrass;
|
||||
} else if (id_str == PaletteGroupName::k3DObject) {
|
||||
}
|
||||
if (id_str == PaletteGroupName::k3DObject) {
|
||||
return &PaletteMetadata::k3DObject;
|
||||
} else if (id_str == PaletteGroupName::kOverworldMiniMap) {
|
||||
}
|
||||
if (id_str == PaletteGroupName::kOverworldMiniMap) {
|
||||
return &PaletteMetadata::kOverworldMiniMap;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<const PaletteGroupMetadata*> GetAllPaletteGroups() {
|
||||
return {
|
||||
// Overworld
|
||||
&PaletteMetadata::kOverworldMain,
|
||||
&PaletteMetadata::kOverworldAux,
|
||||
&PaletteMetadata::kOverworldAnimated,
|
||||
|
||||
// Dungeon
|
||||
&PaletteMetadata::kDungeonMain,
|
||||
|
||||
// Sprites
|
||||
&PaletteMetadata::kGlobalSprites,
|
||||
&PaletteMetadata::kSpritesAux1,
|
||||
&PaletteMetadata::kSpritesAux2,
|
||||
&PaletteMetadata::kSpritesAux3,
|
||||
|
||||
// Equipment
|
||||
&PaletteMetadata::kArmor,
|
||||
&PaletteMetadata::kSwords,
|
||||
&PaletteMetadata::kShields,
|
||||
|
||||
// Interface
|
||||
&PaletteMetadata::kHud,
|
||||
&PaletteMetadata::kOverworldMiniMap,
|
||||
|
||||
// Special
|
||||
&PaletteMetadata::kGrass,
|
||||
&PaletteMetadata::k3DObject
|
||||
};
|
||||
return {// Overworld
|
||||
&PaletteMetadata::kOverworldMain, &PaletteMetadata::kOverworldAux,
|
||||
&PaletteMetadata::kOverworldAnimated,
|
||||
|
||||
// Dungeon
|
||||
&PaletteMetadata::kDungeonMain,
|
||||
|
||||
// Sprites
|
||||
&PaletteMetadata::kGlobalSprites, &PaletteMetadata::kSpritesAux1,
|
||||
&PaletteMetadata::kSpritesAux2, &PaletteMetadata::kSpritesAux3,
|
||||
|
||||
// Equipment
|
||||
&PaletteMetadata::kArmor, &PaletteMetadata::kSwords,
|
||||
&PaletteMetadata::kShields,
|
||||
|
||||
// Interface
|
||||
&PaletteMetadata::kHud, &PaletteMetadata::kOverworldMiniMap,
|
||||
|
||||
// Special
|
||||
&PaletteMetadata::kGrass, &PaletteMetadata::k3DObject};
|
||||
}
|
||||
|
||||
} // namespace zelda3
|
||||
} // namespace yaze
|
||||
|
||||
} // namespace yaze::zelda3
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace yaze {
|
||||
namespace zelda3 {
|
||||
namespace yaze::zelda3 {
|
||||
|
||||
// ============================================================================
|
||||
// Palette Group Names
|
||||
@@ -303,8 +302,7 @@ const PaletteGroupMetadata* GetPaletteGroupMetadata(const char* group_id);
|
||||
// Get all available palette groups
|
||||
std::vector<const PaletteGroupMetadata*> GetAllPaletteGroups();
|
||||
|
||||
} // namespace zelda3
|
||||
} // namespace yaze
|
||||
} // namespace yaze::zelda3
|
||||
|
||||
#endif // YAZE_ZELDA3_PALETTE_CONSTANTS_H
|
||||
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
#include "zelda3/zelda3_labels.h"
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "zelda3/common.h"
|
||||
#include "zelda3/dungeon/room.h"
|
||||
#include "zelda3/sprite/sprite.h"
|
||||
#include "zelda3/sprite/overlord.h"
|
||||
#include "zelda3/sprite/sprite.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace zelda3 {
|
||||
namespace yaze::zelda3 {
|
||||
|
||||
// Room names - reuse existing kRoomNames array
|
||||
const std::vector<std::string>& Zelda3Labels::GetRoomNames() {
|
||||
@@ -55,147 +58,291 @@ const std::vector<std::string>& Zelda3Labels::GetOverlordNames() {
|
||||
// Overworld map names (64 Light World + 64 Dark World + 32 Special Areas)
|
||||
const std::vector<std::string>& Zelda3Labels::GetOverworldMapNames() {
|
||||
static const std::vector<std::string> map_names = {
|
||||
// Light World (0x00-0x3F)
|
||||
"Lost Woods", "Master Sword Pedestal", "Castle Courtyard", "Link's House",
|
||||
"Eastern Palace", "Desert Palace", "Hyrule Castle", "Witch's Hut",
|
||||
"Kakariko Village", "Death Mountain", "Tower of Hera", "Spectacle Rock",
|
||||
"Graveyard", "Sanctuary", "Lake Hylia", "Desert of Mystery",
|
||||
"Eastern Ruins", "Zora's Domain", "Catfish", "Dam",
|
||||
"Potion Shop", "Kakariko Well", "Blacksmith", "Sick Kid",
|
||||
"Library", "Mushroom", "Magic Bat", "Fairy Fountain",
|
||||
"Fortune Teller", "Lake Shop", "Bomb Shop", "Cave 45",
|
||||
"Checkerboard Cave", "Mini Moldorm Cave", "Ice Rod Cave", "Bonk Rocks",
|
||||
"Bottle Merchant", "Sahasrahla's Hut", "Chicken House", "Aginah's Cave",
|
||||
"Dam Exterior", "Mimic Cave Exterior", "Waterfall Fairy", "Pyramid",
|
||||
"Fat Fairy", "Spike Cave", "Hookshot Cave", "Graveyard Ledge",
|
||||
"Dark Lumberjacks", "Bumper Cave", "Skull Woods 1", "Skull Woods 2",
|
||||
"Skull Woods 3", "Skull Woods 4", "Skull Woods 5", "Skull Woods 6",
|
||||
"Skull Woods 7", "Skull Woods 8", "Ice Palace Exterior", "Misery Mire Exterior",
|
||||
"Palace of Darkness Exterior", "Swamp Palace Exterior", "Turtle Rock Exterior", "Thieves' Town Exterior",
|
||||
|
||||
// Dark World (0x40-0x7F)
|
||||
"Dark Woods", "Dark Chapel", "Dark Castle", "Dark Shields",
|
||||
"Dark Palace", "Dark Desert", "Dark Castle Gate", "Dark Witch",
|
||||
"Dark Village", "Dark Mountain", "Dark Tower", "Dark Rocks",
|
||||
"Dark Graveyard", "Dark Sanctuary", "Dark Lake", "Dark Desert South",
|
||||
"Dark Eastern", "Dark Zora", "Dark Catfish", "Dark Dam",
|
||||
"Dark Shop", "Dark Well", "Dark Blacksmith", "Dark Sick Kid",
|
||||
"Dark Library", "Dark Mushroom", "Dark Bat", "Dark Fountain",
|
||||
"Dark Fortune", "Dark Lake Shop", "Dark Bomb Shop", "Dark Cave 45",
|
||||
"Dark Checker", "Dark Mini Moldorm", "Dark Ice Rod", "Dark Bonk",
|
||||
"Dark Bottle", "Dark Sahasrahla", "Dark Chicken", "Dark Aginah",
|
||||
"Dark Dam Exit", "Dark Mimic Exit", "Dark Waterfall", "Pyramid Top",
|
||||
"Dark Fat Fairy", "Dark Spike Cave", "Dark Hookshot", "Dark Graveyard Ledge",
|
||||
"Lumberjack House", "Dark Bumper", "Skull Woods A", "Skull Woods B",
|
||||
"Skull Woods C", "Skull Woods D", "Skull Woods E", "Skull Woods F",
|
||||
"Skull Woods G", "Skull Woods H", "Ice Palace Entry", "Misery Mire Entry",
|
||||
"Palace of Darkness Entry", "Swamp Palace Entry", "Turtle Rock Entry", "Thieves' Town Entry",
|
||||
|
||||
// Special Areas (0x80-0x9F)
|
||||
"Special Area 1", "Special Area 2", "Special Area 3", "Special Area 4",
|
||||
"Special Area 5", "Special Area 6", "Special Area 7", "Special Area 8",
|
||||
"Special Area 9", "Special Area 10", "Special Area 11", "Special Area 12",
|
||||
"Special Area 13", "Special Area 14", "Special Area 15", "Special Area 16",
|
||||
"Special Area 17", "Special Area 18", "Special Area 19", "Special Area 20",
|
||||
"Special Area 21", "Special Area 22", "Special Area 23", "Special Area 24",
|
||||
"Special Area 25", "Special Area 26", "Special Area 27", "Special Area 28",
|
||||
"Special Area 29", "Special Area 30", "Special Area 31", "Special Area 32"
|
||||
};
|
||||
// Light World (0x00-0x3F)
|
||||
"Lost Woods", "Master Sword Pedestal", "Castle Courtyard", "Link's House",
|
||||
"Eastern Palace", "Desert Palace", "Hyrule Castle", "Witch's Hut",
|
||||
"Kakariko Village", "Death Mountain", "Tower of Hera", "Spectacle Rock",
|
||||
"Graveyard", "Sanctuary", "Lake Hylia", "Desert of Mystery",
|
||||
"Eastern Ruins", "Zora's Domain", "Catfish", "Dam", "Potion Shop",
|
||||
"Kakariko Well", "Blacksmith", "Sick Kid", "Library", "Mushroom",
|
||||
"Magic Bat", "Fairy Fountain", "Fortune Teller", "Lake Shop", "Bomb Shop",
|
||||
"Cave 45", "Checkerboard Cave", "Mini Moldorm Cave", "Ice Rod Cave",
|
||||
"Bonk Rocks", "Bottle Merchant", "Sahasrahla's Hut", "Chicken House",
|
||||
"Aginah's Cave", "Dam Exterior", "Mimic Cave Exterior", "Waterfall Fairy",
|
||||
"Pyramid", "Fat Fairy", "Spike Cave", "Hookshot Cave", "Graveyard Ledge",
|
||||
"Dark Lumberjacks", "Bumper Cave", "Skull Woods 1", "Skull Woods 2",
|
||||
"Skull Woods 3", "Skull Woods 4", "Skull Woods 5", "Skull Woods 6",
|
||||
"Skull Woods 7", "Skull Woods 8", "Ice Palace Exterior",
|
||||
"Misery Mire Exterior", "Palace of Darkness Exterior",
|
||||
"Swamp Palace Exterior", "Turtle Rock Exterior", "Thieves' Town Exterior",
|
||||
|
||||
// Dark World (0x40-0x7F)
|
||||
"Dark Woods", "Dark Chapel", "Dark Castle", "Dark Shields", "Dark Palace",
|
||||
"Dark Desert", "Dark Castle Gate", "Dark Witch", "Dark Village",
|
||||
"Dark Mountain", "Dark Tower", "Dark Rocks", "Dark Graveyard",
|
||||
"Dark Sanctuary", "Dark Lake", "Dark Desert South", "Dark Eastern",
|
||||
"Dark Zora", "Dark Catfish", "Dark Dam", "Dark Shop", "Dark Well",
|
||||
"Dark Blacksmith", "Dark Sick Kid", "Dark Library", "Dark Mushroom",
|
||||
"Dark Bat", "Dark Fountain", "Dark Fortune", "Dark Lake Shop",
|
||||
"Dark Bomb Shop", "Dark Cave 45", "Dark Checker", "Dark Mini Moldorm",
|
||||
"Dark Ice Rod", "Dark Bonk", "Dark Bottle", "Dark Sahasrahla",
|
||||
"Dark Chicken", "Dark Aginah", "Dark Dam Exit", "Dark Mimic Exit",
|
||||
"Dark Waterfall", "Pyramid Top", "Dark Fat Fairy", "Dark Spike Cave",
|
||||
"Dark Hookshot", "Dark Graveyard Ledge", "Lumberjack House",
|
||||
"Dark Bumper", "Skull Woods A", "Skull Woods B", "Skull Woods C",
|
||||
"Skull Woods D", "Skull Woods E", "Skull Woods F", "Skull Woods G",
|
||||
"Skull Woods H", "Ice Palace Entry", "Misery Mire Entry",
|
||||
"Palace of Darkness Entry", "Swamp Palace Entry", "Turtle Rock Entry",
|
||||
"Thieves' Town Entry",
|
||||
|
||||
// Special Areas (0x80-0x9F)
|
||||
"Special Area 1", "Special Area 2", "Special Area 3", "Special Area 4",
|
||||
"Special Area 5", "Special Area 6", "Special Area 7", "Special Area 8",
|
||||
"Special Area 9", "Special Area 10", "Special Area 11", "Special Area 12",
|
||||
"Special Area 13", "Special Area 14", "Special Area 15",
|
||||
"Special Area 16", "Special Area 17", "Special Area 18",
|
||||
"Special Area 19", "Special Area 20", "Special Area 21",
|
||||
"Special Area 22", "Special Area 23", "Special Area 24",
|
||||
"Special Area 25", "Special Area 26", "Special Area 27",
|
||||
"Special Area 28", "Special Area 29", "Special Area 30",
|
||||
"Special Area 31", "Special Area 32"};
|
||||
return map_names;
|
||||
}
|
||||
|
||||
// Item names (complete item list)
|
||||
const std::vector<std::string>& Zelda3Labels::GetItemNames() {
|
||||
static const std::vector<std::string> item_names = {
|
||||
"None", "Fighter Sword", "Master Sword", "Tempered Sword",
|
||||
"Golden Sword", "Fighter Shield", "Fire Shield", "Mirror Shield",
|
||||
"Fire Rod", "Ice Rod", "Hammer", "Hookshot",
|
||||
"Bow", "Boomerang", "Powder", "Bee Badge",
|
||||
"Bombos Medallion", "Ether Medallion", "Quake Medallion", "Lamp",
|
||||
"Shovel", "Flute", "Somaria Cane", "Bottle",
|
||||
"Heart Piece", "Byrna Cane", "Cape", "Mirror",
|
||||
"Power Glove", "Titan Mitt", "Book of Mudora", "Zora Flippers",
|
||||
"Moon Pearl", "Crystal", "Bug Net", "Blue Mail",
|
||||
"Red Mail", "Key", "Compass", "Heart Container",
|
||||
"Bomb", "3 Bombs", "Mushroom", "Red Boomerang",
|
||||
"Red Potion", "Green Potion", "Blue Potion", "Red Potion (Refill)",
|
||||
"Green Potion (Refill)", "Blue Potion (Refill)", "10 Bombs", "Big Key",
|
||||
"Map", "1 Rupee", "5 Rupees", "20 Rupees",
|
||||
"Pendant of Courage", "Pendant of Wisdom", "Pendant of Power", "Bow and Arrows",
|
||||
"Silver Arrows Upgrade", "Bee", "Fairy", "Heart Container (Boss)",
|
||||
"Heart", "1 Arrow", "10 Arrows", "Magic",
|
||||
"Small Magic", "300 Rupees", "20 Rupees (Green)", "100 Rupees",
|
||||
"50 Rupees", "Heart Container (Sanctuary)", "Arrow Refill (5)", "Arrow Refill (10)",
|
||||
"Bomb Refill (1)", "Bomb Refill (4)", "Bomb Refill (8)", "Blue Shield (Refill)",
|
||||
"Magic Upgrade (1/2)", "Magic Upgrade (1/4)", "Programmable Item 1", "Programmable Item 2",
|
||||
"Programmable Item 3", "Silvers", "Rupoor", "Null Item",
|
||||
"Red Clock", "Blue Clock", "Green Clock", "Progressive Sword",
|
||||
"Progressive Shield", "Progressive Armor", "Progressive Lifting Glove", "RNG Item (Single)",
|
||||
"RNG Item (Multi)", "Progressive Bow", "Progressive Bow (Alt)", "Aga Pendant",
|
||||
"Pendant (Green)", "Blue Pendant", "Good Bee", "Tossed Key"
|
||||
};
|
||||
"None",
|
||||
"Fighter Sword",
|
||||
"Master Sword",
|
||||
"Tempered Sword",
|
||||
"Golden Sword",
|
||||
"Fighter Shield",
|
||||
"Fire Shield",
|
||||
"Mirror Shield",
|
||||
"Fire Rod",
|
||||
"Ice Rod",
|
||||
"Hammer",
|
||||
"Hookshot",
|
||||
"Bow",
|
||||
"Boomerang",
|
||||
"Powder",
|
||||
"Bee Badge",
|
||||
"Bombos Medallion",
|
||||
"Ether Medallion",
|
||||
"Quake Medallion",
|
||||
"Lamp",
|
||||
"Shovel",
|
||||
"Flute",
|
||||
"Somaria Cane",
|
||||
"Bottle",
|
||||
"Heart Piece",
|
||||
"Byrna Cane",
|
||||
"Cape",
|
||||
"Mirror",
|
||||
"Power Glove",
|
||||
"Titan Mitt",
|
||||
"Book of Mudora",
|
||||
"Zora Flippers",
|
||||
"Moon Pearl",
|
||||
"Crystal",
|
||||
"Bug Net",
|
||||
"Blue Mail",
|
||||
"Red Mail",
|
||||
"Key",
|
||||
"Compass",
|
||||
"Heart Container",
|
||||
"Bomb",
|
||||
"3 Bombs",
|
||||
"Mushroom",
|
||||
"Red Boomerang",
|
||||
"Red Potion",
|
||||
"Green Potion",
|
||||
"Blue Potion",
|
||||
"Red Potion (Refill)",
|
||||
"Green Potion (Refill)",
|
||||
"Blue Potion (Refill)",
|
||||
"10 Bombs",
|
||||
"Big Key",
|
||||
"Map",
|
||||
"1 Rupee",
|
||||
"5 Rupees",
|
||||
"20 Rupees",
|
||||
"Pendant of Courage",
|
||||
"Pendant of Wisdom",
|
||||
"Pendant of Power",
|
||||
"Bow and Arrows",
|
||||
"Silver Arrows Upgrade",
|
||||
"Bee",
|
||||
"Fairy",
|
||||
"Heart Container (Boss)",
|
||||
"Heart",
|
||||
"1 Arrow",
|
||||
"10 Arrows",
|
||||
"Magic",
|
||||
"Small Magic",
|
||||
"300 Rupees",
|
||||
"20 Rupees (Green)",
|
||||
"100 Rupees",
|
||||
"50 Rupees",
|
||||
"Heart Container (Sanctuary)",
|
||||
"Arrow Refill (5)",
|
||||
"Arrow Refill (10)",
|
||||
"Bomb Refill (1)",
|
||||
"Bomb Refill (4)",
|
||||
"Bomb Refill (8)",
|
||||
"Blue Shield (Refill)",
|
||||
"Magic Upgrade (1/2)",
|
||||
"Magic Upgrade (1/4)",
|
||||
"Programmable Item 1",
|
||||
"Programmable Item 2",
|
||||
"Programmable Item 3",
|
||||
"Silvers",
|
||||
"Rupoor",
|
||||
"Null Item",
|
||||
"Red Clock",
|
||||
"Blue Clock",
|
||||
"Green Clock",
|
||||
"Progressive Sword",
|
||||
"Progressive Shield",
|
||||
"Progressive Armor",
|
||||
"Progressive Lifting Glove",
|
||||
"RNG Item (Single)",
|
||||
"RNG Item (Multi)",
|
||||
"Progressive Bow",
|
||||
"Progressive Bow (Alt)",
|
||||
"Aga Pendant",
|
||||
"Pendant (Green)",
|
||||
"Blue Pendant",
|
||||
"Good Bee",
|
||||
"Tossed Key"};
|
||||
return item_names;
|
||||
}
|
||||
|
||||
// Music track names
|
||||
const std::vector<std::string>& Zelda3Labels::GetMusicTrackNames() {
|
||||
static const std::vector<std::string> music_names = {
|
||||
"Nothing", "Light World", "Beginning", "Rabbit",
|
||||
"Forest", "Intro", "Town", "Warp",
|
||||
"Dark World", "Master Sword", "File Select", "Soldier",
|
||||
"Boss", "Dark World Death Mountain", "Minigame", "Skull Woods",
|
||||
"Indoor", "Cave 1", "Zelda's Rescue", "Crystal",
|
||||
"Shop", "Cave 2", "Game Over", "Boss Victory",
|
||||
"Sanctuary", "Boss Victory (Short)", "Dark World Woods", "Pendant",
|
||||
"Ganon's Message", "Hyrule Castle", "Light World Death Mountain", "Eastern Palace",
|
||||
"Desert Palace", "Agahnim's Theme", "Damp Dungeon", "Ganon Reveals",
|
||||
"Confrontation", "Ganon's Theme", "Triforce", "Credits",
|
||||
"Unused", "Unused", "Unused", "Unused",
|
||||
"Unused", "Unused", "Unused", "Unused"
|
||||
};
|
||||
"Nothing",
|
||||
"Light World",
|
||||
"Beginning",
|
||||
"Rabbit",
|
||||
"Forest",
|
||||
"Intro",
|
||||
"Town",
|
||||
"Warp",
|
||||
"Dark World",
|
||||
"Master Sword",
|
||||
"File Select",
|
||||
"Soldier",
|
||||
"Boss",
|
||||
"Dark World Death Mountain",
|
||||
"Minigame",
|
||||
"Skull Woods",
|
||||
"Indoor",
|
||||
"Cave 1",
|
||||
"Zelda's Rescue",
|
||||
"Crystal",
|
||||
"Shop",
|
||||
"Cave 2",
|
||||
"Game Over",
|
||||
"Boss Victory",
|
||||
"Sanctuary",
|
||||
"Boss Victory (Short)",
|
||||
"Dark World Woods",
|
||||
"Pendant",
|
||||
"Ganon's Message",
|
||||
"Hyrule Castle",
|
||||
"Light World Death Mountain",
|
||||
"Eastern Palace",
|
||||
"Desert Palace",
|
||||
"Agahnim's Theme",
|
||||
"Damp Dungeon",
|
||||
"Ganon Reveals",
|
||||
"Confrontation",
|
||||
"Ganon's Theme",
|
||||
"Triforce",
|
||||
"Credits",
|
||||
"Unused",
|
||||
"Unused",
|
||||
"Unused",
|
||||
"Unused",
|
||||
"Unused",
|
||||
"Unused",
|
||||
"Unused",
|
||||
"Unused"};
|
||||
return music_names;
|
||||
}
|
||||
|
||||
// Graphics sheet names
|
||||
const std::vector<std::string>& Zelda3Labels::GetGraphicsSheetNames() {
|
||||
static const std::vector<std::string> gfx_names = {
|
||||
"Sprite Sheet 0", "Sprite Sheet 1", "Sprite Sheet 2", "Sprite Sheet 3",
|
||||
"Sprite Sheet 4", "Sprite Sheet 5", "Sprite Sheet 6", "Sprite Sheet 7",
|
||||
"Sprite Sheet 8", "Sprite Sheet 9", "Sprite Sheet A", "Sprite Sheet B",
|
||||
"Sprite Sheet C", "Sprite Sheet D", "Sprite Sheet E", "Sprite Sheet F",
|
||||
"Link's Sprites", "Sword Sprites", "Shield Sprites", "Common Sprites",
|
||||
"Boss Sprites", "NPC Sprites", "Enemy Sprites 1", "Enemy Sprites 2",
|
||||
"Item Sprites", "Dungeon Objects", "Overworld Objects", "Interface",
|
||||
"Font", "Credits", "Unused", "Unused"
|
||||
};
|
||||
static const std::vector<std::string> gfx_names = {"Sprite Sheet 0",
|
||||
"Sprite Sheet 1",
|
||||
"Sprite Sheet 2",
|
||||
"Sprite Sheet 3",
|
||||
"Sprite Sheet 4",
|
||||
"Sprite Sheet 5",
|
||||
"Sprite Sheet 6",
|
||||
"Sprite Sheet 7",
|
||||
"Sprite Sheet 8",
|
||||
"Sprite Sheet 9",
|
||||
"Sprite Sheet A",
|
||||
"Sprite Sheet B",
|
||||
"Sprite Sheet C",
|
||||
"Sprite Sheet D",
|
||||
"Sprite Sheet E",
|
||||
"Sprite Sheet F",
|
||||
"Link's Sprites",
|
||||
"Sword Sprites",
|
||||
"Shield Sprites",
|
||||
"Common Sprites",
|
||||
"Boss Sprites",
|
||||
"NPC Sprites",
|
||||
"Enemy Sprites 1",
|
||||
"Enemy Sprites 2",
|
||||
"Item Sprites",
|
||||
"Dungeon Objects",
|
||||
"Overworld Objects",
|
||||
"Interface",
|
||||
"Font",
|
||||
"Credits",
|
||||
"Unused",
|
||||
"Unused"};
|
||||
return gfx_names;
|
||||
}
|
||||
|
||||
// Room object names - these are large, so we'll delegate to a helper
|
||||
namespace {
|
||||
std::vector<std::string> ConvertArrayToVector(const char** array, size_t size) {
|
||||
std::vector<std::string> result;
|
||||
result.reserve(size);
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
result.emplace_back(array[i]);
|
||||
}
|
||||
return result;
|
||||
std::vector<std::string> ConvertArrayToVector(const char** array, size_t size) {
|
||||
std::vector<std::string> result;
|
||||
result.reserve(size);
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
result.emplace_back(array[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
const std::vector<std::string>& Zelda3Labels::GetType1RoomObjectNames() {
|
||||
static const std::vector<std::string> names = []() {
|
||||
std::vector<std::string> result;
|
||||
// Note: Type1RoomObjectNames is constexpr, we need to count its size
|
||||
// For now, we'll add known objects. In full implementation,
|
||||
// For now, we'll add known objects. In full implementation,
|
||||
// we'd import from room_object.h
|
||||
result = {
|
||||
"Ceiling ↔", "Wall (top, north) ↔", "Wall (top, south) ↔",
|
||||
"Wall (bottom, north) ↔", "Wall (bottom, south) ↔", "Wall columns (north) ↔",
|
||||
"Wall columns (south) ↔", "Deep wall (north) ↔", "Deep wall (south) ↔",
|
||||
"Diagonal wall A ◤ (top) ↔", "Diagonal wall A ◣ (top) ↔",
|
||||
"Diagonal wall A ◥ (top) ↔", "Diagonal wall A ◢ (top) ↔",
|
||||
// ... Add all Type1 objects here
|
||||
"Ceiling ↔",
|
||||
"Wall (top, north) ↔",
|
||||
"Wall (top, south) ↔",
|
||||
"Wall (bottom, north) ↔",
|
||||
"Wall (bottom, south) ↔",
|
||||
"Wall columns (north) ↔",
|
||||
"Wall columns (south) ↔",
|
||||
"Deep wall (north) ↔",
|
||||
"Deep wall (south) ↔",
|
||||
"Diagonal wall A ◤ (top) ↔",
|
||||
"Diagonal wall A ◣ (top) ↔",
|
||||
"Diagonal wall A ◥ (top) ↔",
|
||||
"Diagonal wall A ◢ (top) ↔",
|
||||
// ... Add all Type1 objects here
|
||||
};
|
||||
return result;
|
||||
}();
|
||||
@@ -236,137 +383,172 @@ const std::vector<std::string>& Zelda3Labels::GetRoomEffectNames() {
|
||||
// Room tag names
|
||||
const std::vector<std::string>& Zelda3Labels::GetRoomTagNames() {
|
||||
static const std::vector<std::string> tag_names = {
|
||||
"No Tag", "NW", "NE", "SW", "SE",
|
||||
"West", "East", "North", "South",
|
||||
"Entrance", "Treasure", "Boss", "Dark"
|
||||
};
|
||||
"No Tag", "NW", "NE", "SW", "SE", "West", "East",
|
||||
"North", "South", "Entrance", "Treasure", "Boss", "Dark"};
|
||||
return tag_names;
|
||||
}
|
||||
|
||||
// Tile type names
|
||||
const std::vector<std::string>& Zelda3Labels::GetTileTypeNames() {
|
||||
static const std::vector<std::string> tile_names = {
|
||||
"Nothing (standard floor)", "Nothing (unused?)", "Collision",
|
||||
"Collision (unknown types)", "Collision", "Collision (unused?)",
|
||||
"Collision", "Collision", "Deep water", "Shallow water",
|
||||
"Unknown (near water/pit edges)", "Collision (water/pit edges)",
|
||||
"Overlay mask", "Spike floor", "GT ice", "Ice palace ice",
|
||||
"Slope ◤", "Slope ◥", "Slope ◣", "Slope ◢",
|
||||
"Nothing (unused?)", "Nothing (unused?)", "Nothing (unused?)",
|
||||
"Slope ◤", "Slope ◥", "Slope ◣", "Slope ◢",
|
||||
"Layer swap", "Pit", "Manual stairs", "Pot switch", "Pressure switch",
|
||||
"Blocks switch (chest, PoD, walls)", "Layer toggle", "Layer 2 overlay",
|
||||
"North single-layer auto stairs", "North layer swap auto stairs",
|
||||
"South single-layer auto stairs", "South layer swap auto stairs",
|
||||
"North/south layer swap auto stairs", "North/south single-layer auto stairs",
|
||||
"West single-layer auto stairs", "West layer swap auto stairs",
|
||||
"East single-layer auto stairs", "East layer swap auto stairs",
|
||||
"East/west layer swap auto stairs", "East/west single-layer auto stairs",
|
||||
"Nothing (stairs edge)", "Straight inter-room stairs south/up",
|
||||
"Straight inter-room stairs north/down", "Straight inter-room stairs south/down 2",
|
||||
"Straight inter-room stairs north/up 2", "Star tile (inactive on GBA)",
|
||||
"Collision (near stairs)", "Warp tile", "Square corners ⌜⌝⌞⌟",
|
||||
"Thick corner ⌜", "Thick corner ⌝", "Thick corner ⌞", "Thick corner ⌟",
|
||||
"Roof/grass tiles?", "Spike floor"
|
||||
};
|
||||
"Nothing (standard floor)",
|
||||
"Nothing (unused?)",
|
||||
"Collision",
|
||||
"Collision (unknown types)",
|
||||
"Collision",
|
||||
"Collision (unused?)",
|
||||
"Collision",
|
||||
"Collision",
|
||||
"Deep water",
|
||||
"Shallow water",
|
||||
"Unknown (near water/pit edges)",
|
||||
"Collision (water/pit edges)",
|
||||
"Overlay mask",
|
||||
"Spike floor",
|
||||
"GT ice",
|
||||
"Ice palace ice",
|
||||
"Slope ◤",
|
||||
"Slope ◥",
|
||||
"Slope ◣",
|
||||
"Slope ◢",
|
||||
"Nothing (unused?)",
|
||||
"Nothing (unused?)",
|
||||
"Nothing (unused?)",
|
||||
"Slope ◤",
|
||||
"Slope ◥",
|
||||
"Slope ◣",
|
||||
"Slope ◢",
|
||||
"Layer swap",
|
||||
"Pit",
|
||||
"Manual stairs",
|
||||
"Pot switch",
|
||||
"Pressure switch",
|
||||
"Blocks switch (chest, PoD, walls)",
|
||||
"Layer toggle",
|
||||
"Layer 2 overlay",
|
||||
"North single-layer auto stairs",
|
||||
"North layer swap auto stairs",
|
||||
"South single-layer auto stairs",
|
||||
"South layer swap auto stairs",
|
||||
"North/south layer swap auto stairs",
|
||||
"North/south single-layer auto stairs",
|
||||
"West single-layer auto stairs",
|
||||
"West layer swap auto stairs",
|
||||
"East single-layer auto stairs",
|
||||
"East layer swap auto stairs",
|
||||
"East/west layer swap auto stairs",
|
||||
"East/west single-layer auto stairs",
|
||||
"Nothing (stairs edge)",
|
||||
"Straight inter-room stairs south/up",
|
||||
"Straight inter-room stairs north/down",
|
||||
"Straight inter-room stairs south/down 2",
|
||||
"Straight inter-room stairs north/up 2",
|
||||
"Star tile (inactive on GBA)",
|
||||
"Collision (near stairs)",
|
||||
"Warp tile",
|
||||
"Square corners ⌜⌝⌞⌟",
|
||||
"Thick corner ⌜",
|
||||
"Thick corner ⌝",
|
||||
"Thick corner ⌞",
|
||||
"Thick corner ⌟",
|
||||
"Roof/grass tiles?",
|
||||
"Spike floor"};
|
||||
return tile_names;
|
||||
}
|
||||
|
||||
// Convert all labels to structured map for project embedding
|
||||
std::unordered_map<std::string, std::unordered_map<std::string, std::string>>
|
||||
std::unordered_map<std::string, std::unordered_map<std::string, std::string>>
|
||||
Zelda3Labels::ToResourceLabels() {
|
||||
std::unordered_map<std::string, std::unordered_map<std::string, std::string>> labels;
|
||||
|
||||
std::unordered_map<std::string, std::unordered_map<std::string, std::string>>
|
||||
labels;
|
||||
|
||||
// Rooms
|
||||
const auto& rooms = GetRoomNames();
|
||||
for (size_t i = 0; i < rooms.size(); ++i) {
|
||||
labels["room"][std::to_string(i)] = rooms[i];
|
||||
}
|
||||
|
||||
|
||||
// Entrances
|
||||
const auto& entrances = GetEntranceNames();
|
||||
for (size_t i = 0; i < entrances.size(); ++i) {
|
||||
labels["entrance"][std::to_string(i)] = entrances[i];
|
||||
}
|
||||
|
||||
|
||||
// Sprites
|
||||
const auto& sprites = GetSpriteNames();
|
||||
for (size_t i = 0; i < sprites.size(); ++i) {
|
||||
labels["sprite"][std::to_string(i)] = sprites[i];
|
||||
}
|
||||
|
||||
|
||||
// Overlords
|
||||
const auto& overlords = GetOverlordNames();
|
||||
for (size_t i = 0; i < overlords.size(); ++i) {
|
||||
labels["overlord"][std::to_string(i)] = overlords[i];
|
||||
}
|
||||
|
||||
|
||||
// Overworld maps
|
||||
const auto& maps = GetOverworldMapNames();
|
||||
for (size_t i = 0; i < maps.size(); ++i) {
|
||||
labels["overworld_map"][std::to_string(i)] = maps[i];
|
||||
}
|
||||
|
||||
|
||||
// Items
|
||||
const auto& items = GetItemNames();
|
||||
for (size_t i = 0; i < items.size(); ++i) {
|
||||
labels["item"][std::to_string(i)] = items[i];
|
||||
}
|
||||
|
||||
|
||||
// Music tracks
|
||||
const auto& music = GetMusicTrackNames();
|
||||
for (size_t i = 0; i < music.size(); ++i) {
|
||||
labels["music"][std::to_string(i)] = music[i];
|
||||
}
|
||||
|
||||
|
||||
// Graphics sheets
|
||||
const auto& gfx = GetGraphicsSheetNames();
|
||||
for (size_t i = 0; i < gfx.size(); ++i) {
|
||||
labels["graphics"][std::to_string(i)] = gfx[i];
|
||||
}
|
||||
|
||||
|
||||
// Room effects
|
||||
const auto& effects = GetRoomEffectNames();
|
||||
for (size_t i = 0; i < effects.size(); ++i) {
|
||||
labels["room_effect"][std::to_string(i)] = effects[i];
|
||||
}
|
||||
|
||||
|
||||
// Room tags
|
||||
const auto& tags = GetRoomTagNames();
|
||||
for (size_t i = 0; i < tags.size(); ++i) {
|
||||
labels["room_tag"][std::to_string(i)] = tags[i];
|
||||
}
|
||||
|
||||
|
||||
// Tile types
|
||||
const auto& tiles = GetTileTypeNames();
|
||||
for (size_t i = 0; i < tiles.size(); ++i) {
|
||||
labels["tile_type"][std::to_string(i)] = tiles[i];
|
||||
}
|
||||
|
||||
|
||||
return labels;
|
||||
}
|
||||
|
||||
// Get a label by resource type and ID
|
||||
std::string Zelda3Labels::GetLabel(const std::string& resource_type, int id,
|
||||
const std::string& default_value) {
|
||||
const std::string& default_value) {
|
||||
static auto labels = ToResourceLabels();
|
||||
|
||||
|
||||
auto type_it = labels.find(resource_type);
|
||||
if (type_it == labels.end()) {
|
||||
return default_value.empty()
|
||||
? resource_type + "_" + std::to_string(id)
|
||||
: default_value;
|
||||
return default_value.empty() ? resource_type + "_" + std::to_string(id)
|
||||
: default_value;
|
||||
}
|
||||
|
||||
|
||||
auto label_it = type_it->second.find(std::to_string(id));
|
||||
if (label_it == type_it->second.end()) {
|
||||
return default_value.empty()
|
||||
? resource_type + "_" + std::to_string(id)
|
||||
: default_value;
|
||||
return default_value.empty() ? resource_type + "_" + std::to_string(id)
|
||||
: default_value;
|
||||
}
|
||||
|
||||
|
||||
return label_it->second;
|
||||
}
|
||||
|
||||
} // namespace zelda3
|
||||
} // namespace yaze
|
||||
} // namespace yaze::zelda3
|
||||
|
||||
@@ -48,10 +48,10 @@ if(YAZE_BUILD_TESTS)
|
||||
if(MSVC)
|
||||
target_link_options(${suite_name} PRIVATE /STACK:16777216)
|
||||
# Force whole-archive linking for protobuf to ensure all symbols are included
|
||||
if(YAZE_WITH_GRPC AND YAZE_PROTOBUF_TARGET)
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
target_link_options(${suite_name} PRIVATE /WHOLEARCHIVE:$<TARGET_FILE:${YAZE_PROTOBUF_TARGET}>)
|
||||
endif()
|
||||
if(YAZE_WITH_GRPC AND YAZE_PROTOBUF_TARGETS)
|
||||
foreach(_yaze_proto_target IN LISTS YAZE_PROTOBUF_TARGETS)
|
||||
target_link_options(${suite_name} PRIVATE /WHOLEARCHIVE:$<TARGET_FILE:${_yaze_proto_target}>)
|
||||
endforeach()
|
||||
endif()
|
||||
else()
|
||||
target_link_options(${suite_name} PRIVATE -Wl,--stack,16777216)
|
||||
|
||||
Reference in New Issue
Block a user