2#include <absl/status/status.h>
3#include <absl/status/statusor.h>
15#include "absl/strings/str_format.h"
26#include "imgui/imgui.h"
36 : panel_manager_(panel_manager),
37 toast_manager_(toast_manager),
38 user_settings_(user_settings) {}
55 observer->OnSessionSwitched(index, session);
62 observer->OnSessionCreated(index, session);
68 observer->OnSessionClosed(index);
75 observer->OnSessionRomLoaded(index, session);
86 sessions_.push_back(std::make_unique<RomSession>());
98 LOG_INFO(
"SessionCoordinator",
"Created new session %zu (total: %zu)",
119 sessions_.push_back(std::make_unique<RomSession>());
131 LOG_INFO(
"SessionCoordinator",
"Duplicated session %zu (total: %zu)",
174 LOG_INFO(
"SessionCoordinator",
"Closed session %zu (total: %zu)", index,
196 if (old_index != index) {
222 return session ? &session->rom :
nullptr;
227 return session ? &session->game_data :
nullptr;
232 return session ? &session->editors :
nullptr;
251 const std::string& filepath)
const {
252 if (filepath.empty())
256 if (session->filepath == filepath) {
270 ImGui::SetNextWindowSize(ImVec2(400, 300), ImGuiCond_FirstUseEver);
271 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(),
272 ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
282 for (
size_t i = 0; i <
sessions_.size(); ++i) {
285 ImGui::PushID(
static_cast<int>(i));
293 if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
294 ImGui::OpenPopup(
"SessionContextMenu");
297 if (ImGui::BeginPopup(
"SessionContextMenu")) {
308 if (ImGui::Button(absl::StrFormat(
"%s New Session",
ICON_MD_ADD).c_str())) {
320 ImGui::Button(absl::StrFormat(
"%s Close",
ICON_MD_CLOSE).c_str())) {
334 ImGui::SetNextWindowSize(ImVec2(600, 400), ImGuiCond_FirstUseEver);
335 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(),
336 ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
354 if (ImGui::BeginTable(
"SessionTable", 4,
355 ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg |
356 ImGuiTableFlags_Resizable)) {
357 ImGui::TableSetupColumn(
"Session", ImGuiTableColumnFlags_WidthStretch,
359 ImGui::TableSetupColumn(
"ROM File", ImGuiTableColumnFlags_WidthStretch,
361 ImGui::TableSetupColumn(
"Status", ImGuiTableColumnFlags_WidthStretch, 0.2f);
362 ImGui::TableSetupColumn(
"Actions", ImGuiTableColumnFlags_WidthFixed,
364 ImGui::TableHeadersRow();
366 for (
size_t i = 0; i <
sessions_.size(); ++i) {
370 ImGui::PushID(
static_cast<int>(i));
372 ImGui::TableNextRow();
375 ImGui::TableNextColumn();
377 ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f),
"%s %s",
386 ImGui::TableNextColumn();
387 if (session->rom.is_loaded()) {
388 ImGui::Text(
"%s", session->filepath.c_str());
390 ImGui::TextDisabled(
"(No ROM loaded)");
394 ImGui::TableNextColumn();
395 if (session->rom.is_loaded()) {
396 ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f),
"Loaded");
398 ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f),
"Empty");
402 ImGui::TableNextColumn();
403 if (!is_active && ImGui::SmallButton(
"Switch")) {
425 ImGui::SetNextWindowSize(ImVec2(300, 150), ImGuiCond_Always);
426 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(),
427 ImGuiCond_Always, ImVec2(0.5f, 0.5f));
440 if (ImGui::Button(
"OK")) {
447 if (ImGui::Button(
"Cancel")) {
459 if (ImGui::BeginTabBar(
"SessionTabs")) {
460 for (
size_t i = 0; i <
sessions_.size(); ++i) {
465 if (session->rom.is_loaded()) {
470 if (ImGui::BeginTabItem(tab_name.c_str())) {
478 if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
479 ImGui::OpenPopup(absl::StrFormat(
"SessionTabContext_%zu", i).c_str());
482 if (ImGui::BeginPopup(
483 absl::StrFormat(
"SessionTabContext_%zu", i).c_str())) {
499 ImGui::PushStyleColor(ImGuiCol_Text, accent_color);
501 ImGui::PopStyleColor();
503 if (ImGui::IsItemHovered()) {
504 ImGui::SetTooltip(
"Active Session: %s\nClick to open session switcher",
508 if (ImGui::IsItemClicked()) {
515 return "Invalid Session";
520 if (!session->custom_name.empty()) {
521 return session->custom_name;
524 if (session->rom.is_loaded()) {
525 return absl::StrFormat(
526 "Session %zu (%s)", index,
527 std::filesystem::path(session->filepath).stem().string());
530 return absl::StrFormat(
"Session %zu (Empty)", index);
538 const std::string& new_name) {
542 sessions_[index]->custom_name = new_name;
543 LOG_INFO(
"SessionCoordinator",
"Renamed session %zu to '%s'", index,
548 const std::string& editor_name,
size_t session_index)
const {
559 const auto& session =
sessions_[session_index];
560 std::string session_name =
561 session->custom_name.empty() ? session->rom.title() : session->custom_name;
564 if (session_name.length() > 20) {
565 session_name = session_name.substr(0, 17) +
"...";
568 return absl::StrFormat(
"%s - %s##session_%zu", editor_name, session_name,
615 for (
size_t session_idx = 0; session_idx <
sessions_.size(); ++session_idx) {
617 if (!session->rom.is_loaded())
623 for (
auto editor : session->editors.active_editors_) {
624 if (*editor->active()) {
627 if (overworld_editor.jump_to_tab() != -1) {
629 session->editors.GetDungeonEditor()->add_room(
630 overworld_editor.jump_to_tab());
638 if (is_card_based_editor) {
645 absl::Status status = editor->Update();
649 std::string editor_name =
kEditorNames[
static_cast<int>(editor->type())];
651 absl::StrFormat(
"%s Error: %s", editor_name, status.message()),
657 std::string window_title =
661 ImGui::SetNextWindowSize(ImGui::GetMainViewport()->WorkSize,
662 ImGuiCond_FirstUseEver);
663 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->WorkPos,
664 ImGuiCond_FirstUseEver);
666 if (ImGui::Begin(window_title.c_str(), editor->active(),
667 ImGuiWindowFlags_None)) {
674 absl::Status status = editor->Update();
678 std::string editor_name =
kEditorNames[
static_cast<int>(editor->type())];
709 if (session->rom.is_loaded()) {
721 size_t session_index) {
722 if (filename.empty()) {
723 return absl::InvalidArgumentError(
"Invalid parameters");
726 size_t target_index =
729 return absl::InvalidArgumentError(
"Invalid session index");
733 LOG_INFO(
"SessionCoordinator",
"LoadRomIntoSession: %s -> session %zu",
734 filename.c_str(), target_index);
736 return absl::OkStatus();
740 const std::string& filename) {
742 return absl::FailedPreconditionError(
"No active session");
746 LOG_INFO(
"SessionCoordinator",
"SaveActiveSession: session %zu",
749 return absl::OkStatus();
753 const std::string& filename) {
755 return absl::InvalidArgumentError(
"Invalid parameters");
759 LOG_INFO(
"SessionCoordinator",
"SaveSessionAs: session %zu -> %s",
760 session_index, filename.c_str());
762 return absl::OkStatus();
766 Rom&& rom,
const std::string& filepath) {
767 size_t new_session_id =
sessions_.size();
770 session->filepath = filepath;
779 return session.get();
784 size_t loaded_count = 0;
786 if (session->rom.is_loaded()) {
791 if (loaded_count > 0) {
793 if (!(*it)->rom.is_loaded() &&
sessions_.size() > 1) {
802 LOG_INFO(
"SessionCoordinator",
"Cleaned up closed sessions (remaining: %zu)",
812 for (
size_t i = 0; i <
sessions_.size(); ++i) {
821 LOG_INFO(
"SessionCoordinator",
"Cleared all sessions");
862 throw std::out_of_range(
863 absl::StrFormat(
"Invalid session index: %zu", index));
868 const std::string& base_name)
const {
872 std::string name = base_name;
878 if (session->custom_name == name) {
887 name = absl::StrFormat(
"%s %d", base_name, counter++);
896 absl::StrFormat(
"Maximum %zu sessions allowed",
kMaxSessions),
902 const std::string& operation,
bool success) {
904 std::string message =
905 absl::StrFormat(
"%s %s", operation, success ?
"succeeded" :
"failed");
918 ImGui::PushStyleColor(ImGuiCol_Text, color);
921 if (session->rom.is_loaded()) {
926 if (ImGui::BeginTabItem(tab_name.c_str())) {
933 ImGui::PopStyleColor();
938 absl::StrFormat(
"%s Switch to Session",
ICON_MD_TAB).c_str())) {
942 if (ImGui::MenuItem(absl::StrFormat(
"%s Rename",
ICON_MD_EDIT).c_str())) {
959 absl::StrFormat(
"%s Close Session",
ICON_MD_CLOSE).c_str())) {
971 ImGui::PushStyleColor(ImGuiCol_Text, color);
973 if (session->rom.is_loaded()) {
979 ImGui::PopStyleColor();
984 static const ImVec4 colors[] = {
985 ImVec4(0.0f, 1.0f, 0.0f, 1.0f),
986 ImVec4(0.0f, 0.5f, 1.0f, 1.0f),
987 ImVec4(1.0f, 0.5f, 0.0f, 1.0f),
988 ImVec4(1.0f, 0.0f, 1.0f, 1.0f),
989 ImVec4(1.0f, 1.0f, 0.0f, 1.0f),
990 ImVec4(0.0f, 1.0f, 1.0f, 1.0f),
991 ImVec4(1.0f, 0.0f, 0.0f, 1.0f),
992 ImVec4(0.5f, 0.5f, 0.5f, 1.0f),
995 return colors[index % (
sizeof(colors) /
sizeof(colors[0]))];
1004 if (session->rom.is_loaded()) {
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
void ConfigureSession(RomSession *session)
void SetCurrentEditor(Editor *editor)
static bool IsPanelBasedEditor(EditorType type)
Contains a complete set of editors for a single ROM instance.
Main UI class for editing overworld maps in A Link to the Past.
Central registry for all editor cards with session awareness and dependency injection.
void SetActiveSession(size_t session_id)
void UnregisterSession(size_t session_id)
void HideAllPanelsInSession(size_t session_id)
void ShowAllPanelsInCategory(size_t session_id, const std::string &category)
void HideAllPanelsInCategory(size_t session_id, const std::string &category)
void ShowAllPanelsInSession(size_t session_id)
void NotifySessionCreated(size_t index, RomSession *session)
void SwitchToSession(size_t index)
void * GetActiveSession() const
char session_rename_buffer_[256]
void * GetSession(size_t index) const
void RemoveObserver(SessionObserver *observer)
size_t GetEmptySessionCount() const
std::string GenerateUniqueEditorTitle(const std::string &editor_name, size_t session_index) const
void CleanupClosedSessions()
zelda3::GameData * GetCurrentGameData() const
EditorSet * GetCurrentEditorSet() const
size_t GetActiveSessionIndex() const
void CloseCurrentSession()
EditorManager * editor_manager_
void NotifySessionClosed(size_t index)
RomSession * GetActiveRomSession() const
void ShowAllPanelsInActiveSession()
void CloseSession(size_t index)
size_t GetActiveSessionCount() const
void NotifySessionRomLoaded(size_t index, RomSession *session)
void FocusPreviousSession()
void UpdateSessionCount()
void DrawSessionManager()
absl::StatusOr< RomSession * > CreateSessionFromRom(Rom &&rom, const std::string &filepath)
UserSettings * user_settings_
void DuplicateCurrentSession()
void DrawSessionContextMenu(size_t index)
absl::Status SaveSessionAs(size_t session_index, const std::string &filename)
size_t active_session_index_
absl::Status SaveActiveSession(const std::string &filename="")
void ActivateSession(size_t index)
ImVec4 GetSessionColor(size_t index) const
absl::Status LoadRomIntoSession(const std::string &filename, size_t session_index=SIZE_MAX)
std::string GetSessionDisplayName(size_t index) const
void RenameSession(size_t index, const std::string &new_name)
bool IsSessionModified(size_t index) const
size_t session_to_rename_
SessionCoordinator(PanelManager *panel_manager, ToastManager *toast_manager, UserSettings *user_settings)
size_t GetTotalSessionCount() const
void UpdateActiveSession()
bool HasDuplicateSession(const std::string &filepath) const
static constexpr size_t kMinSessions
void ToggleSessionSwitcher()
void ShowPanelsInCategory(const std::string &category)
ToastManager * toast_manager_
void HidePanelsInCategory(const std::string &category)
void HideAllPanelsInActiveSession()
bool IsSessionClosed(size_t index) const
std::string GetSessionIcon(size_t index) const
bool show_session_manager_
void DrawSessionRenameDialog()
void ShowSessionLimitWarning()
void DrawSessionSwitcher()
std::string GetActiveSessionDisplayName() const
bool show_session_switcher_
Rom * GetCurrentRom() const
void ShowSessionOperationResult(const std::string &operation, bool success)
bool IsValidSessionIndex(size_t index) const
bool IsSessionEmpty(size_t index) const
std::vector< SessionObserver * > observers_
bool HasMultipleSessions() const
void DrawSessionTab(size_t index, bool is_active)
void DrawSessionBadge(size_t index)
void RemoveSession(size_t index)
void SetActiveSessionIndex(size_t index)
bool IsSessionActive(size_t index) const
std::vector< std::unique_ptr< RomSession > > sessions_
void ValidateSessionIndex(size_t index) const
void DrawSessionIndicator()
void AddObserver(SessionObserver *observer)
void NotifySessionSwitched(size_t index, RomSession *session)
bool IsSessionLoaded(size_t index) const
size_t GetLoadedSessionCount() const
std::string GenerateUniqueSessionName(const std::string &base_name) const
PanelManager * panel_manager_
bool show_session_rename_dialog_
static constexpr size_t kMaxSessions
Observer interface for session state changes.
void Show(const std::string &message, ToastType type=ToastType::kInfo, float ttl_seconds=3.0f)
Manages user preferences and settings persistence.
const Theme & GetCurrentTheme() const
static ThemeManager & Get()
#define ICON_MD_CHECK_CIRCLE
#define ICON_MD_RADIO_BUTTON_CHECKED
#define ICON_MD_CONTENT_COPY
#define ICON_MD_RADIO_BUTTON_UNCHECKED
#define ICON_MD_ANALYTICS
#define LOG_INFO(category, format,...)
constexpr std::array< const char *, 14 > kEditorNames
ImVec4 ConvertColorToImVec4(const Color &color)
Represents a single session, containing a ROM and its associated editors.