Implement multi-session support and welcome screen in EditorManager

- Removed the homepage display logic and replaced it with a welcome screen that appears when no ROM is loaded or no active editors are present.
- Enhanced session management by iterating through all sessions to check for active editors, allowing for better multi-session docking.
- Introduced new methods for generating unique editor titles based on session context and added feature flags for per-session configurations.
- Added safeguards for ROM loading state checks in editor methods to prevent operations on unloaded ROMs.
This commit is contained in:
scawful
2025-09-26 17:32:21 -04:00
parent 0f37061299
commit a53e759043
8 changed files with 358 additions and 26 deletions

View File

@@ -423,8 +423,6 @@ void EditorManager::Initialize(const std::string &filename) {
{},
{},
{
{absl::StrCat(ICON_MD_HOME, " Home"), "",
[&]() { show_homepage_ = true; }},
{kAssemblyEditorName, "", [&]() { show_asm_editor_ = true; },
[&]() { return show_asm_editor_; }},
{kDungeonEditorName, "",
@@ -734,37 +732,78 @@ absl::Status EditorManager::Update() {
autosave_timer_ = 0.0f;
}
if (show_homepage_) {
ImGui::Begin("Home", &show_homepage_);
DrawHomepage();
ImGui::End();
}
// Check if ROM is loaded before allowing editor updates
if (!current_editor_set_) {
// Show welcome screen when no session is active
if (sessions_.empty()) {
DrawWelcomeScreen();
}
return absl::OkStatus();
}
for (auto editor : current_editor_set_->active_editors_) {
if (*editor->active()) {
if (editor->type() == EditorType::kOverworld) {
auto &overworld_editor = static_cast<OverworldEditor &>(*editor);
if (overworld_editor.jump_to_tab() != -1) {
current_editor_set_->dungeon_editor_.set_active(true);
// Set the dungeon editor to the jump to tab
current_editor_set_->dungeon_editor_.add_room(
overworld_editor.jump_to_tab());
overworld_editor.jump_to_tab_ = -1;
// Check if current ROM is valid
if (!current_rom_) {
DrawWelcomeScreen();
return absl::OkStatus();
}
// Check if any editors are active across ALL sessions
bool any_editor_active = false;
for (const auto& session : sessions_) {
if (!session.rom.is_loaded()) continue;
for (auto editor : session.editors.active_editors_) {
if (*editor->active()) {
any_editor_active = true;
break;
}
}
if (any_editor_active) break;
}
// Show welcome screen if no editors are active (ROM loaded but editors not opened)
if (!any_editor_active) {
DrawWelcomeScreen();
return absl::OkStatus();
}
// Iterate through ALL sessions to support multi-session docking
for (size_t session_idx = 0; session_idx < sessions_.size(); ++session_idx) {
auto& session = sessions_[session_idx];
if (!session.rom.is_loaded()) continue; // Skip sessions with invalid ROMs
for (auto editor : session.editors.active_editors_) {
if (*editor->active()) {
if (editor->type() == EditorType::kOverworld) {
auto &overworld_editor = static_cast<OverworldEditor &>(*editor);
if (overworld_editor.jump_to_tab() != -1) {
session.editors.dungeon_editor_.set_active(true);
// Set the dungeon editor to the jump to tab
session.editors.dungeon_editor_.add_room(overworld_editor.jump_to_tab());
overworld_editor.jump_to_tab_ = -1;
}
}
}
// Generate unique window titles for multi-session support
size_t session_index = GetCurrentSessionIndex();
std::string window_title = GenerateUniqueEditorTitle(editor->type(), session_index);
if (ImGui::Begin(window_title.c_str(), editor->active())) {
current_editor_ = editor;
status_ = editor->Update();
// Generate unique window titles for multi-session support
std::string window_title = GenerateUniqueEditorTitle(editor->type(), session_idx);
if (ImGui::Begin(window_title.c_str(), editor->active())) {
// Temporarily switch context for this editor's update
Rom* prev_rom = current_rom_;
EditorSet* prev_editor_set = current_editor_set_;
current_rom_ = &session.rom;
current_editor_set_ = &session.editors;
current_editor_ = editor;
status_ = editor->Update();
// Restore context
current_rom_ = prev_rom;
current_editor_set_ = prev_editor_set;
}
ImGui::End();
}
ImGui::End();
}
}
return absl::OkStatus();
@@ -1424,6 +1463,26 @@ size_t EditorManager::GetCurrentSessionIndex() const {
return 0; // Default to first session if not found
}
std::string EditorManager::GenerateUniqueEditorTitle(EditorType type, size_t session_index) const {
const char* base_name = kEditorNames[static_cast<int>(type)];
if (sessions_.size() <= 1) {
// Single session - use simple name
return std::string(base_name);
}
// Multi-session - include session identifier
const auto& session = sessions_[session_index];
std::string session_name = session.GetDisplayName();
// Truncate long session names
if (session_name.length() > 20) {
session_name = session_name.substr(0, 17) + "...";
}
return absl::StrFormat("%s - %s##session_%zu", base_name, session_name, session_index);
}
// Layout Management Functions
void EditorManager::ResetWorkspaceLayout() {
// Show confirmation popup first
@@ -1820,5 +1879,172 @@ void EditorManager::DrawSessionRenameDialog() {
ImGui::End();
}
void EditorManager::DrawWelcomeScreen() {
ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Always, ImVec2(0.5f, 0.5f));
ImGui::SetNextWindowSize(ImVec2(680, 500), ImGuiCond_Always);
ImGuiWindowFlags flags = ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar;
if (ImGui::Begin("Welcome to Yaze", nullptr, flags)) {
// Header (reuse homepage style)
TextWrapped("Welcome to the Yet Another Zelda3 Editor (yaze)!");
TextWrapped("The Legend of Zelda: A Link to the Past.");
// Show different messages based on state
if (!sessions_.empty() && !current_rom_) {
ImGui::Separator();
ImGui::Spacing();
ImGui::TextColored(ImVec4(1.0f, 0.7f, 0.0f, 1.0f), ICON_MD_WARNING " ROM Loading Required");
TextWrapped("A session exists but no ROM is loaded. Please load a ROM file to continue editing.");
ImGui::Text("Active Sessions: %zu", sessions_.size());
} else {
ImGui::Separator();
ImGui::Spacing();
TextWrapped("No ROM loaded.");
}
ImGui::Spacing();
// Primary actions with material design icons
ImGui::Text("Get Started:");
ImGui::Spacing();
if (ImGui::Button(ICON_MD_FILE_OPEN " Open ROM File", ImVec2(180, 35))) {
status_ = LoadRom();
if (!status_.ok()) {
toast_manager_.Show(std::string(status_.message()), editor::ToastType::kError);
}
}
ImGui::SameLine();
if (ImGui::Button(ICON_MD_FOLDER_OPEN " Open Project", ImVec2(180, 35))) {
auto file_name = core::FileDialogWrapper::ShowOpenFileDialog();
if (!file_name.empty()) {
status_ = OpenRomOrProject(file_name);
if (!status_.ok()) {
toast_manager_.Show(std::string(status_.message()), editor::ToastType::kError);
}
}
}
ImGui::Spacing();
// Feature flags section (per-session)
ImGui::Text("Options:");
auto* flags = GetCurrentFeatureFlags();
Checkbox("Load custom overworld features", &flags->overworld.kLoadCustomOverworld);
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing();
// Recent files section (reuse homepage logic)
ImGui::Text("Recent Files:");
ImGui::BeginChild("RecentFiles", ImVec2(0, 100), true);
static RecentFilesManager manager("recent_files.txt");
manager.Load();
for (const auto &file : manager.GetRecentFiles()) {
if (gui::ClickableText(file.c_str())) {
status_ = OpenRomOrProject(file);
if (!status_.ok()) {
toast_manager_.Show(std::string(status_.message()), editor::ToastType::kError);
}
}
}
ImGui::EndChild();
ImGui::Spacing();
// Show editor access buttons for loaded sessions
bool has_loaded_sessions = false;
for (const auto& session : sessions_) {
if (session.rom.is_loaded()) {
has_loaded_sessions = true;
break;
}
}
if (has_loaded_sessions) {
ImGui::Spacing();
ImGui::Separator();
ImGui::Text("Available Editors:");
ImGui::Text("Click to open editor windows that can be docked side by side");
ImGui::Spacing();
// Show sessions and their editors
for (size_t session_idx = 0; session_idx < sessions_.size(); ++session_idx) {
const auto& session = sessions_[session_idx];
if (!session.rom.is_loaded()) continue;
ImGui::Text("Session: %s", session.GetDisplayName().c_str());
// Editor buttons in a grid layout for this session
if (ImGui::BeginTable(absl::StrFormat("EditorsTable##%zu", session_idx).c_str(), 4,
ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoHostExtendX)) {
// Row 1: Primary editors
ImGui::TableNextColumn();
if (ImGui::Button(absl::StrFormat(ICON_MD_MAP " Overworld##%zu", session_idx).c_str(), ImVec2(120, 30))) {
sessions_[session_idx].editors.overworld_editor_.set_active(true);
}
ImGui::TableNextColumn();
if (ImGui::Button(absl::StrFormat(ICON_MD_DOMAIN " Dungeon##%zu", session_idx).c_str(), ImVec2(120, 30))) {
sessions_[session_idx].editors.dungeon_editor_.set_active(true);
}
ImGui::TableNextColumn();
if (ImGui::Button(absl::StrFormat(ICON_MD_IMAGE " Graphics##%zu", session_idx).c_str(), ImVec2(120, 30))) {
sessions_[session_idx].editors.graphics_editor_.set_active(true);
}
ImGui::TableNextColumn();
if (ImGui::Button(absl::StrFormat(ICON_MD_PALETTE " Palette##%zu", session_idx).c_str(), ImVec2(120, 30))) {
sessions_[session_idx].editors.palette_editor_.set_active(true);
}
// Row 2: Secondary editors
ImGui::TableNextColumn();
if (ImGui::Button(absl::StrFormat(ICON_MD_MESSAGE " Message##%zu", session_idx).c_str(), ImVec2(120, 30))) {
sessions_[session_idx].editors.message_editor_.set_active(true);
}
ImGui::TableNextColumn();
if (ImGui::Button(absl::StrFormat(ICON_MD_PERSON " Sprite##%zu", session_idx).c_str(), ImVec2(120, 30))) {
sessions_[session_idx].editors.sprite_editor_.set_active(true);
}
ImGui::TableNextColumn();
if (ImGui::Button(absl::StrFormat(ICON_MD_MUSIC_NOTE " Music##%zu", session_idx).c_str(), ImVec2(120, 30))) {
sessions_[session_idx].editors.music_editor_.set_active(true);
}
ImGui::TableNextColumn();
if (ImGui::Button(absl::StrFormat(ICON_MD_MONITOR " Screen##%zu", session_idx).c_str(), ImVec2(120, 30))) {
sessions_[session_idx].editors.screen_editor_.set_active(true);
}
ImGui::EndTable();
}
if (session_idx < sessions_.size() - 1) {
ImGui::Spacing();
}
}
}
// Links section
ImGui::Spacing();
ImGui::Separator();
ImGui::Text("Help & Support:");
if (gui::ClickableText(ICON_MD_HELP " Getting Started Guide")) {
gui::OpenUrl("https://github.com/scawful/yaze/blob/master/docs/01-getting-started.md");
}
if (gui::ClickableText(ICON_MD_BUG_REPORT " Report Issues")) {
gui::OpenUrl("https://github.com/scawful/yaze/issues");
}
// Show tip about drag and drop
ImGui::Spacing();
ImGui::TextColored(ImVec4(0.6f, 0.8f, 1.0f, 1.0f), ICON_MD_TIPS_AND_UPDATES " Tip: Drag and drop ROM files onto the window");
}
ImGui::End();
}
} // namespace editor
} // namespace yaze