2#include <absl/status/status.h>
3#include <absl/status/statusor.h>
15#include "absl/strings/str_format.h"
30#include "imgui/imgui.h"
40 : panel_manager_(panel_manager),
41 toast_manager_(toast_manager),
42 user_settings_(user_settings) {}
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();
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()) {
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")) {
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())) {
504 if (ImGui::IsItemHovered()) {
505 ImGui::SetTooltip(
"Active Session: %s\nClick to open session switcher",
509 if (ImGui::IsItemClicked()) {
516 return "Invalid Session";
521 if (!session->custom_name.empty()) {
522 return session->custom_name;
525 if (session->rom.is_loaded()) {
526 return absl::StrFormat(
527 "Session %zu (%s)", index,
528 std::filesystem::path(session->filepath).stem().string());
531 return absl::StrFormat(
"Session %zu (Empty)", index);
539 const std::string& new_name) {
543 sessions_[index]->custom_name = new_name;
544 LOG_INFO(
"SessionCoordinator",
"Renamed session %zu to '%s'", index,
549 const std::string& editor_name,
size_t session_index)
const {
560 const auto& session =
sessions_[session_index];
561 std::string session_name = session->custom_name.empty()
562 ? session->rom.title()
563 : session->custom_name;
566 if (session_name.length() > 20) {
567 session_name = session_name.substr(0, 17) +
"...";
570 return absl::StrFormat(
"%s - %s##session_%zu", editor_name, session_name,
617 for (
size_t session_idx = 0; session_idx <
sessions_.size(); ++session_idx) {
619 if (!session->rom.is_loaded())
625 for (
auto editor : session->editors.active_editors_) {
626 if (*editor->active()) {
629 if (overworld_editor.jump_to_tab() != -1) {
631 session->editors.GetDungeonEditor()->add_room(
632 overworld_editor.jump_to_tab());
638 bool is_card_based_editor =
641 if (is_card_based_editor) {
648 absl::Status status = editor->Update();
652 std::string editor_name =
655 absl::StrFormat(
"%s Error: %s", editor_name, status.message()),
662 kEditorNames[
static_cast<int>(editor->type())], session_idx);
665 ImGui::SetNextWindowSize(ImGui::GetMainViewport()->WorkSize,
666 ImGuiCond_FirstUseEver);
667 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->WorkPos,
668 ImGuiCond_FirstUseEver);
670 if (ImGui::Begin(window_title.c_str(), editor->active(),
671 ImGuiWindowFlags_None)) {
678 absl::Status status = editor->Update();
682 std::string editor_name =
714 if (session->rom.is_loaded()) {
726 size_t session_index) {
727 if (filename.empty()) {
728 return absl::InvalidArgumentError(
"Invalid parameters");
731 size_t target_index =
734 return absl::InvalidArgumentError(
"Invalid session index");
738 LOG_INFO(
"SessionCoordinator",
"LoadRomIntoSession: %s -> session %zu",
739 filename.c_str(), target_index);
741 return absl::OkStatus();
745 const std::string& filename) {
747 return absl::FailedPreconditionError(
"No active session");
751 LOG_INFO(
"SessionCoordinator",
"SaveActiveSession: session %zu",
754 return absl::OkStatus();
758 const std::string& filename) {
760 return absl::InvalidArgumentError(
"Invalid parameters");
764 LOG_INFO(
"SessionCoordinator",
"SaveSessionAs: session %zu -> %s",
765 session_index, filename.c_str());
767 return absl::OkStatus();
771 Rom&& rom,
const std::string& filepath) {
772 size_t new_session_id =
sessions_.size();
773 sessions_.push_back(std::make_unique<RomSession>(
776 session->filepath = filepath;
785 return session.get();
790 size_t loaded_count = 0;
792 if (session->rom.is_loaded()) {
797 if (loaded_count > 0) {
799 if (!(*it)->rom.is_loaded() &&
sessions_.size() > 1) {
808 LOG_INFO(
"SessionCoordinator",
"Cleaned up closed sessions (remaining: %zu)",
818 for (
size_t i = 0; i <
sessions_.size(); ++i) {
827 LOG_INFO(
"SessionCoordinator",
"Cleared all sessions");
867 throw std::out_of_range(
868 absl::StrFormat(
"Invalid session index: %zu", index));
873 const std::string& base_name)
const {
877 std::string name = base_name;
883 if (session->custom_name == name) {
892 name = absl::StrFormat(
"%s %d", base_name, counter++);
901 absl::StrFormat(
"Maximum %zu sessions allowed",
kMaxSessions),
907 const std::string& operation,
bool success) {
909 std::string message =
910 absl::StrFormat(
"%s %s", operation, success ?
"succeeded" :
"failed");
926 if (session->rom.is_loaded()) {
931 if (ImGui::BeginTabItem(tab_name.c_str())) {
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())) {
976 if (session->rom.is_loaded()) {
985 static const ImVec4 colors[] = {
986 ImVec4(0.0f, 1.0f, 0.0f, 1.0f),
987 ImVec4(0.0f, 0.5f, 1.0f, 1.0f),
988 ImVec4(1.0f, 0.5f, 0.0f, 1.0f),
989 ImVec4(1.0f, 0.0f, 1.0f, 1.0f),
990 ImVec4(1.0f, 1.0f, 0.0f, 1.0f),
991 ImVec4(0.0f, 1.0f, 1.0f, 1.0f),
992 ImVec4(1.0f, 0.0f, 0.0f, 1.0f),
993 ImVec4(0.5f, 0.5f, 0.5f, 1.0f),
996 return colors[index % (
sizeof(colors) /
sizeof(colors[0]))];
1005 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)
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.
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
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)
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
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
PanelManager * panel_manager_
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.
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)