yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
editor_manager_sessions.cc
Go to the documentation of this file.
2
3#include <memory>
4#include <string>
5
8#include "core/features.h"
9#include "core/rom_settings.h"
12
13#ifdef YAZE_ENABLE_TESTING
15#endif
16
17namespace yaze::editor {
18namespace {
19
21 const project::YazeProject& project) {
22 return {
23 .base_path = project.custom_objects_folder.empty()
24 ? ""
25 : project.GetAbsolutePath(project.custom_objects_folder),
26 .custom_file_map = project.custom_object_files,
27 .dungeon_tile_patterns =
28 project.dungeon_tile_patterns.empty()
29 ? ""
30 : project.GetAbsolutePath(project.dungeon_tile_patterns),
31 };
32}
33
34} // namespace
35
37 RomSession* session, const project::YazeProject& project) {
38 if (!session) {
39 return;
40 }
41 const bool needs_version_manager =
42 !session->project_context.has_value() || !session->version_manager;
43 if (session->project_context.has_value()) {
44 *session->project_context = project;
45 } else {
46 session->project_context.emplace(project);
47 }
48 if (needs_version_manager) {
49 session->version_manager =
50 std::make_unique<core::VersionManager>(&*session->project_context);
51 }
52 session->feature_flags = project.feature_flags;
53 // Configure after the stable context and VersionManager exist. EditorSet
54 // instances are created before SessionCreatedEvent, so their first
55 // dependency pass can legitimately have no project yet.
56 ConfigureSession(session);
57}
58
62
65 overrides.font_global_scale =
67 overrides.autosave_enabled =
69 overrides.autosave_interval =
71 overrides.backup_before_save =
74 } else {
75 // A raw ROM has no workspace settings of its own. Copying the default
76 // project values here used to turn autosave on and save it globally.
78 }
79 if (ImGui::GetCurrentContext() != nullptr) {
80 ImGui::GetIO().FontGlobalScale = user_settings_.prefs().font_global_scale;
81 }
82
85 auto& custom_object_manager = zelda3::CustomObjectManager::Get();
86 const auto custom_object_state =
87 BuildCustomObjectRuntimeState(current_project_);
88 if (session_coordinator_ != nullptr &&
89 session_coordinator_->GetActiveRomSession() != nullptr) {
90 custom_object_manager.ActivateRuntimeContext(
91 session_coordinator_->GetActiveRomSession()->session_id(),
92 custom_object_state);
93 } else {
94 custom_object_manager.RestoreState(custom_object_state);
95 }
96
104 } else {
106 }
107
113 const auto* session = session_coordinator_
114 ? session_coordinator_->GetActiveRomSession()
115 : nullptr;
116 project_management_panel_->SetProject(
117 &current_project_, session != nullptr && session->project_dirty);
120 }
121}
122
124 if (!session || !session->project_context.has_value()) {
125 if (!session) {
127 // Closing the last ROM ends the project's workspace overlay. Left
128 // active, Save() would keep writing the pre-project values over global
129 // changes made with no ROM open.
131 if (ImGui::GetCurrentContext() != nullptr) {
132 ImGui::GetIO().FontGlobalScale =
134 }
136 }
140 version_manager_ = nullptr;
142 project_management_panel_->SetVersionManager(nullptr);
143 }
144 return false;
145 }
146
147 if (!session->version_manager) {
148 session->version_manager =
149 std::make_unique<core::VersionManager>(&*session->project_context);
150 ConfigureSession(session);
151 }
153 version_manager_ = session->version_manager.get();
159 return true;
160}
161
182
183void EditorManager::HandleSessionSwitched(size_t new_index, RomSession* session,
184 bool transient) {
185 // Confirmation consent and Save As targets belong to the ROM that started
186 // the request. Never carry them across a user session switch. Frame-level
187 // context switches are temporary and must not consume the active request.
188 if (!transient) {
190 (!session ||
191 session->session_id() != rom_load_options_owner_->session_id)) {
192 CloseRomLoadOptions("another session became active");
193 }
198 CancelPendingRomSave(/*hide_popups=*/true);
201 if (!RestoreProjectContextForSession(session) && session != nullptr) {
202 LOG_ERROR("EditorManager",
203 "Session %zu has no project context; saves are disabled",
204 new_index);
205 }
206 } else {
207 // Frame iteration must not copy full projects or reset project-wide caches.
208 // Feature flags are cheap and directly affect per-editor draw/save paths.
210 if (session) {
213 const auto custom_object_state =
214 session->project_context.has_value()
215 ? BuildCustomObjectRuntimeState(*session->project_context)
218 session->session_id(), custom_object_state);
219 } else {
222 }
224 }
225
226 // User switches and per-frame visits must select the same session's display
227 // cache. Its stable bitmap addresses also keep queued uploads/draws valid.
228 if (session) {
229 session->ActivateGraphicsDisplay();
230 } else {
231 gfx::Arena::Get().BindGraphicsSheets(nullptr, nullptr);
232 }
233
234 // Explicit navigation completes the gesture. Per-frame visits to another
235 // session must not split a held color-picker drag into separate undo steps.
236 if (!transient) {
238 }
239 // Palette edit history and dirty tracking are session-owned. Select the
240 // matching state before any session-specific editor or save action runs.
242 : nullptr);
243
244 // Update RightDrawerManager with the new session's settings editor
246 right_drawer_manager_->SetSettingsPanel(
247 session ? session->editors.GetSettingsPanel() : nullptr);
248 }
249
250 // Update properties panel with new ROM
251 if (session) {
252 if (!transient) {
254 }
256 } else {
259 }
260
261 // Update ContentRegistry context with current session's ROM and GameData
262 ContentRegistry::Context::SetRom(session ? &session->rom : nullptr);
264 : nullptr);
265
266 // current_editor_ is also session-owned. Resolve it from the newly active
267 // EditorSet, or clear it when the current category has no editor backing.
268 const std::string active_category = window_manager_.GetActiveCategory();
269 Editor* active_editor = ResolveEditorForCategory(active_category);
270 SetCurrentEditor(active_editor);
272 active_editor);
273
274 // Keep room/sprite labels in sync with active session context.
276
277 const std::string category = window_manager_.GetActiveCategory();
278 // Per-frame visits must keep the live visibility flags. Replaying saved
279 // preferences here resurrects windows immediately after an ImGui close.
280 if (!transient && session != nullptr && !category.empty() &&
282 auto it = user_settings_.prefs().panel_visibility_state.find(category);
283 if (it != user_settings_.prefs().panel_visibility_state.end()) {
284 const size_t session_id = session ? session->session_id() : new_index;
285 window_manager_.RestoreVisibilityState(session_id, it->second);
286 }
287 }
288
289 // A user-facing ROM-session switch does not reactivate the editor through
290 // EditorActivator::SwitchToEditor. Refresh an existing layout context so
291 // optional panels opened afterward dock against this session's live tree.
292 // First-time layout construction must remain on the normal activation path,
293 // after EnsureEditorAssetsLoaded has initialized and registered the panels.
294 // Transient switches are frame-iteration implementation details and must not
295 // replace the user-facing session context.
296 const EditorType active_editor_type =
297 active_editor ? active_editor->type() : EditorType::kUnknown;
298 const size_t active_editor_index = EditorTypeIndex(active_editor_type);
299 const bool active_session_editor_initialized =
300 session != nullptr &&
301 active_editor_index < session->editor_initialized.size() &&
302 session->editor_initialized[active_editor_index];
303 if (!transient && active_editor != nullptr &&
304 EditorRegistry::IsPanelBasedEditor(active_editor_type) &&
305 active_session_editor_initialized &&
306 layout_manager_->IsLayoutInitialized(active_editor_type)) {
307 editor_activator_.InitializeEditorLayout(active_editor_type);
308 }
309
310#ifdef YAZE_ENABLE_TESTING
311 test::TestManager::Get().SetCurrentRom(session ? &session->rom : nullptr);
312#endif
313
314 // Restore this session's loaded/committed identity, never its pending bytes,
315 // before the next save-policy check (including transient activation).
317
318 LOG_DEBUG("EditorManager", "Session switched to %zu via EventBus", new_index);
319}
320
323 CancelPendingRomSave(/*hide_popups=*/true);
324 }
326 if (session && !session->project_context.has_value()) {
327 // A parsed project deliberately detaches the prior owner before creating
328 // its ROM session. Normal raw-ROM and empty-session opens must instead get
329 // a neutral context, not a copy of whichever project happened to be active.
330 if (!active_project_context_session_id_.has_value() &&
333 } else {
334 project::YazeProject neutral_project;
335 BindProjectContextToSession(session, neutral_project);
336 }
337 }
338
339 const size_t session_id = session ? session->session_id() : index;
343 index == session_coordinator_->GetActiveSessionIndex()) {
346 : nullptr);
348 }
349 LOG_INFO("EditorManager", "Session %zu created via EventBus", index);
350}
351
353 CancelPendingRomSave(/*hide_popups=*/true);
354
355 // SessionClosedEvent is emitted before the owning RomSession is erased and
356 // before the active index is adjusted. Bind the surviving session now so the
357 // closing session's destructor cannot leave PaletteManager unbound.
358 RomSession* palette_session = nullptr;
360 const size_t session_count = session_coordinator_->GetTotalSessionCount();
361 auto* active_session = session_coordinator_->GetActiveRomSession();
362 auto* closing_session =
363 session_coordinator_->IsValidSessionIndex(index)
364 ? static_cast<RomSession*>(session_coordinator_->GetSession(index))
365 : nullptr;
366 if (closing_session && rom_load_options_owner_ &&
367 closing_session->session_id() == rom_load_options_owner_->session_id) {
368 CloseRomLoadOptions("its ROM was closed");
369 }
370 if (closing_session != nullptr) {
372 closing_session->session_id());
373 }
374 if (closing_session != nullptr &&
375 active_project_context_session_id_ == closing_session->session_id()) {
378 if (runtime_feature_flags_session_id_ == closing_session->session_id()) {
380 }
381 version_manager_ = nullptr;
383 project_management_panel_->SetVersionManager(nullptr);
384 }
385 }
386 palette_session = active_session;
387 if (closing_session != nullptr && active_session == closing_session &&
388 session_count > 1) {
389 const size_t replacement_index = index > 0 ? index - 1 : 1;
390 palette_session = static_cast<RomSession*>(
391 session_coordinator_->GetSession(replacement_index));
392 }
393 }
395 palette_session ? &palette_session->game_data : nullptr);
396
397 // Update ContentRegistry - it will be set to new active ROM on next switch
398 // If no sessions remain, clear the context
400 session_coordinator_->GetTotalSessionCount() == 0) {
402 }
403
404 // Avoid dangling/stale label map pointers after session close.
406
407#ifdef YAZE_ENABLE_TESTING
408 // Update test manager - it will get the new current ROM on next switch
410#endif
411
412 LOG_INFO("EditorManager", "Session %zu closed via EventBus", index);
413}
414
416 auto* session =
417 static_cast<RomSession*>(session_coordinator_->GetSession(index));
418 if (session && rom_load_options_owner_ &&
419 session->session_id() == rom_load_options_owner_->session_id) {
420 CloseRomLoadOptions("the ROM was reloaded");
421 }
422 ResetAssetState(session);
423
424 // Update ContentRegistry when ROM is loaded (if this is the active session)
425 if (rom && session_coordinator_ &&
426 index == session_coordinator_->GetActiveSessionIndex()) {
429 // Also update GameData from the session
430 if (session) {
431 ContentRegistry::Context::SetGameData(&session->game_data);
432 }
434 }
435
436#ifdef YAZE_ENABLE_TESTING
437 if (rom) {
439 }
440#endif
441
442 LOG_INFO("EditorManager", "ROM loaded in session %zu via EventBus", index);
443}
444
447 return;
448 }
449
450 const size_t current_index = GetCurrentSessionIndex();
451 if (index == current_index ||
452 !session_coordinator_->IsValidSessionIndex(index)) {
453 return;
454 }
455
456 const size_t current_session_id = GetCurrentSessionId();
457 const size_t target_session_id = session_coordinator_->GetSessionId(index);
460 current_session_id, target_session_id})) {
461 return;
462 }
463
464 session_coordinator_->SwitchToSession(index);
465}
466
468 if (!session)
469 return;
471 ConfigureEditorDependencies(&session->editors, &session->rom,
472 session->editors.session_id());
473}
474
475// SessionScope implementation
477 size_t session_index)
478 : manager_(manager),
479 prev_rom_(manager->GetCurrentRom()),
480 prev_editor_set_(manager->GetCurrentEditorSet()),
481 prev_session_index_(manager->GetCurrentSessionIndex()) {
482 // Set new session context
483 manager_->session_coordinator_->SwitchToSession(session_index);
484}
485
487 // Restore previous context
488 manager_->session_coordinator_->SwitchToSession(prev_session_index_);
489}
490
491} // namespace yaze::editor
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
Definition rom.h:52
static Flags & get()
Definition features.h:122
static RomSettings & Get()
void SetAddressOverrides(const RomAddressOverrides &overrides)
void InitializeEditorLayout(EditorType type)
Initialize the DockBuilder layout for an editor.
SessionScope(EditorManager *manager, size_t session_index)
The EditorManager controls the main editor window and manages the various editor classes.
std::optional< RomLoadOptionsOwner > rom_load_options_owner_
std::unique_ptr< SessionCoordinator > session_coordinator_
RomLifecycleManager rom_lifecycle_
std::optional< PendingRomSave > pending_rom_save_
void RestoreProjectEditingStateForSession(RomSession *session)
bool MaybeGuardPendingSessionAction(PendingUnsavedSessionAction action)
project::YazeProject current_project_
void HandleSessionSwitched(size_t new_index, RomSession *session, bool transient=false)
std::unique_ptr< RightDrawerManager > right_drawer_manager_
void SetCurrentEditor(Editor *editor) override
Rom * GetCurrentRom() const override
void CancelPendingRomSave(bool hide_popups=false)
std::unique_ptr< LayoutManager > layout_manager_
void ResetAssetState(RomSession *session)
core::VersionManager * version_manager_
Editor * ResolveEditorForCategory(const std::string &category)
std::optional< size_t > runtime_feature_flags_session_id_
void HandleSessionRomLoaded(size_t index, Rom *rom)
ProjectFileEditor project_file_editor_
WorkspaceWindowManager window_manager_
std::optional< size_t > pending_project_open_previous_session_id_
bool PendingRomSaveMatchesActiveSession() const
std::optional< size_t > active_project_context_session_id_
void BindProjectContextToSession(RomSession *session, const project::YazeProject &project)
size_t GetCurrentSessionId() const
Stable workspace identity; unlike the UI index, it does not compact.
SelectionPropertiesPanel selection_properties_panel_
void ConfigureSession(RomSession *session) override
void ConfigureEditorDependencies(EditorSet *editor_set, Rom *rom, size_t session_id)
std::unique_ptr< ProjectManagementPanel > project_management_panel_
void CloseRomLoadOptions(const char *reason)
bool RestoreProjectContextForSession(RomSession *session)
EditorActivator editor_activator_
std::optional< PendingProjectRomSelection > pending_project_rom_selection_
void HandleSessionCreated(size_t index, RomSession *session)
static bool IsPanelBasedEditor(EditorType type)
size_t session_id() const
void set_user_settings(UserSettings *settings)
SettingsPanel * GetSettingsPanel() const
Interface for editor classes.
Definition editor.h:252
EditorType type() const
Definition editor.h:355
void RestoreState(const ProjectFileEditorState &state, project::YazeProject *project)
void ResetForProject(project::YazeProject *project)
void SetProjectContext(const project::YazeProject *project)
void ApplyDefaultBackupPolicy(bool enabled, const std::string &folder, int retention_count, bool keep_daily, int keep_daily_days)
Apply default backup policy from user settings.
void ClearSelection()
Clear the current selection.
void ApplyWorkspaceOverrides(const WorkspaceOverrides &overrides)
void RestoreVisibilityState(size_t session_id, const std::unordered_map< std::string, bool > &state, bool publish_events=false)
Restore panel visibility state from persistence.
static constexpr const char * kDashboardCategory
void RegisterRegistryWindowContentsForSession(size_t session_id)
Register descriptors for all registry window contents in a session.
void RestorePinnedState(const std::unordered_map< std::string, bool > &state)
Restore pinned panel state from persistence.
void BindGraphicsSheets(GraphicsSheets *sheets, const void *owner)
Definition arena.cc:522
static Arena & Get()
Definition arena.cc:24
void ActivateSession(zelda3::GameData *game_data)
Select the palette state for a ROM session without resnapshotting it.
void CommitColorEdit(uint64_t caller_id=0)
static PaletteManager & Get()
Get the singleton instance.
void SetCurrentRom(Rom *rom)
static TestManager & Get()
void RemoveRuntimeContext(uint64_t context_id)
static CustomObjectManager & Get()
void ActivateRuntimeContext(uint64_t context_id, const State &state)
static DrawRoutineRegistry & Get()
#define LOG_DEBUG(category, format,...)
Definition log.h:133
#define LOG_ERROR(category, format,...)
Definition log.h:140
#define LOG_INFO(category, format,...)
Definition log.h:136
void SetEditorWindowContext(const std::string &category, Editor *editor)
void SetRom(Rom *rom)
Set the current ROM instance.
void SetGameData(::yaze::zelda3::GameData *data)
Set the current game data instance.
void Clear()
Clear all context state.
void SetCurrentProject(::yaze::project::YazeProject *project)
Set the current project instance.
zelda3::CustomObjectManager::State BuildCustomObjectRuntimeState(const project::YazeProject &project)
Editors are the view controllers for the application.
size_t EditorTypeIndex(EditorType type)
Definition editor.h:242
Represents a single session, containing a ROM and its associated editors.
core::FeatureFlags::Flags feature_flags
ProjectFileEditorState project_file_editor_state
std::array< bool, kEditorTypeCount > editor_initialized
zelda3::GameData game_data
std::unique_ptr< core::VersionManager > version_manager
std::optional< project::YazeProject > project_context
std::unordered_map< std::string, std::unordered_map< std::string, bool > > panel_visibility_state
std::unordered_map< std::string, bool > pinned_panels
Modern project structure with comprehensive settings consolidation.
Definition project.h:290
std::string rom_backup_folder
Definition project.h:309
std::unordered_map< int, std::vector< std::string > > custom_object_files
Definition project.h:358
std::string custom_objects_folder
Definition project.h:330
bool project_opened() const
Definition project.h:538
WorkspaceSettings workspace_settings
Definition project.h:362
std::string GetAbsolutePath(const std::string &relative_path) const
Definition project.cc:2675
std::string dungeon_tile_patterns
Definition project.h:325
core::FeatureFlags::Flags feature_flags
Definition project.h:361
core::RomAddressOverrides rom_address_overrides
Definition project.h:367