yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
session_coordinator.cc
Go to the documentation of this file.
2#include <absl/status/status.h>
3#include <absl/status/statusor.h>
4
5#include <algorithm>
6#include <cstdint>
7#include <cstdio>
8#include <cstring>
9#include <filesystem>
10#include <memory>
11#include <stdexcept>
12#include <string>
13#include <utility>
14
15#include "absl/strings/str_format.h"
20#include "app/gui/core/icons.h"
22#include "core/color.h"
23#include "editor/editor.h"
26#include "imgui/imgui.h"
27#include "util/log.h"
28#include "zelda3/game_data.h"
29
30namespace yaze {
31namespace editor {
32
34 ToastManager* toast_manager,
35 UserSettings* user_settings)
36 : panel_manager_(panel_manager),
37 toast_manager_(toast_manager),
38 user_settings_(user_settings) {}
39
41 if (observer) {
42 observers_.push_back(observer);
43 }
44}
45
47 observers_.erase(std::remove(observers_.begin(), observers_.end(), observer),
48 observers_.end());
49}
50
52 RomSession* session) {
53 for (auto* observer : observers_) {
54 observer->OnSessionSwitched(index, session);
55 }
56}
57
59 RomSession* session) {
60 for (auto* observer : observers_) {
61 observer->OnSessionCreated(index, session);
62 }
63}
64
66 for (auto* observer : observers_) {
67 observer->OnSessionClosed(index);
68 }
69}
70
72 RomSession* session) {
73 for (auto* observer : observers_) {
74 observer->OnSessionRomLoaded(index, session);
75 }
76}
77
81 return;
82 }
83
84 // Create new empty session
85 sessions_.push_back(std::make_unique<RomSession>());
87
88 // Set as active session
90
91 // Configure the new session
92 if (editor_manager_) {
93 auto& session = sessions_.back();
94 editor_manager_->ConfigureSession(session.get());
95 }
96
97 LOG_INFO("SessionCoordinator", "Created new session %zu (total: %zu)",
99
100 // Notify observers
102
103 ShowSessionOperationResult("Create Session", true);
104}
105
107 if (sessions_.empty())
108 return;
109
112 return;
113 }
114
115 // Create new empty session (cannot actually duplicate due to non-movable
116 // editors)
117 // TODO: Implement proper duplication when editors become movable
118 sessions_.push_back(std::make_unique<RomSession>());
120
121 // Set as active session
122 active_session_index_ = sessions_.size() - 1;
123
124 // Configure the new session
125 if (editor_manager_) {
126 auto& session = sessions_.back();
127 editor_manager_->ConfigureSession(session.get());
128 }
129
130 LOG_INFO("SessionCoordinator", "Duplicated session %zu (total: %zu)",
132
133 // Notify observers
135
136 ShowSessionOperationResult("Duplicate Session", true);
137}
138
142
144 if (!IsValidSessionIndex(index))
145 return;
146
148 // Don't allow closing the last session
149 if (toast_manager_) {
150 toast_manager_->Show("Cannot close the last session",
152 }
153 return;
154 }
155
156 // Unregister cards for this session
157 if (panel_manager_) {
159 }
160
161 // Notify observers before removal
162 NotifySessionClosed(index);
163
164 // Remove session (safe now with unique_ptr!)
165 sessions_.erase(sessions_.begin() + index);
167
168 // Adjust active session index
169 if (active_session_index_ >= index && active_session_index_ > 0) {
171 }
172
173 LOG_INFO("SessionCoordinator", "Closed session %zu (total: %zu)", index,
175
176 ShowSessionOperationResult("Close Session", true);
177}
178
180 CloseSession(index);
181}
182
184 if (!IsValidSessionIndex(index))
185 return;
186
187 size_t old_index = active_session_index_;
188 active_session_index_ = index;
189
190 if (panel_manager_) {
192 }
193
194 // Only notify if actually switching to a different session
195 if (old_index != index) {
196 NotifySessionSwitched(index, sessions_[index].get());
197 }
198}
199
201 SwitchToSession(index);
202}
203
207
210 return nullptr;
211 }
212 return sessions_[active_session_index_].get();
213}
214
218
220 auto* session = GetActiveRomSession();
221 return session ? &session->rom : nullptr;
222}
223
225 auto* session = GetActiveRomSession();
226 return session ? &session->game_data : nullptr;
227}
228
230 auto* session = GetActiveRomSession();
231 return session ? &session->editors : nullptr;
232}
233
234void* SessionCoordinator::GetSession(size_t index) const {
235 if (!IsValidSessionIndex(index)) {
236 return nullptr;
237 }
238 return sessions_[index].get();
239}
240
242 return session_count_ > 1;
243}
244
248
250 const std::string& filepath) const {
251 if (filepath.empty())
252 return false;
253
254 for (const auto& session : sessions_) {
255 if (session->filepath == filepath) {
256 return true;
257 }
258 }
259 return false;
260}
261
263 if (sessions_.empty())
264 return;
265
267 return;
268
269 ImGui::SetNextWindowSize(ImVec2(400, 300), ImGuiCond_FirstUseEver);
270 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(),
271 ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
272
273 if (!ImGui::Begin("Session Switcher", &show_session_switcher_)) {
274 ImGui::End();
275 return;
276 }
277
278 ImGui::Text("%s Active Sessions (%zu)", ICON_MD_TAB, session_count_);
279 ImGui::Separator();
280
281 for (size_t i = 0; i < sessions_.size(); ++i) {
282 bool is_active = (i == active_session_index_);
283
284 ImGui::PushID(static_cast<int>(i));
285
286 // Session tab
287 if (ImGui::Selectable(GetSessionDisplayName(i).c_str(), is_active)) {
289 }
290
291 // Right-click context menu
292 if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
293 ImGui::OpenPopup("SessionContextMenu");
294 }
295
296 if (ImGui::BeginPopup("SessionContextMenu")) {
298 ImGui::EndPopup();
299 }
300
301 ImGui::PopID();
302 }
303
304 ImGui::Separator();
305
306 // Action buttons
307 if (ImGui::Button(absl::StrFormat("%s New Session", ICON_MD_ADD).c_str())) {
309 }
310
311 ImGui::SameLine();
312 if (ImGui::Button(
313 absl::StrFormat("%s Duplicate", ICON_MD_CONTENT_COPY).c_str())) {
315 }
316
317 ImGui::SameLine();
318 if (HasMultipleSessions() &&
319 ImGui::Button(absl::StrFormat("%s Close", ICON_MD_CLOSE).c_str())) {
321 }
322
323 ImGui::End();
324}
325
327 if (sessions_.empty())
328 return;
329
331 return;
332
333 ImGui::SetNextWindowSize(ImVec2(600, 400), ImGuiCond_FirstUseEver);
334 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(),
335 ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
336
337 if (!ImGui::Begin("Session Manager", &show_session_manager_)) {
338 ImGui::End();
339 return;
340 }
341
342 // Session statistics
343 ImGui::Text("%s Session Statistics", ICON_MD_ANALYTICS);
344 ImGui::Separator();
345
346 ImGui::Text("Total Sessions: %zu", GetTotalSessionCount());
347 ImGui::Text("Loaded Sessions: %zu", GetLoadedSessionCount());
348 ImGui::Text("Empty Sessions: %zu", GetEmptySessionCount());
349
350 ImGui::Spacing();
351
352 // Session list
353 if (ImGui::BeginTable("SessionTable", 4,
354 ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg |
355 ImGuiTableFlags_Resizable)) {
356 ImGui::TableSetupColumn("Session", ImGuiTableColumnFlags_WidthStretch,
357 0.3f);
358 ImGui::TableSetupColumn("ROM File", ImGuiTableColumnFlags_WidthStretch,
359 0.4f);
360 ImGui::TableSetupColumn("Status", ImGuiTableColumnFlags_WidthStretch, 0.2f);
361 ImGui::TableSetupColumn("Actions", ImGuiTableColumnFlags_WidthFixed,
362 120.0f);
363 ImGui::TableHeadersRow();
364
365 for (size_t i = 0; i < sessions_.size(); ++i) {
366 const auto& session = sessions_[i];
367 bool is_active = (i == active_session_index_);
368
369 ImGui::PushID(static_cast<int>(i));
370
371 ImGui::TableNextRow();
372
373 // Session name
374 ImGui::TableNextColumn();
375 if (is_active) {
376 ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f), "%s %s",
378 GetSessionDisplayName(i).c_str());
379 } else {
380 ImGui::Text("%s %s", ICON_MD_RADIO_BUTTON_UNCHECKED,
381 GetSessionDisplayName(i).c_str());
382 }
383
384 // ROM file
385 ImGui::TableNextColumn();
386 if (session->rom.is_loaded()) {
387 ImGui::Text("%s", session->filepath.c_str());
388 } else {
389 ImGui::TextDisabled("(No ROM loaded)");
390 }
391
392 // Status
393 ImGui::TableNextColumn();
394 if (session->rom.is_loaded()) {
395 ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f), "Loaded");
396 } else {
397 ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "Empty");
398 }
399
400 // Actions
401 ImGui::TableNextColumn();
402 if (!is_active && ImGui::SmallButton("Switch")) {
404 }
405
406 ImGui::SameLine();
407 if (HasMultipleSessions() && ImGui::SmallButton("Close")) {
408 CloseSession(i);
409 }
410
411 ImGui::PopID();
412 }
413
414 ImGui::EndTable();
415 }
416
417 ImGui::End();
418}
419
422 return;
423
424 ImGui::SetNextWindowSize(ImVec2(300, 150), ImGuiCond_Always);
425 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(),
426 ImGuiCond_Always, ImVec2(0.5f, 0.5f));
427
428 if (!ImGui::Begin("Rename Session", &show_session_rename_dialog_)) {
429 ImGui::End();
430 return;
431 }
432
433 ImGui::Text("Rename session %zu:", session_to_rename_);
434 ImGui::InputText("Name", session_rename_buffer_,
435 sizeof(session_rename_buffer_));
436
437 ImGui::Spacing();
438
439 if (ImGui::Button("OK")) {
442 session_rename_buffer_[0] = '\0';
443 }
444
445 ImGui::SameLine();
446 if (ImGui::Button("Cancel")) {
448 session_rename_buffer_[0] = '\0';
449 }
450
451 ImGui::End();
452}
453
455 if (sessions_.empty())
456 return;
457
458 if (ImGui::BeginTabBar("SessionTabs")) {
459 for (size_t i = 0; i < sessions_.size(); ++i) {
460 bool is_active = (i == active_session_index_);
461 const auto& session = sessions_[i];
462
463 std::string tab_name = GetSessionDisplayName(i);
464 if (session->rom.is_loaded()) {
465 tab_name += " ";
466 tab_name += ICON_MD_CHECK_CIRCLE;
467 }
468
469 if (ImGui::BeginTabItem(tab_name.c_str())) {
470 if (!is_active) {
472 }
473 ImGui::EndTabItem();
474 }
475
476 // Right-click context menu
477 if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
478 ImGui::OpenPopup(absl::StrFormat("SessionTabContext_%zu", i).c_str());
479 }
480
481 if (ImGui::BeginPopup(
482 absl::StrFormat("SessionTabContext_%zu", i).c_str())) {
484 ImGui::EndPopup();
485 }
486 }
487 ImGui::EndTabBar();
488 }
489}
490
492 if (!HasMultipleSessions())
493 return;
494
495 const auto& theme = gui::ThemeManager::Get().GetCurrentTheme();
496 ImVec4 accent_color = ConvertColorToImVec4(theme.accent);
497
498 ImGui::PushStyleColor(ImGuiCol_Text, accent_color);
499 ImGui::Text("%s Session %zu", ICON_MD_TAB, active_session_index_);
500 ImGui::PopStyleColor();
501
502 if (ImGui::IsItemHovered()) {
503 ImGui::SetTooltip("Active Session: %s\nClick to open session switcher",
505 }
506
507 if (ImGui::IsItemClicked()) {
509 }
510}
511
512std::string SessionCoordinator::GetSessionDisplayName(size_t index) const {
513 if (!IsValidSessionIndex(index)) {
514 return "Invalid Session";
515 }
516
517 const auto& session = sessions_[index];
518
519 if (!session->custom_name.empty()) {
520 return session->custom_name;
521 }
522
523 if (session->rom.is_loaded()) {
524 return absl::StrFormat(
525 "Session %zu (%s)", index,
526 std::filesystem::path(session->filepath).stem().string());
527 }
528
529 return absl::StrFormat("Session %zu (Empty)", index);
530}
531
535
537 const std::string& new_name) {
538 if (!IsValidSessionIndex(index) || new_name.empty())
539 return;
540
541 sessions_[index]->custom_name = new_name;
542 LOG_INFO("SessionCoordinator", "Renamed session %zu to '%s'", index,
543 new_name.c_str());
544}
545
547 const std::string& editor_name, size_t session_index) const {
548 if (sessions_.size() <= 1) {
549 // Single session - use simple name
550 return editor_name;
551 }
552
553 if (session_index >= sessions_.size()) {
554 return editor_name;
555 }
556
557 // Multi-session - include session identifier
558 const auto& session = sessions_[session_index];
559 std::string session_name = session->custom_name.empty()
560 ? session->rom.title()
561 : session->custom_name;
562
563 // Truncate long session names
564 if (session_name.length() > 20) {
565 session_name = session_name.substr(0, 17) + "...";
566 }
567
568 return absl::StrFormat("%s - %s##session_%zu", editor_name, session_name,
569 session_index);
570}
571
573 SwitchToSession(index);
574}
575
579
580// Panel coordination across sessions
586
592
593void SessionCoordinator::ShowPanelsInCategory(const std::string& category) {
594 if (panel_manager_) {
596 }
597}
598
599void SessionCoordinator::HidePanelsInCategory(const std::string& category) {
600 if (panel_manager_) {
602 }
603}
604
606 return index < sessions_.size();
607}
608
610 if (sessions_.empty())
611 return;
612
613 size_t original_session_idx = active_session_index_;
614
615 for (size_t session_idx = 0; session_idx < sessions_.size(); ++session_idx) {
616 auto& session = sessions_[session_idx];
617 if (!session->rom.is_loaded())
618 continue; // Skip sessions with invalid ROMs
619
620 // Switch context
621 SwitchToSession(session_idx);
622
623 for (auto editor : session->editors.active_editors_) {
624 if (*editor->active()) {
625 if (editor->type() == EditorType::kOverworld) {
626 auto& overworld_editor = static_cast<OverworldEditor&>(*editor);
627 if (overworld_editor.jump_to_tab() != -1) {
628 // Set the dungeon editor to the jump to tab
629 session->editors.GetDungeonEditor()->add_room(
630 overworld_editor.jump_to_tab());
631 overworld_editor.jump_to_tab_ = -1;
632 }
633 }
634
635 // CARD-BASED EDITORS: Don't wrap in Begin/End, they manage own windows
636 bool is_card_based_editor =
637 EditorManager::IsPanelBasedEditor(editor->type());
638
639 if (is_card_based_editor) {
640 // Panel-based editors create their own top-level windows
641 // No parent wrapper needed - this allows independent docking
642 if (editor_manager_) {
644 }
645
646 absl::Status status = editor->Update();
647
648 // Route editor errors to toast manager
649 if (!status.ok() && toast_manager_) {
650 std::string editor_name =
651 kEditorNames[static_cast<int>(editor->type())];
653 absl::StrFormat("%s Error: %s", editor_name, status.message()),
654 ToastType::kError, 8.0f);
655 }
656
657 } else {
658 // TRADITIONAL EDITORS: Wrap in Begin/End
659 std::string window_title = GenerateUniqueEditorTitle(
660 kEditorNames[static_cast<int>(editor->type())], session_idx);
661
662 // Set window to maximize on first open
663 ImGui::SetNextWindowSize(ImGui::GetMainViewport()->WorkSize,
664 ImGuiCond_FirstUseEver);
665 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->WorkPos,
666 ImGuiCond_FirstUseEver);
667
668 if (ImGui::Begin(window_title.c_str(), editor->active(),
669 ImGuiWindowFlags_None)) { // Allow full docking
670 // Temporarily switch context for this editor's update
671 // (Already switched via SwitchToSession)
672 if (editor_manager_) {
674 }
675
676 absl::Status status = editor->Update();
677
678 // Route editor errors to toast manager
679 if (!status.ok() && toast_manager_) {
680 std::string editor_name =
681 kEditorNames[static_cast<int>(editor->type())];
682 toast_manager_->Show(absl::StrFormat("%s Error: %s", editor_name,
683 status.message()),
684 ToastType::kError, 8.0f);
685 }
686 }
687 ImGui::End();
688 }
689 }
690 }
691 }
692
693 // Restore original session context
694 SwitchToSession(original_session_idx);
695}
696
697bool SessionCoordinator::IsSessionActive(size_t index) const {
698 return index == active_session_index_;
699}
700
701bool SessionCoordinator::IsSessionLoaded(size_t index) const {
702 return IsValidSessionIndex(index) && sessions_[index]->rom.is_loaded();
703}
704
708
710 size_t count = 0;
711 for (const auto& session : sessions_) {
712 if (session->rom.is_loaded()) {
713 count++;
714 }
715 }
716 return count;
717}
718
722
723absl::Status SessionCoordinator::LoadRomIntoSession(const std::string& filename,
724 size_t session_index) {
725 if (filename.empty()) {
726 return absl::InvalidArgumentError("Invalid parameters");
727 }
728
729 size_t target_index =
730 (session_index == SIZE_MAX) ? active_session_index_ : session_index;
731 if (!IsValidSessionIndex(target_index)) {
732 return absl::InvalidArgumentError("Invalid session index");
733 }
734
735 // TODO: Implement actual ROM loading
736 LOG_INFO("SessionCoordinator", "LoadRomIntoSession: %s -> session %zu",
737 filename.c_str(), target_index);
738
739 return absl::OkStatus();
740}
741
743 const std::string& filename) {
745 return absl::FailedPreconditionError("No active session");
746 }
747
748 // TODO: Implement actual ROM saving
749 LOG_INFO("SessionCoordinator", "SaveActiveSession: session %zu",
751
752 return absl::OkStatus();
753}
754
755absl::Status SessionCoordinator::SaveSessionAs(size_t session_index,
756 const std::string& filename) {
757 if (!IsValidSessionIndex(session_index) || filename.empty()) {
758 return absl::InvalidArgumentError("Invalid parameters");
759 }
760
761 // TODO: Implement actual ROM saving
762 LOG_INFO("SessionCoordinator", "SaveSessionAs: session %zu -> %s",
763 session_index, filename.c_str());
764
765 return absl::OkStatus();
766}
767
769 Rom&& rom, const std::string& filepath) {
770 size_t new_session_id = sessions_.size();
771 sessions_.push_back(std::make_unique<RomSession>(
772 std::move(rom), user_settings_, new_session_id));
773 auto& session = sessions_.back();
774 session->filepath = filepath;
775
777 SwitchToSession(new_session_id);
778
779 // Notify observers
780 NotifySessionCreated(new_session_id, session.get());
781 NotifySessionRomLoaded(new_session_id, session.get());
782
783 return session.get();
784}
785
787 // Mark empty sessions as closed (except keep at least one)
788 size_t loaded_count = 0;
789 for (const auto& session : sessions_) {
790 if (session->rom.is_loaded()) {
791 loaded_count++;
792 }
793 }
794
795 if (loaded_count > 0) {
796 for (auto it = sessions_.begin(); it != sessions_.end();) {
797 if (!(*it)->rom.is_loaded() && sessions_.size() > 1) {
798 it = sessions_.erase(it);
799 } else {
800 ++it;
801 }
802 }
803 }
804
806 LOG_INFO("SessionCoordinator", "Cleaned up closed sessions (remaining: %zu)",
808}
809
811 if (sessions_.empty())
812 return;
813
814 // Unregister all session cards
815 if (panel_manager_) {
816 for (size_t i = 0; i < sessions_.size(); ++i) {
818 }
819 }
820
821 sessions_.clear();
824
825 LOG_INFO("SessionCoordinator", "Cleared all sessions");
826}
827
829 if (sessions_.empty())
830 return;
831
832 size_t next_index = (active_session_index_ + 1) % sessions_.size();
833 SwitchToSession(next_index);
834}
835
837 if (sessions_.empty())
838 return;
839
840 size_t prev_index = (active_session_index_ == 0) ? sessions_.size() - 1
842 SwitchToSession(prev_index);
843}
844
846 if (sessions_.empty())
847 return;
849}
850
852 if (sessions_.empty())
853 return;
854 SwitchToSession(sessions_.size() - 1);
855}
856
858 if (!sessions_.empty() && active_session_index_ >= sessions_.size()) {
859 active_session_index_ = sessions_.size() - 1;
860 }
861}
862
864 if (!IsValidSessionIndex(index)) {
865 throw std::out_of_range(
866 absl::StrFormat("Invalid session index: %zu", index));
867 }
868}
869
871 const std::string& base_name) const {
872 if (sessions_.empty())
873 return base_name;
874
875 std::string name = base_name;
876 int counter = 1;
877
878 while (true) {
879 bool found = false;
880 for (const auto& session : sessions_) {
881 if (session->custom_name == name) {
882 found = true;
883 break;
884 }
885 }
886
887 if (!found)
888 break;
889
890 name = absl::StrFormat("%s %d", base_name, counter++);
891 }
892
893 return name;
894}
895
897 if (toast_manager_) {
899 absl::StrFormat("Maximum %zu sessions allowed", kMaxSessions),
901 }
902}
903
905 const std::string& operation, bool success) {
906 if (toast_manager_) {
907 std::string message =
908 absl::StrFormat("%s %s", operation, success ? "succeeded" : "failed");
910 toast_manager_->Show(message, type);
911 }
912}
913
914void SessionCoordinator::DrawSessionTab(size_t index, bool is_active) {
915 if (index >= sessions_.size())
916 return;
917
918 const auto& session = sessions_[index];
919
920 ImVec4 color = GetSessionColor(index);
921 ImGui::PushStyleColor(ImGuiCol_Text, color);
922
923 std::string tab_name = GetSessionDisplayName(index);
924 if (session->rom.is_loaded()) {
925 tab_name += " ";
926 tab_name += ICON_MD_CHECK_CIRCLE;
927 }
928
929 if (ImGui::BeginTabItem(tab_name.c_str())) {
930 if (!is_active) {
931 SwitchToSession(index);
932 }
933 ImGui::EndTabItem();
934 }
935
936 ImGui::PopStyleColor();
937}
938
940 if (ImGui::MenuItem(
941 absl::StrFormat("%s Switch to Session", ICON_MD_TAB).c_str())) {
942 SwitchToSession(index);
943 }
944
945 if (ImGui::MenuItem(absl::StrFormat("%s Rename", ICON_MD_EDIT).c_str())) {
946 session_to_rename_ = index;
947 strncpy(session_rename_buffer_, GetSessionDisplayName(index).c_str(),
948 sizeof(session_rename_buffer_) - 1);
951 }
952
953 if (ImGui::MenuItem(
954 absl::StrFormat("%s Duplicate", ICON_MD_CONTENT_COPY).c_str())) {
955 // TODO: Implement session duplication
956 }
957
958 ImGui::Separator();
959
960 if (HasMultipleSessions() &&
961 ImGui::MenuItem(
962 absl::StrFormat("%s Close Session", ICON_MD_CLOSE).c_str())) {
963 CloseSession(index);
964 }
965}
966
968 if (index >= sessions_.size())
969 return;
970
971 const auto& session = sessions_[index];
972 ImVec4 color = GetSessionColor(index);
973
974 ImGui::PushStyleColor(ImGuiCol_Text, color);
975
976 if (session->rom.is_loaded()) {
977 ImGui::Text("%s", ICON_MD_CHECK_CIRCLE);
978 } else {
979 ImGui::Text("%s", ICON_MD_RADIO_BUTTON_UNCHECKED);
980 }
981
982 ImGui::PopStyleColor();
983}
984
985ImVec4 SessionCoordinator::GetSessionColor(size_t index) const {
986 // Generate consistent colors for sessions
987 static const ImVec4 colors[] = {
988 ImVec4(0.0f, 1.0f, 0.0f, 1.0f), // Green
989 ImVec4(0.0f, 0.5f, 1.0f, 1.0f), // Blue
990 ImVec4(1.0f, 0.5f, 0.0f, 1.0f), // Orange
991 ImVec4(1.0f, 0.0f, 1.0f, 1.0f), // Magenta
992 ImVec4(1.0f, 1.0f, 0.0f, 1.0f), // Yellow
993 ImVec4(0.0f, 1.0f, 1.0f, 1.0f), // Cyan
994 ImVec4(1.0f, 0.0f, 0.0f, 1.0f), // Red
995 ImVec4(0.5f, 0.5f, 0.5f, 1.0f), // Gray
996 };
997
998 return colors[index % (sizeof(colors) / sizeof(colors[0]))];
999}
1000
1001std::string SessionCoordinator::GetSessionIcon(size_t index) const {
1002 if (index >= sessions_.size())
1004
1005 const auto& session = sessions_[index];
1006
1007 if (session->rom.is_loaded()) {
1008 return ICON_MD_CHECK_CIRCLE;
1009 } else {
1011 }
1012}
1013
1014bool SessionCoordinator::IsSessionEmpty(size_t index) const {
1015 return IsValidSessionIndex(index) && !sessions_[index]->rom.is_loaded();
1016}
1017
1018bool SessionCoordinator::IsSessionClosed(size_t index) const {
1019 return !IsValidSessionIndex(index);
1020}
1021
1023 // TODO: Implement modification tracking
1024 return false;
1025}
1026
1027} // namespace editor
1028} // namespace yaze
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
Definition rom.h:24
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 * GetSession(size_t index) const
void RemoveObserver(SessionObserver *observer)
std::string GenerateUniqueEditorTitle(const std::string &editor_name, size_t session_index) const
zelda3::GameData * GetCurrentGameData() const
void NotifySessionRomLoaded(size_t index, RomSession *session)
absl::StatusOr< RomSession * > CreateSessionFromRom(Rom &&rom, const std::string &filepath)
absl::Status SaveSessionAs(size_t session_index, const std::string &filename)
absl::Status SaveActiveSession(const std::string &filename="")
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
SessionCoordinator(PanelManager *panel_manager, ToastManager *toast_manager, UserSettings *user_settings)
bool HasDuplicateSession(const std::string &filepath) const
void ShowPanelsInCategory(const std::string &category)
void HidePanelsInCategory(const std::string &category)
bool IsSessionClosed(size_t index) const
std::string GetSessionIcon(size_t index) const
std::string GetActiveSessionDisplayName() 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_
void DrawSessionTab(size_t index, bool is_active)
bool IsSessionActive(size_t index) const
std::vector< std::unique_ptr< RomSession > > sessions_
void ValidateSessionIndex(size_t index) const
void AddObserver(SessionObserver *observer)
void NotifySessionSwitched(size_t index, RomSession *session)
bool IsSessionLoaded(size_t index) const
std::string GenerateUniqueSessionName(const std::string &base_name) const
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_EDIT
Definition icons.h:645
#define ICON_MD_ADD
Definition icons.h:86
#define ICON_MD_CHECK_CIRCLE
Definition icons.h:400
#define ICON_MD_RADIO_BUTTON_CHECKED
Definition icons.h:1548
#define ICON_MD_TAB
Definition icons.h:1930
#define ICON_MD_CONTENT_COPY
Definition icons.h:465
#define ICON_MD_RADIO_BUTTON_UNCHECKED
Definition icons.h:1551
#define ICON_MD_CLOSE
Definition icons.h:418
#define ICON_MD_ANALYTICS
Definition icons.h:154
#define LOG_INFO(category, format,...)
Definition log.h:105
constexpr std::array< const char *, 14 > kEditorNames
Definition editor.h:167
ImVec4 ConvertColorToImVec4(const Color &color)
Definition color.h:23
Represents a single session, containing a ROM and its associated editors.