2#include <absl/status/status.h>
3#include <absl/status/statusor.h>
15#include "absl/strings/str_format.h"
31#include "imgui/imgui.h"
41 : window_manager_(window_manager),
42 toast_manager_(toast_manager),
43 user_settings_(user_settings) {}
87 sessions_.push_back(std::make_unique<RomSession>());
99 LOG_INFO(
"SessionCoordinator",
"Created new session %zu (total: %zu)",
120 sessions_.push_back(std::make_unique<RomSession>());
132 LOG_INFO(
"SessionCoordinator",
"Duplicated session %zu (total: %zu)",
175 LOG_INFO(
"SessionCoordinator",
"Closed session %zu (total: %zu)", index,
197 if (old_index != index) {
223 return session ? &session->rom :
nullptr;
228 return session ? &session->game_data :
nullptr;
233 return session ? &session->editors :
nullptr;
252 const std::string& filepath)
const {
253 if (filepath.empty())
257 if (session->filepath == filepath) {
271 ImGui::SetNextWindowSize(ImVec2(400, 300), ImGuiCond_FirstUseEver);
272 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(),
273 ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
283 for (
size_t i = 0; i <
sessions_.size(); ++i) {
286 ImGui::PushID(
static_cast<int>(i));
294 if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
295 ImGui::OpenPopup(
"SessionContextMenu");
298 if (ImGui::BeginPopup(
"SessionContextMenu")) {
309 if (ImGui::Button(absl::StrFormat(
"%s New Session",
ICON_MD_ADD).c_str())) {
321 ImGui::Button(absl::StrFormat(
"%s Close",
ICON_MD_CLOSE).c_str())) {
335 ImGui::SetNextWindowSize(ImVec2(600, 400), ImGuiCond_FirstUseEver);
336 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(),
337 ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
355 if (ImGui::BeginTable(
"SessionTable", 4,
356 ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg |
357 ImGuiTableFlags_Resizable)) {
358 ImGui::TableSetupColumn(
"Session", ImGuiTableColumnFlags_WidthStretch,
360 ImGui::TableSetupColumn(
"ROM File", ImGuiTableColumnFlags_WidthStretch,
362 ImGui::TableSetupColumn(
"Status", ImGuiTableColumnFlags_WidthStretch, 0.2f);
363 ImGui::TableSetupColumn(
"Actions", ImGuiTableColumnFlags_WidthFixed,
365 ImGui::TableHeadersRow();
367 for (
size_t i = 0; i <
sessions_.size(); ++i) {
371 ImGui::PushID(
static_cast<int>(i));
373 ImGui::TableNextRow();
376 ImGui::TableNextColumn();
387 ImGui::TableNextColumn();
388 if (session->rom.is_loaded()) {
389 ImGui::Text(
"%s", session->filepath.c_str());
391 ImGui::TextDisabled(
"(No ROM loaded)");
395 ImGui::TableNextColumn();
396 if (session->rom.is_loaded()) {
403 ImGui::TableNextColumn();
404 if (!is_active && ImGui::SmallButton(
"Switch")) {
426 ImGui::SetNextWindowSize(ImVec2(300, 150), ImGuiCond_Always);
427 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(),
428 ImGuiCond_Always, ImVec2(0.5f, 0.5f));
441 if (ImGui::Button(
"OK")) {
448 if (ImGui::Button(
"Cancel")) {
461 for (
size_t i = 0; i <
sessions_.size(); ++i) {
466 if (session->rom.is_loaded()) {
471 if (ImGui::BeginTabItem(tab_name.c_str())) {
479 if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
480 ImGui::OpenPopup(absl::StrFormat(
"SessionTabContext_%zu", i).c_str());
483 if (ImGui::BeginPopup(
484 absl::StrFormat(
"SessionTabContext_%zu", i).c_str())) {
505 if (ImGui::IsItemHovered()) {
506 ImGui::SetTooltip(
"Active Session: %s\nClick to open session switcher",
510 if (ImGui::IsItemClicked()) {
517 return "Invalid Session";
522 if (!session->custom_name.empty()) {
523 return session->custom_name;
526 if (session->rom.is_loaded()) {
527 return absl::StrFormat(
528 "Session %zu (%s)", index,
529 std::filesystem::path(session->filepath).stem().string());
532 return absl::StrFormat(
"Session %zu (Empty)", index);
540 const std::string& new_name) {
544 sessions_[index]->custom_name = new_name;
545 LOG_INFO(
"SessionCoordinator",
"Renamed session %zu to '%s'", index,
550 const std::string& editor_name,
size_t session_index)
const {
561 const auto& session =
sessions_[session_index];
562 std::string session_name = session->custom_name.empty()
563 ? session->rom.title()
564 : session->custom_name;
567 if (session_name.length() > 20) {
568 session_name = session_name.substr(0, 17) +
"...";
571 return absl::StrFormat(
"%s - %s##session_%zu", editor_name, session_name,
618 for (
size_t session_idx = 0; session_idx <
sessions_.size(); ++session_idx) {
620 const bool rom_loaded = session->rom.is_loaded();
630 for (
auto editor : session->editors.active_editors_) {
631 if (*editor->active()) {
639 if (overworld_editor.jump_to_tab() != -1) {
641 session->editors.GetDungeonEditor()->add_room(
642 overworld_editor.jump_to_tab());
648 bool is_card_based_editor =
651 if (is_card_based_editor) {
658 absl::Status status = editor->Update();
662 std::string editor_name =
665 absl::StrFormat(
"%s Error: %s", editor_name, status.message()),
672 kEditorNames[
static_cast<int>(editor->type())], session_idx);
675 ImGui::SetNextWindowSize(ImGui::GetMainViewport()->WorkSize,
676 ImGuiCond_FirstUseEver);
677 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->WorkPos,
678 ImGuiCond_FirstUseEver);
680 if (ImGui::Begin(window_title.c_str(), editor->active(),
681 ImGuiWindowFlags_None)) {
688 absl::Status status = editor->Update();
692 std::string editor_name =
724 if (session->rom.is_loaded()) {
736 size_t session_index) {
737 if (filename.empty()) {
738 return absl::InvalidArgumentError(
"Invalid parameters");
741 size_t target_index =
744 return absl::InvalidArgumentError(
"Invalid session index");
748 LOG_INFO(
"SessionCoordinator",
"LoadRomIntoSession: %s -> session %zu",
749 filename.c_str(), target_index);
751 return absl::OkStatus();
755 const std::string& filename) {
757 return absl::FailedPreconditionError(
"No active session");
761 LOG_INFO(
"SessionCoordinator",
"SaveActiveSession: session %zu",
764 return absl::OkStatus();
768 const std::string& filename) {
770 return absl::InvalidArgumentError(
"Invalid parameters");
774 LOG_INFO(
"SessionCoordinator",
"SaveSessionAs: session %zu -> %s",
775 session_index, filename.c_str());
777 return absl::OkStatus();
781 Rom&& rom,
const std::string& filepath) {
782 size_t new_session_id =
sessions_.size();
783 sessions_.push_back(std::make_unique<RomSession>(
786 session->filepath = filepath;
795 return session.get();
800 size_t loaded_count = 0;
802 if (session->rom.is_loaded()) {
807 if (loaded_count > 0) {
809 if (!(*it)->rom.is_loaded() &&
sessions_.size() > 1) {
818 LOG_INFO(
"SessionCoordinator",
"Cleaned up closed sessions (remaining: %zu)",
828 for (
size_t i = 0; i <
sessions_.size(); ++i) {
837 LOG_INFO(
"SessionCoordinator",
"Cleared all sessions");
877 throw std::out_of_range(
878 absl::StrFormat(
"Invalid session index: %zu", index));
883 const std::string& base_name)
const {
887 std::string name = base_name;
893 if (session->custom_name == name) {
902 name = absl::StrFormat(
"%s %d", base_name, counter++);
911 absl::StrFormat(
"Maximum %zu sessions allowed",
kMaxSessions),
917 const std::string& operation,
bool success) {
919 std::string message =
920 absl::StrFormat(
"%s %s", operation, success ?
"succeeded" :
"failed");
936 if (session->rom.is_loaded()) {
941 if (ImGui::BeginTabItem(tab_name.c_str())) {
951 absl::StrFormat(
"%s Switch to Session",
ICON_MD_TAB).c_str())) {
955 if (ImGui::MenuItem(absl::StrFormat(
"%s Rename",
ICON_MD_EDIT).c_str())) {
972 absl::StrFormat(
"%s Close Session",
ICON_MD_CLOSE).c_str())) {
986 if (session->rom.is_loaded()) {
995 static const ImVec4 colors[] = {
996 ImVec4(0.0f, 1.0f, 0.0f, 1.0f),
997 ImVec4(0.0f, 0.5f, 1.0f, 1.0f),
998 ImVec4(1.0f, 0.5f, 0.0f, 1.0f),
999 ImVec4(1.0f, 0.0f, 1.0f, 1.0f),
1000 ImVec4(1.0f, 1.0f, 0.0f, 1.0f),
1001 ImVec4(0.0f, 1.0f, 1.0f, 1.0f),
1002 ImVec4(1.0f, 0.0f, 0.0f, 1.0f),
1003 ImVec4(0.5f, 0.5f, 0.5f, 1.0f),
1006 return colors[index % (
sizeof(colors) /
sizeof(colors[0]))];
1015 if (session->rom.is_loaded()) {
void Publish(const T &event)
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
static bool IsPanelBasedEditor(EditorType type)
static bool UpdateAllowedWithoutLoadedRom(EditorType type)
Contains a complete set of editors for a single ROM instance.
virtual void ConfigureSession(RomSession *session)=0
virtual void SetCurrentEditor(Editor *editor)=0
Main UI class for editing overworld maps in A Link to the Past.
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
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()
EditorRegistry * editor_registry_
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)
SessionCoordinator(WorkspaceWindowManager *window_manager, ToastManager *toast_manager, UserSettings *user_settings)
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_
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
WorkspaceWindowManager * window_manager_
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
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
ISessionConfigurator * editor_manager_
void DrawSessionIndicator()
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
bool show_session_rename_dialog_
static constexpr size_t kMaxSessions
void Show(const std::string &message, ToastType type=ToastType::kInfo, float ttl_seconds=3.0f)
Manages user preferences and settings persistence.
Central registry for all editor cards with session awareness and dependency injection.
void HideAllWindowsInCategory(size_t session_id, const std::string &category)
void SetActiveSession(size_t session_id)
void UnregisterSession(size_t session_id)
void HideAllWindowsInSession(size_t session_id)
void ShowAllWindowsInSession(size_t session_id)
void ShowAllWindowsInCategory(size_t session_id, const std::string &category)
RAII guard for ImGui style colors.
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)
bool BeginThemedTabBar(const char *id, ImGuiTabBarFlags flags)
A stylized tab bar with "Mission Control" branding.
static RomLoadedEvent Create(Rom *r, const std::string &file, size_t session)
Represents a single session, containing a ROM and its associated editors.
static SessionClosedEvent Create(size_t idx)
static SessionCreatedEvent Create(size_t idx, RomSession *sess)
static SessionSwitchedEvent Create(size_t old_idx, size_t new_idx, RomSession *sess)