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) {}
54 observer->OnSessionSwitched(index, session);
61 observer->OnSessionCreated(index, session);
67 observer->OnSessionClosed(index);
74 observer->OnSessionRomLoaded(index, session);
85 sessions_.push_back(std::make_unique<RomSession>());
97 LOG_INFO(
"SessionCoordinator",
"Created new session %zu (total: %zu)",
118 sessions_.push_back(std::make_unique<RomSession>());
130 LOG_INFO(
"SessionCoordinator",
"Duplicated session %zu (total: %zu)",
173 LOG_INFO(
"SessionCoordinator",
"Closed session %zu (total: %zu)", index,
195 if (old_index != index) {
221 return session ? &session->rom :
nullptr;
226 return session ? &session->game_data :
nullptr;
231 return session ? &session->editors :
nullptr;
250 const std::string& filepath)
const {
251 if (filepath.empty())
255 if (session->filepath == filepath) {
269 ImGui::SetNextWindowSize(ImVec2(400, 300), ImGuiCond_FirstUseEver);
270 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(),
271 ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
281 for (
size_t i = 0; i <
sessions_.size(); ++i) {
284 ImGui::PushID(
static_cast<int>(i));
292 if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
293 ImGui::OpenPopup(
"SessionContextMenu");
296 if (ImGui::BeginPopup(
"SessionContextMenu")) {
307 if (ImGui::Button(absl::StrFormat(
"%s New Session",
ICON_MD_ADD).c_str())) {
319 ImGui::Button(absl::StrFormat(
"%s Close",
ICON_MD_CLOSE).c_str())) {
333 ImGui::SetNextWindowSize(ImVec2(600, 400), ImGuiCond_FirstUseEver);
334 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(),
335 ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
353 if (ImGui::BeginTable(
"SessionTable", 4,
354 ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg |
355 ImGuiTableFlags_Resizable)) {
356 ImGui::TableSetupColumn(
"Session", ImGuiTableColumnFlags_WidthStretch,
358 ImGui::TableSetupColumn(
"ROM File", ImGuiTableColumnFlags_WidthStretch,
360 ImGui::TableSetupColumn(
"Status", ImGuiTableColumnFlags_WidthStretch, 0.2f);
361 ImGui::TableSetupColumn(
"Actions", ImGuiTableColumnFlags_WidthFixed,
363 ImGui::TableHeadersRow();
365 for (
size_t i = 0; i <
sessions_.size(); ++i) {
369 ImGui::PushID(
static_cast<int>(i));
371 ImGui::TableNextRow();
374 ImGui::TableNextColumn();
376 ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f),
"%s %s",
385 ImGui::TableNextColumn();
386 if (session->rom.is_loaded()) {
387 ImGui::Text(
"%s", session->filepath.c_str());
389 ImGui::TextDisabled(
"(No ROM loaded)");
393 ImGui::TableNextColumn();
394 if (session->rom.is_loaded()) {
395 ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f),
"Loaded");
397 ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f),
"Empty");
401 ImGui::TableNextColumn();
402 if (!is_active && ImGui::SmallButton(
"Switch")) {
424 ImGui::SetNextWindowSize(ImVec2(300, 150), ImGuiCond_Always);
425 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(),
426 ImGuiCond_Always, ImVec2(0.5f, 0.5f));
439 if (ImGui::Button(
"OK")) {
446 if (ImGui::Button(
"Cancel")) {
458 if (ImGui::BeginTabBar(
"SessionTabs")) {
459 for (
size_t i = 0; i <
sessions_.size(); ++i) {
464 if (session->rom.is_loaded()) {
469 if (ImGui::BeginTabItem(tab_name.c_str())) {
477 if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
478 ImGui::OpenPopup(absl::StrFormat(
"SessionTabContext_%zu", i).c_str());
481 if (ImGui::BeginPopup(
482 absl::StrFormat(
"SessionTabContext_%zu", i).c_str())) {
498 ImGui::PushStyleColor(ImGuiCol_Text, accent_color);
500 ImGui::PopStyleColor();
502 if (ImGui::IsItemHovered()) {
503 ImGui::SetTooltip(
"Active Session: %s\nClick to open session switcher",
507 if (ImGui::IsItemClicked()) {
514 return "Invalid Session";
519 if (!session->custom_name.empty()) {
520 return session->custom_name;
523 if (session->rom.is_loaded()) {
524 return absl::StrFormat(
525 "Session %zu (%s)", index,
526 std::filesystem::path(session->filepath).stem().string());
529 return absl::StrFormat(
"Session %zu (Empty)", index);
537 const std::string& new_name) {
541 sessions_[index]->custom_name = new_name;
542 LOG_INFO(
"SessionCoordinator",
"Renamed session %zu to '%s'", index,
547 const std::string& editor_name,
size_t session_index)
const {
558 const auto& session =
sessions_[session_index];
559 std::string session_name = session->custom_name.empty()
560 ? session->rom.title()
561 : 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());
636 bool is_card_based_editor =
639 if (is_card_based_editor) {
646 absl::Status status = editor->Update();
650 std::string editor_name =
653 absl::StrFormat(
"%s Error: %s", editor_name, status.message()),
660 kEditorNames[
static_cast<int>(editor->type())], session_idx);
663 ImGui::SetNextWindowSize(ImGui::GetMainViewport()->WorkSize,
664 ImGuiCond_FirstUseEver);
665 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->WorkPos,
666 ImGuiCond_FirstUseEver);
668 if (ImGui::Begin(window_title.c_str(), editor->active(),
669 ImGuiWindowFlags_None)) {
676 absl::Status status = editor->Update();
680 std::string editor_name =
712 if (session->rom.is_loaded()) {
724 size_t session_index) {
725 if (filename.empty()) {
726 return absl::InvalidArgumentError(
"Invalid parameters");
729 size_t target_index =
732 return absl::InvalidArgumentError(
"Invalid session index");
736 LOG_INFO(
"SessionCoordinator",
"LoadRomIntoSession: %s -> session %zu",
737 filename.c_str(), target_index);
739 return absl::OkStatus();
743 const std::string& filename) {
745 return absl::FailedPreconditionError(
"No active session");
749 LOG_INFO(
"SessionCoordinator",
"SaveActiveSession: session %zu",
752 return absl::OkStatus();
756 const std::string& filename) {
758 return absl::InvalidArgumentError(
"Invalid parameters");
762 LOG_INFO(
"SessionCoordinator",
"SaveSessionAs: session %zu -> %s",
763 session_index, filename.c_str());
765 return absl::OkStatus();
769 Rom&& rom,
const std::string& filepath) {
770 size_t new_session_id =
sessions_.size();
771 sessions_.push_back(std::make_unique<RomSession>(
774 session->filepath = filepath;
783 return session.get();
788 size_t loaded_count = 0;
790 if (session->rom.is_loaded()) {
795 if (loaded_count > 0) {
797 if (!(*it)->rom.is_loaded() &&
sessions_.size() > 1) {
806 LOG_INFO(
"SessionCoordinator",
"Cleaned up closed sessions (remaining: %zu)",
816 for (
size_t i = 0; i <
sessions_.size(); ++i) {
825 LOG_INFO(
"SessionCoordinator",
"Cleared all sessions");
865 throw std::out_of_range(
866 absl::StrFormat(
"Invalid session index: %zu", index));
871 const std::string& base_name)
const {
875 std::string name = base_name;
881 if (session->custom_name == name) {
890 name = absl::StrFormat(
"%s %d", base_name, counter++);
899 absl::StrFormat(
"Maximum %zu sessions allowed",
kMaxSessions),
905 const std::string& operation,
bool success) {
907 std::string message =
908 absl::StrFormat(
"%s %s", operation, success ?
"succeeded" :
"failed");
921 ImGui::PushStyleColor(ImGuiCol_Text, color);
924 if (session->rom.is_loaded()) {
929 if (ImGui::BeginTabItem(tab_name.c_str())) {
936 ImGui::PopStyleColor();
941 absl::StrFormat(
"%s Switch to Session",
ICON_MD_TAB).c_str())) {
945 if (ImGui::MenuItem(absl::StrFormat(
"%s Rename",
ICON_MD_EDIT).c_str())) {
962 absl::StrFormat(
"%s Close Session",
ICON_MD_CLOSE).c_str())) {
974 ImGui::PushStyleColor(ImGuiCol_Text, color);
976 if (session->rom.is_loaded()) {
982 ImGui::PopStyleColor();
987 static const ImVec4 colors[] = {
988 ImVec4(0.0f, 1.0f, 0.0f, 1.0f),
989 ImVec4(0.0f, 0.5f, 1.0f, 1.0f),
990 ImVec4(1.0f, 0.5f, 0.0f, 1.0f),
991 ImVec4(1.0f, 0.0f, 1.0f, 1.0f),
992 ImVec4(1.0f, 1.0f, 0.0f, 1.0f),
993 ImVec4(0.0f, 1.0f, 1.0f, 1.0f),
994 ImVec4(1.0f, 0.0f, 0.0f, 1.0f),
995 ImVec4(0.5f, 0.5f, 0.5f, 1.0f),
998 return colors[index % (
sizeof(colors) /
sizeof(colors[0]))];
1007 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.