6#include "absl/strings/str_format.h"
10#include "imgui/imgui.h"
19 : sessions_ptr_(sessions_ptr),
20 card_registry_(card_registry),
21 toast_manager_(toast_manager),
22 user_settings_(user_settings) {
23 auto* sessions =
static_cast<std::deque<RomSession>*
>(
sessions_ptr_);
24 if (sessions && !sessions->empty()) {
31#define GET_SESSIONS() static_cast<std::deque<RomSession>*>(sessions_ptr_)
44 sessions->emplace_back();
50 LOG_INFO(
"SessionCoordinator",
"Created new session %zu (total: %zu)",
58 if (!sessions || sessions->empty())
68 sessions->emplace_back();
74 LOG_INFO(
"SessionCoordinator",
"Duplicated session %zu (total: %zu)",
105 sessions->at(index).custom_name =
"[CLOSED SESSION]";
116 LOG_INFO(
"SessionCoordinator",
"Closed session %zu (total: %zu)", index,
159 return session ? &session->rom :
nullptr;
164 return session ? &session->editors :
nullptr;
172 return &sessions->at(index);
184 const std::string& filepath)
const {
186 if (!sessions || filepath.empty())
189 for (
const auto& session : *sessions) {
190 if (session.filepath == filepath) {
199 if (!sessions || sessions->empty())
205 ImGui::SetNextWindowSize(ImVec2(400, 300), ImGuiCond_FirstUseEver);
206 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(),
207 ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
217 for (
size_t i = 0; i < sessions->size(); ++i) {
218 const auto& session = sessions->at(i);
221 ImGui::PushID(
static_cast<int>(i));
229 if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
230 ImGui::OpenPopup(
"SessionContextMenu");
233 if (ImGui::BeginPopup(
"SessionContextMenu")) {
244 if (ImGui::Button(absl::StrFormat(
"%s New Session",
ICON_MD_ADD).c_str())) {
256 ImGui::Button(absl::StrFormat(
"%s Close",
ICON_MD_CLOSE).c_str())) {
265 if (!sessions || sessions->empty())
271 ImGui::SetNextWindowSize(ImVec2(600, 400), ImGuiCond_FirstUseEver);
272 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(),
273 ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
291 if (ImGui::BeginTable(
"SessionTable", 4,
292 ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg |
293 ImGuiTableFlags_Resizable)) {
295 ImGui::TableSetupColumn(
"Session", ImGuiTableColumnFlags_WidthStretch,
297 ImGui::TableSetupColumn(
"ROM File", ImGuiTableColumnFlags_WidthStretch,
299 ImGui::TableSetupColumn(
"Status", ImGuiTableColumnFlags_WidthStretch, 0.2f);
300 ImGui::TableSetupColumn(
"Actions", ImGuiTableColumnFlags_WidthFixed,
302 ImGui::TableHeadersRow();
304 for (
size_t i = 0; i < sessions->size(); ++i) {
305 const auto& session = sessions->at(i);
308 ImGui::PushID(
static_cast<int>(i));
310 ImGui::TableNextRow();
313 ImGui::TableNextColumn();
315 ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f),
"%s %s",
324 ImGui::TableNextColumn();
325 if (session.rom.is_loaded()) {
326 ImGui::Text(
"%s", session.filepath.c_str());
328 ImGui::TextDisabled(
"(No ROM loaded)");
332 ImGui::TableNextColumn();
333 if (session.rom.is_loaded()) {
334 ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f),
"Loaded");
336 ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f),
"Empty");
340 ImGui::TableNextColumn();
341 if (!is_active && ImGui::SmallButton(
"Switch")) {
363 ImGui::SetNextWindowSize(ImVec2(300, 150), ImGuiCond_Always);
364 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(),
365 ImGuiCond_Always, ImVec2(0.5f, 0.5f));
378 if (ImGui::Button(
"OK")) {
385 if (ImGui::Button(
"Cancel")) {
395 if (!sessions || sessions->empty())
398 if (ImGui::BeginTabBar(
"SessionTabs")) {
399 for (
size_t i = 0; i < sessions->size(); ++i) {
401 const auto& session = sessions->at(i);
404 if (session.rom.is_loaded()) {
409 if (ImGui::BeginTabItem(tab_name.c_str())) {
417 if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
418 ImGui::OpenPopup(absl::StrFormat(
"SessionTabContext_%zu", i).c_str());
421 if (ImGui::BeginPopup(
422 absl::StrFormat(
"SessionTabContext_%zu", i).c_str())) {
438 ImGui::PushStyleColor(ImGuiCol_Text, accent_color);
440 ImGui::PopStyleColor();
442 if (ImGui::IsItemHovered()) {
443 ImGui::SetTooltip(
"Active Session: %s\nClick to open session switcher",
447 if (ImGui::IsItemClicked()) {
455 return "Invalid Session";
458 const auto& session = sessions->at(index);
460 if (!session.custom_name.empty()) {
461 return session.custom_name;
464 if (session.rom.is_loaded()) {
465 return absl::StrFormat(
466 "Session %zu (%s)", index,
467 std::filesystem::path(session.filepath).stem().string());
470 return absl::StrFormat(
"Session %zu (Empty)", index);
478 const std::string& new_name) {
483 sessions->at(index).custom_name = new_name;
484 LOG_INFO(
"SessionCoordinator",
"Renamed session %zu to '%s'", index,
489 const std::string& editor_name,
size_t session_index)
const {
492 if (!sessions || sessions->size() <= 1) {
497 if (session_index >= sessions->size()) {
502 const auto& session = sessions->at(session_index);
503 std::string session_name =
504 session.custom_name.empty() ? session.rom.title() : session.custom_name;
507 if (session_name.length() > 20) {
508 session_name = session_name.substr(0, 17) +
"...";
511 return absl::StrFormat(
"%s - %s##session_%zu", editor_name, session_name,
554 return sessions && index < sessions->size();
564 sessions->at(index).rom.is_loaded();
577 for (
const auto& session : *sessions) {
578 if (session.rom.is_loaded()) {
590 size_t session_index) {
592 if (!sessions || filename.empty()) {
593 return absl::InvalidArgumentError(
"Invalid parameters");
596 size_t target_index =
599 return absl::InvalidArgumentError(
"Invalid session index");
603 LOG_INFO(
"SessionCoordinator",
"LoadRomIntoSession: %s -> session %zu",
604 filename.c_str(), target_index);
606 return absl::OkStatus();
610 const std::string& filename) {
613 return absl::FailedPreconditionError(
"No active session");
617 LOG_INFO(
"SessionCoordinator",
"SaveActiveSession: session %zu",
620 return absl::OkStatus();
624 const std::string& filename) {
627 return absl::InvalidArgumentError(
"Invalid parameters");
631 LOG_INFO(
"SessionCoordinator",
"SaveSessionAs: session %zu -> %s",
632 session_index, filename.c_str());
634 return absl::OkStatus();
638 Rom&& rom,
const std::string& filepath) {
641 return absl::InternalError(
"Sessions not initialized");
643 size_t new_session_id = sessions->size();
644 sessions->emplace_back(std::move(rom),
user_settings_, new_session_id);
661 size_t loaded_count = 0;
662 for (
auto& session : *sessions) {
663 if (session.rom.is_loaded()) {
668 if (loaded_count > 0) {
669 for (
auto& session : *sessions) {
670 if (!session.rom.is_loaded() && sessions->size() > 1) {
671 session.custom_name =
"[CLOSED SESSION]";
677 LOG_INFO(
"SessionCoordinator",
"Cleaned up closed sessions (remaining: %zu)",
688 for (
size_t i = 0; i < sessions->size(); ++i) {
695 for (
auto& session : *sessions) {
696 session.custom_name =
"[CLOSED SESSION]";
702 LOG_INFO(
"SessionCoordinator",
"Cleared all sessions");
707 if (!sessions || sessions->empty())
716 if (!sessions || sessions->empty())
726 if (!sessions || sessions->empty())
733 if (!sessions || sessions->empty())
740 if (sessions && !sessions->empty() &&
748 throw std::out_of_range(
749 absl::StrFormat(
"Invalid session index: %zu", index));
754 const std::string& base_name)
const {
759 std::string name = base_name;
764 for (
const auto& session : *sessions) {
765 if (session.custom_name == name) {
774 name = absl::StrFormat(
"%s %d", base_name, counter++);
783 absl::StrFormat(
"Maximum %zu sessions allowed",
kMaxSessions),
789 const std::string& operation,
bool success) {
791 std::string message =
792 absl::StrFormat(
"%s %s", operation, success ?
"succeeded" :
"failed");
800 if (!sessions || index >= sessions->size())
803 const auto& session = sessions->at(index);
806 ImGui::PushStyleColor(ImGuiCol_Text, color);
809 if (session.rom.is_loaded()) {
814 if (ImGui::BeginTabItem(tab_name.c_str())) {
821 ImGui::PopStyleColor();
826 absl::StrFormat(
"%s Switch to Session",
ICON_MD_TAB).c_str())) {
830 if (ImGui::MenuItem(absl::StrFormat(
"%s Rename",
ICON_MD_EDIT).c_str())) {
847 absl::StrFormat(
"%s Close Session",
ICON_MD_CLOSE).c_str())) {
854 if (!sessions || index >= sessions->size())
857 const auto& session = sessions->at(index);
860 ImGui::PushStyleColor(ImGuiCol_Text, color);
862 if (session.rom.is_loaded()) {
868 ImGui::PopStyleColor();
873 static const ImVec4 colors[] = {
874 ImVec4(0.0f, 1.0f, 0.0f, 1.0f),
875 ImVec4(0.0f, 0.5f, 1.0f, 1.0f),
876 ImVec4(1.0f, 0.5f, 0.0f, 1.0f),
877 ImVec4(1.0f, 0.0f, 1.0f, 1.0f),
878 ImVec4(1.0f, 1.0f, 0.0f, 1.0f),
879 ImVec4(0.0f, 1.0f, 1.0f, 1.0f),
880 ImVec4(1.0f, 0.0f, 0.0f, 1.0f),
881 ImVec4(0.5f, 0.5f, 0.5f, 1.0f),
884 return colors[index % (
sizeof(colors) /
sizeof(colors[0]))];
889 if (!sessions || index >= sessions->size())
892 const auto& session = sessions->at(index);
894 if (session.rom.is_loaded()) {
904 !sessions->at(index).rom.is_loaded();
The Rom class is used to load, save, and modify Rom data.
Central registry for all editor cards with session awareness and dependency injection.
void HideAllCardsInSession(size_t session_id)
Hide all cards in a specific session.
void UnregisterSession(size_t session_id)
Unregister a session and all its cards.
void SetActiveSession(size_t session_id)
Set the currently active session.
void ShowAllCardsInCategory(size_t session_id, const std::string &category)
Show all cards in a category for a session.
void ShowAllCardsInSession(size_t session_id)
Show all cards in a specific session.
void HideAllCardsInCategory(size_t session_id, const std::string &category)
Hide all cards in a category for a session.
Contains a complete set of editors for a single ROM instance.
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()
EditorSet * GetCurrentEditorSet() const
size_t GetActiveSessionIndex() const
void CloseCurrentSession()
RomSession * GetActiveRomSession() const
void CloseSession(size_t index)
size_t GetActiveSessionCount() const
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)
void ShowAllCardsInActiveSession()
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()
ToastManager * toast_manager_
bool IsSessionClosed(size_t index) const
std::string GetSessionIcon(size_t index) const
SessionCoordinator(void *sessions_ptr, EditorCardRegistry *card_registry, ToastManager *toast_manager, UserSettings *user_settings)
bool show_session_manager_
void DrawSessionRenameDialog()
void ShowSessionLimitWarning()
void DrawSessionSwitcher()
void ShowCardsInCategory(const std::string &category)
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
void HideAllCardsInActiveSession()
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
void HideCardsInCategory(const std::string &category)
void ValidateSessionIndex(size_t index) const
EditorCardRegistry * card_registry_
void DrawSessionIndicator()
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.
static ThemeManager & Get()
const EnhancedTheme & GetCurrentTheme() const
#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,...)
ImVec4 ConvertColorToImVec4(const Color &color)
Main namespace for the application.
Represents a single session, containing a ROM and its associated editors.