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
71 if (ImGui::GetCurrentContext() != nullptr) {
72 ImGui::GetIO().FontGlobalScale = user_settings_.prefs().font_global_scale;
73 }
74
77 auto& custom_object_manager = zelda3::CustomObjectManager::Get();
78 const auto custom_object_state =
79 BuildCustomObjectRuntimeState(current_project_);
80 if (session_coordinator_ != nullptr &&
81 session_coordinator_->GetActiveRomSession() != nullptr) {
82 custom_object_manager.ActivateRuntimeContext(
83 session_coordinator_->GetActiveRomSession()->session_id(),
84 custom_object_state);
85 } else {
86 custom_object_manager.RestoreState(custom_object_state);
87 }
88
96 } else {
98 }
99
105 const auto* session = session_coordinator_
106 ? session_coordinator_->GetActiveRomSession()
107 : nullptr;
108 project_management_panel_->SetProject(
109 &current_project_, session != nullptr && session->project_dirty);
112 }
113}
114
116 if (!session || !session->project_context.has_value()) {
117 if (!session) {
120 }
124 version_manager_ = nullptr;
126 project_management_panel_->SetVersionManager(nullptr);
127 }
128 return false;
129 }
130
131 if (!session->version_manager) {
132 session->version_manager =
133 std::make_unique<core::VersionManager>(&*session->project_context);
134 ConfigureSession(session);
135 }
137 version_manager_ = session->version_manager.get();
143 return true;
144}
145
166
167void EditorManager::HandleSessionSwitched(size_t new_index, RomSession* session,
168 bool transient) {
169 // Confirmation consent and Save As targets belong to the ROM that started
170 // the request. Never carry them across a user session switch. Frame-level
171 // context switches are temporary and must not consume the active request.
172 if (!transient) {
177 CancelPendingRomSave(/*hide_popups=*/true);
180 if (!RestoreProjectContextForSession(session) && session != nullptr) {
181 LOG_ERROR("EditorManager",
182 "Session %zu has no project context; saves are disabled",
183 new_index);
184 }
185 } else {
186 // Frame iteration must not copy full projects or reset project-wide caches.
187 // Feature flags are cheap and directly affect per-editor draw/save paths.
189 if (session) {
192 const auto custom_object_state =
193 session->project_context.has_value()
194 ? BuildCustomObjectRuntimeState(*session->project_context)
197 session->session_id(), custom_object_state);
198 } else {
201 }
203 }
204
205 // User switches and per-frame visits must select the same session's display
206 // cache. Its stable bitmap addresses also keep queued uploads/draws valid.
207 if (session) {
208 session->ActivateGraphicsDisplay();
209 } else {
210 gfx::Arena::Get().BindGraphicsSheets(nullptr, nullptr);
211 }
212
213 // Explicit navigation completes the gesture. Per-frame visits to another
214 // session must not split a held color-picker drag into separate undo steps.
215 if (!transient) {
217 }
218 // Palette edit history and dirty tracking are session-owned. Select the
219 // matching state before any session-specific editor or save action runs.
221 : nullptr);
222
223 // Update RightDrawerManager with the new session's settings editor
225 right_drawer_manager_->SetSettingsPanel(
226 session ? session->editors.GetSettingsPanel() : nullptr);
227 }
228
229 // Update properties panel with new ROM
230 if (session) {
231 if (!transient) {
233 }
235 } else {
238 }
239
240 // Update ContentRegistry context with current session's ROM and GameData
241 ContentRegistry::Context::SetRom(session ? &session->rom : nullptr);
243 : nullptr);
244
245 // current_editor_ is also session-owned. Resolve it from the newly active
246 // EditorSet, or clear it when the current category has no editor backing.
247 const std::string active_category = window_manager_.GetActiveCategory();
248 Editor* active_editor = ResolveEditorForCategory(active_category);
249 SetCurrentEditor(active_editor);
251 active_editor);
252
253 // Keep room/sprite labels in sync with active session context.
255
256 const std::string category = window_manager_.GetActiveCategory();
257 if (session != nullptr && !category.empty() &&
259 auto it = user_settings_.prefs().panel_visibility_state.find(category);
260 if (it != user_settings_.prefs().panel_visibility_state.end()) {
261 const size_t session_id = session ? session->session_id() : new_index;
262 window_manager_.RestoreVisibilityState(session_id, it->second);
263 }
264 }
265
266 // A user-facing ROM-session switch does not reactivate the editor through
267 // EditorActivator::SwitchToEditor. Refresh an existing layout context so
268 // optional panels opened afterward dock against this session's live tree.
269 // First-time layout construction must remain on the normal activation path,
270 // after EnsureEditorAssetsLoaded has initialized and registered the panels.
271 // Transient switches are frame-iteration implementation details and must not
272 // replace the user-facing session context.
273 const EditorType active_editor_type =
274 active_editor ? active_editor->type() : EditorType::kUnknown;
275 const size_t active_editor_index = EditorTypeIndex(active_editor_type);
276 const bool active_session_editor_initialized =
277 session != nullptr &&
278 active_editor_index < session->editor_initialized.size() &&
279 session->editor_initialized[active_editor_index];
280 if (!transient && active_editor != nullptr &&
281 EditorRegistry::IsPanelBasedEditor(active_editor_type) &&
282 active_session_editor_initialized &&
283 layout_manager_->IsLayoutInitialized(active_editor_type)) {
284 editor_activator_.InitializeEditorLayout(active_editor_type);
285 }
286
287#ifdef YAZE_ENABLE_TESTING
288 test::TestManager::Get().SetCurrentRom(session ? &session->rom : nullptr);
289#endif
290
291 // Restore this session's loaded/committed identity, never its pending bytes,
292 // before the next save-policy check (including transient activation).
294
295 LOG_DEBUG("EditorManager", "Session switched to %zu via EventBus", new_index);
296}
297
300 CancelPendingRomSave(/*hide_popups=*/true);
301 }
303 if (session && !session->project_context.has_value()) {
304 // A parsed project deliberately detaches the prior owner before creating
305 // its ROM session. Normal raw-ROM and empty-session opens must instead get
306 // a neutral context, not a copy of whichever project happened to be active.
307 if (!active_project_context_session_id_.has_value() &&
310 } else {
311 project::YazeProject neutral_project;
312 BindProjectContextToSession(session, neutral_project);
313 }
314 }
315
316 const size_t session_id = session ? session->session_id() : index;
320 index == session_coordinator_->GetActiveSessionIndex()) {
323 : nullptr);
325 }
326 LOG_INFO("EditorManager", "Session %zu created via EventBus", index);
327}
328
330 CancelPendingRomSave(/*hide_popups=*/true);
331
332 // SessionClosedEvent is emitted before the owning RomSession is erased and
333 // before the active index is adjusted. Bind the surviving session now so the
334 // closing session's destructor cannot leave PaletteManager unbound.
335 RomSession* palette_session = nullptr;
337 const size_t session_count = session_coordinator_->GetTotalSessionCount();
338 auto* active_session = session_coordinator_->GetActiveRomSession();
339 auto* closing_session =
340 session_coordinator_->IsValidSessionIndex(index)
341 ? static_cast<RomSession*>(session_coordinator_->GetSession(index))
342 : nullptr;
343 if (closing_session != nullptr) {
345 closing_session->session_id());
346 }
347 if (closing_session != nullptr &&
348 active_project_context_session_id_ == closing_session->session_id()) {
351 if (runtime_feature_flags_session_id_ == closing_session->session_id()) {
353 }
354 version_manager_ = nullptr;
356 project_management_panel_->SetVersionManager(nullptr);
357 }
358 }
359 palette_session = active_session;
360 if (closing_session != nullptr && active_session == closing_session &&
361 session_count > 1) {
362 const size_t replacement_index = index > 0 ? index - 1 : 1;
363 palette_session = static_cast<RomSession*>(
364 session_coordinator_->GetSession(replacement_index));
365 }
366 }
368 palette_session ? &palette_session->game_data : nullptr);
369
370 // Update ContentRegistry - it will be set to new active ROM on next switch
371 // If no sessions remain, clear the context
373 session_coordinator_->GetTotalSessionCount() == 0) {
375 }
376
377 // Avoid dangling/stale label map pointers after session close.
379
380#ifdef YAZE_ENABLE_TESTING
381 // Update test manager - it will get the new current ROM on next switch
383#endif
384
385 LOG_INFO("EditorManager", "Session %zu closed via EventBus", index);
386}
387
389 auto* session =
390 static_cast<RomSession*>(session_coordinator_->GetSession(index));
391 ResetAssetState(session);
392
393 // Update ContentRegistry when ROM is loaded (if this is the active session)
394 if (rom && session_coordinator_ &&
395 index == session_coordinator_->GetActiveSessionIndex()) {
397 // Also update GameData from the session
398 if (session) {
399 ContentRegistry::Context::SetGameData(&session->game_data);
400 }
402 }
403
404#ifdef YAZE_ENABLE_TESTING
405 if (rom) {
407 }
408#endif
409
410 LOG_INFO("EditorManager", "ROM loaded in session %zu via EventBus", index);
411}
412
415 return;
416 }
417
418 const size_t current_index = GetCurrentSessionIndex();
419 if (index == current_index ||
420 !session_coordinator_->IsValidSessionIndex(index)) {
421 return;
422 }
423
424 const size_t current_session_id = GetCurrentSessionId();
425 const size_t target_session_id = session_coordinator_->GetSessionId(index);
428 current_session_id, target_session_id})) {
429 return;
430 }
431
432 session_coordinator_->SwitchToSession(index);
433}
434
436 if (!session)
437 return;
439 ConfigureEditorDependencies(&session->editors, &session->rom,
440 session->editors.session_id());
441}
442
443// SessionScope implementation
445 size_t session_index)
446 : manager_(manager),
447 prev_rom_(manager->GetCurrentRom()),
448 prev_editor_set_(manager->GetCurrentEditorSet()),
449 prev_session_index_(manager->GetCurrentSessionIndex()) {
450 // Set new session context
451 manager_->session_coordinator_->SwitchToSession(session_index);
452}
453
455 // Restore previous context
456 manager_->session_coordinator_->SwitchToSession(prev_session_index_);
457}
458
459} // 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:49
static Flags & get()
Definition features.h:119
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::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_
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:249
EditorType type() const
Definition editor.h:343
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 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:239
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:287
std::string rom_backup_folder
Definition project.h:306
std::unordered_map< int, std::vector< std::string > > custom_object_files
Definition project.h:342
std::string custom_objects_folder
Definition project.h:327
bool project_opened() const
Definition project.h:505
WorkspaceSettings workspace_settings
Definition project.h:346
std::string GetAbsolutePath(const std::string &relative_path) const
Definition project.cc:2028
std::string dungeon_tile_patterns
Definition project.h:322
core::FeatureFlags::Flags feature_flags
Definition project.h:345
core::RomAddressOverrides rom_address_overrides
Definition project.h:351