yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
editor_manager_frame.cc
Go to the documentation of this file.
2
3#include <chrono>
4#include <string>
5#include <unordered_set>
6#include <utility>
7
8#include "absl/strings/str_format.h"
15#include "app/gui/core/icons.h"
16#include "app/platform/timing.h"
19
20#if defined(YAZE_BUILD_AGENT_UI)
22#endif
23
24namespace yaze {
25namespace editor {
26namespace {
27
28void UpdateBuildWorkflowStatus(StatusBar* status_bar,
29 ProjectManagementPanel* project_panel,
30 const ProjectWorkflowStatus& status) {
32 if (status_bar != nullptr) {
33 status_bar->SetBuildStatus(status);
34 }
35 if (project_panel != nullptr) {
36 project_panel->SetBuildStatus(status);
37 }
38}
39
40void AppendWorkflowHistoryEntry(const std::string& kind,
41 const ProjectWorkflowStatus& status,
42 const std::string& output_log) {
44 {.kind = kind,
45 .status = status,
46 .output_log = output_log,
47 .timestamp = std::chrono::system_clock::now()});
48}
49
50} // namespace
51
68absl::Status EditorManager::Update() {
72
73 return status_;
74}
75
77 // Space/focus transitions can leave mid-animation surfaces ghosted.
78 // Reset transient animation state to a stable endpoint.
81 right_drawer_manager_->OnHostVisibilityChanged(visible);
82 }
83}
84
86 // Update timing manager for accurate delta time across the application
88
89 // Execute keyboard shortcuts (registered via ShortcutConfigurator)
91}
92
94 status_ = absl::OkStatus();
96 if (ui_coordinator_) {
97 ui_coordinator_->RefreshWorkflowActions();
98 }
99 // Check for layout rebuild requests and execute if needed (delegated to LayoutCoordinator)
100 bool is_emulator_visible =
101 ui_coordinator_ && ui_coordinator_->IsEmulatorVisible();
102 EditorType current_type =
104 layout_coordinator_.ProcessLayoutRebuild(current_type, is_emulator_visible);
105
106 // Periodic user settings auto-save
107 if (settings_dirty_) {
108 const float elapsed = TimingManager::Get().GetElapsedTime();
109 if (elapsed - settings_dirty_timestamp_ >= 1.0f) {
110 auto save_status = user_settings_.Save();
111 if (!save_status.ok()) {
112 LOG_WARN("EditorManager", "Failed to save user settings: %s",
113 save_status.ToString().c_str());
114 }
115 settings_dirty_ = false;
116 }
117 }
118
119 // Update agent editor dashboard
120 status_ = agent_ui_->Update();
121
122 // Ensure TestManager always has the current ROM
123 static Rom* last_test_rom = nullptr;
124 auto* current_rom = GetCurrentRom();
125 if (last_test_rom != current_rom) {
126 LOG_DEBUG(
127 "EditorManager",
128 "EditorManager::Update - ROM changed, updating TestManager: %p -> %p",
129 (void*)last_test_rom, (void*)current_rom);
131 last_test_rom = current_rom;
132 }
133
134 // Autosave uses the same guarded serialization, validation, backup, and
135 // atomic commit pipeline as an explicit save. Writing Rom::SaveToFile here
136 // would skip editor model serialization and could persist a partially
137 // failed buffer while clearing its dirty bit.
138 const bool current_session_has_unsaved_work =
140 const auto* current_session =
141 session_coordinator_ ? session_coordinator_->GetActiveRomSession()
142 : nullptr;
143 const bool backup_restore_requires_explicit_save =
144 current_session != nullptr && current_session->backup_restore_pending;
146 current_session_has_unsaved_work &&
147 !backup_restore_requires_explicit_save) {
148 autosave_timer_ += ImGui::GetIO().DeltaTime;
150 autosave_timer_ = 0.0f;
151 auto st = AutosaveActiveSession();
152 if (st.ok()) {
153 toast_manager_.Show("Autosave completed", editor::ToastType::kSuccess);
154 } else if (absl::IsCancelled(st)) {
155 toast_manager_.Show("Autosave paused: confirmation required",
157 } else {
159 absl::StrFormat("Autosave failed: %s", st.message()),
161 }
162 }
163 } else {
164 autosave_timer_ = 0.0f;
165 }
166
167 // Update ROM context for agent UI
168 if (current_rom && current_rom->is_loaded()) {
169 agent_ui_->SetRomContext(current_rom);
170 agent_ui_->SetProjectContext(&current_project_);
171 if (auto* editor_set = GetCurrentEditorSet()) {
172 agent_ui_->SetAsarWrapperContext(editor_set->GetAsarWrapper());
173 // Backend-agnostic symbol feed. Works for ASAR and z3dk alike; tools
174 // prefer this pointer over reaching through the wrapper.
175 agent_ui_->SetAssemblySymbolTableContext(
176 &editor_set->GetAssemblySymbols());
177 }
178 }
179
180 // Delegate session updates to SessionCoordinator
182 session_coordinator_->UpdateSessions();
183 }
184}
185
188 return;
189 }
190
191 const auto snapshot = active_project_build_->GetSnapshot();
192 UpdateBuildWorkflowStatus(
195 snapshot.running
196 ? "Build running"
197 : (snapshot.status.ok() ? "Build succeeded" : "Build failed"),
198 snapshot.output_tail.empty()
199 ? (snapshot.running
200 ? std::string(
201 "Running the configured project build command")
202 : std::string(snapshot.status.message()))
203 : snapshot.output_tail,
204 snapshot.running
206 : (snapshot.status.ok() ? ProjectWorkflowState::kSuccess
208 snapshot.output_tail, snapshot.running));
210 project_management_panel_->SetBuildLogOutput(snapshot.output);
211 }
213
214 if (snapshot.running || active_project_build_reported_) {
215 return;
216 }
217
219 if (!snapshot.status.ok()) {
220 AppendWorkflowHistoryEntry(
221 "Build",
222 MakeBuildStatus("Build failed", std::string(snapshot.status.message()),
223 ProjectWorkflowState::kFailure, snapshot.output_tail,
224 false),
225 snapshot.output);
227 absl::StrFormat("Build failed: %s", snapshot.status.message()),
228 snapshot.status.code() == absl::StatusCode::kCancelled
231 return;
232 }
233
234 AppendWorkflowHistoryEntry(
235 "Build",
237 "Build succeeded",
238 snapshot.output_tail.empty() ? std::string("Project build completed")
239 : snapshot.output_tail,
240 ProjectWorkflowState::kSuccess, snapshot.output_tail, false),
241 snapshot.output);
242 toast_manager_.Show(snapshot.output_tail.empty()
243 ? "Project build completed"
244 : absl::StrFormat("Project build completed: %s",
245 snapshot.output_tail),
247}
248
250
251 // Draw editor selection dialog (managed by UICoordinator)
252 if (ui_coordinator_ && ui_coordinator_->IsEditorSelectionVisible()) {
253 dashboard_panel_->Show();
254 dashboard_panel_->Draw();
255 if (!dashboard_panel_->IsVisible()) {
256 ui_coordinator_->SetEditorSelectionVisible(false);
257 }
258 }
259
260 // Draw ROM load options dialog (ZSCustomOverworld, feature flags, project)
262 const bool finish_current_open =
265 CloseRomLoadOptions("its session or ROM file is no longer active");
266 if (finish_current_open) {
268 }
269 }
272 }
273
274 // Draw window browser (managed by UICoordinator)
275 if (ui_coordinator_ && ui_coordinator_->IsWindowBrowserVisible()) {
276 bool show = true;
277 if (activity_bar_) {
278 activity_bar_->DrawWindowBrowser(GetCurrentSessionId(), &show);
279 }
280 if (!show) {
281 ui_coordinator_->SetWindowBrowserVisible(false);
282 }
283 }
284
285 // Draw background grid effects
286 if (ui_coordinator_) {
287 ui_coordinator_->DrawBackground();
288 }
289
290 // Draw UICoordinator UI components (Welcome Screen, Command Palette, etc.)
291 if (ui_coordinator_) {
292 ui_coordinator_->DrawAllUI();
293 }
294
295 // Handle Welcome screen early-exit for rendering
296 if (ui_coordinator_ && ui_coordinator_->ShouldShowWelcome()) {
298 // Close drawers left over from an editor only when the Welcome screen
299 // replaces the editor surface. Closing them every frame made every
300 // drawer opened from the Welcome screen (File > Settings, Ctrl/Cmd+,,
301 // the drawers button) close again before it was drawn. The Welcome
302 // screen already lays itself out around the drawer width
303 // (GetRightLayoutOffset).
304 if (!welcome_was_shown_) {
305 right_drawer_manager_->CloseDrawer();
306 }
308 right_drawer_manager_->Draw();
309 }
310 welcome_was_shown_ = true;
311 return;
312 }
313 welcome_was_shown_ = false;
314
317 RunEmulator();
318
319 // Draw sidebar
320 if (ui_coordinator_ && ui_coordinator_->IsPanelSidebarVisible()) {
321 auto all_categories = EditorRegistry::GetAllEditorCategories();
322 std::unordered_set<std::string> active_editor_categories;
323
324 if (auto* current_editor_set = GetCurrentEditorSet()) {
326 for (size_t session_idx = 0;
327 session_idx < session_coordinator_->GetTotalSessionCount();
328 ++session_idx) {
329 auto* session = static_cast<RomSession*>(
330 session_coordinator_->GetSession(session_idx));
331 if (!session || !session->rom.is_loaded()) {
332 continue;
333 }
334
335 for (auto* editor : session->editors.active_editors_) {
336 if (*editor->active() && IsPanelBasedEditor(editor->type())) {
337 std::string category =
338 EditorRegistry::GetEditorCategory(editor->type());
339 active_editor_categories.insert(category);
340 }
341 }
342 }
343
344 if (ui_coordinator_->IsEmulatorVisible()) {
345 active_editor_categories.insert("Emulator");
346 }
347 }
348 }
349
350 const bool prefer_dashboard_only =
353 std::string sidebar_category = window_manager_.GetActiveCategory();
354 if (!prefer_dashboard_only && sidebar_category.empty() &&
355 !all_categories.empty()) {
356 sidebar_category = GetPreferredStartupCategory("", all_categories);
357 if (!sidebar_category.empty()) {
358 window_manager_.SetActiveCategory(sidebar_category, /*notify=*/false);
359 SyncEditorContextForCategory(sidebar_category);
361 sidebar_category);
362 if (it != user_settings_.prefs().panel_visibility_state.end()) {
364 window_manager_.GetActiveSessionId(), it->second);
365 }
366 }
367 }
368
369 auto has_rom_callback = [this]() -> bool {
370 auto* rom = GetCurrentRom();
371 return rom && rom->is_loaded();
372 };
373
374 if (activity_bar_ && ui_coordinator_->ShouldShowActivityBar()) {
375 auto is_rom_dirty_callback = [this]() -> bool {
376 auto* rom = GetCurrentRom();
377 return rom && rom->is_loaded() && rom->dirty();
378 };
379 auto pending_dungeon_rooms_callback = [this]() -> int {
380 if (auto* editor_set = GetCurrentEditorSet()) {
381 if (auto* dungeon_editor = editor_set->GetEditorAs<DungeonEditorV2>(
383 return dungeon_editor->PendingRoomCount();
384 }
385 }
386 return 0;
387 };
388 activity_bar_->Render(GetCurrentSessionId(), sidebar_category,
389 all_categories, active_editor_categories,
390 has_rom_callback, is_rom_dirty_callback,
391 pending_dungeon_rooms_callback);
392 }
393 }
394
395 // Draw right panel
398 right_drawer_manager_->Draw();
399 }
400
401 // Update and draw status bar
404 const size_t session_index = GetCurrentSessionIndex();
405 const size_t session_count = session_coordinator_->GetActiveSessionCount();
407 session_index, session_count,
408 session_count > 1 ? session_coordinator_->GetActiveSessionDisplayName()
409 : std::string{});
410 }
411
412 bool has_agent_info = false;
413#if defined(YAZE_BUILD_AGENT_UI)
414 if (auto* agent_editor = agent_ui_->GetAgentEditor()) {
415 auto* chat = agent_editor->GetAgentChat();
416 const auto* ctx = agent_ui_->GetContext();
417 if (ctx) {
418 const auto& config = ctx->agent_config();
419 bool active = chat && *chat->active();
420 status_bar_.SetAgentInfo(config.ai_provider, config.ai_model, active);
421 has_agent_info = true;
422 }
423 }
424#endif
425 if (!has_agent_info) {
427 }
430 }
431
432 // Editor-aware context: let the active editor push its own mode/custom
433 // segments for this frame without wiping event-driven cursor/selection/zoom
434 // state that older editors still publish through the event bus.
436 if (current_editor_) {
438
439 StatusBarSegmentOptions editor_opts;
440 editor_opts.tooltip = "Click to switch editor (Ctrl+E)";
441 editor_opts.on_click = [this]() {
442 if (ui_coordinator_) {
443 ui_coordinator_->ShowEditorSelection();
444 }
445 };
448 std::move(editor_opts));
449 } else {
451 }
452
453 const size_t session_index = GetCurrentSessionIndex();
454 if (SessionHasPendingUnsavedWork(session_index)) {
455 StatusBarSegmentOptions dirty_opts;
456 dirty_opts.tooltip = absl::StrFormat(
457 "%s. Click to save.", DescribePendingUnsavedWork(session_index));
458 dirty_opts.on_click = [this]() {
459 auto status = SaveCurrentSession();
460 if (!absl::IsCancelled(status) && !status.ok()) {
461 toast_manager_.Show(std::string(status.message()), ToastType::kError);
462 }
463 };
465 std::move(dirty_opts));
466 } else {
468 }
469
470 if (right_drawer_manager_ && right_drawer_manager_->IsDrawerExpanded()) {
471 const auto active = right_drawer_manager_->GetActiveDrawer();
473 StatusBarSegmentOptions drawer_opts;
474 drawer_opts.tooltip =
475 "Right sidebar open — Esc closes, View > Right Sidebar toggles";
477 std::move(drawer_opts));
478 }
479 }
480
482
483 // Check if ROM is loaded before drawing panels
484 auto* current_editor_set = GetCurrentEditorSet();
485 if (!current_editor_set || !GetCurrentRom()) {
486 if (window_manager_.GetActiveCategory() == "Agent") {
488 }
489 return;
490 }
491
492 // Central workspace window drawing
494
495 // The Performance Dashboard is rendered by DrawSecondaryWindows below, which
496 // also drives Update() and syncs the visibility flag back. Rendering here as
497 // well gave a second Begin/End on the same window name every frame, stacking
498 // duplicate metrics, memory blocks and frame plots with colliding plot IDs.
499
500 // Draw SessionCoordinator UI components
502 session_coordinator_->DrawSessionSwitcher();
503 session_coordinator_->DrawSessionManager();
504 session_coordinator_->DrawSessionRenameDialog();
505 }
506}
507
509 if (ImGui::BeginMenuBar()) {
510 const bool sidebar_visible = window_manager_.IsSidebarVisible();
511 const char* icon = sidebar_visible ? ICON_MD_MENU_OPEN : ICON_MD_MENU;
512 const char* tooltip = sidebar_visible ? "Hide Activity Bar (Ctrl+B)"
513 : "Show Activity Bar (Ctrl+B)";
514 if (ui_coordinator_ && ui_coordinator_->DrawMenuBarIconButton(
515 icon, tooltip, sidebar_visible)) {
517 }
518
519 // Delegate menu building to MenuOrchestrator
520 if (menu_orchestrator_) {
521 menu_orchestrator_->BuildMainMenu();
522 }
523
524 // Delegate menu bar extras to UICoordinator
525 if (ui_coordinator_) {
526 ui_coordinator_->DrawMenuBarExtras();
527 }
528
529 ImGui::EndMenuBar();
530 }
531}
532
534
535 // ImGui debug windows
536 if (ui_coordinator_) {
537 if (ui_coordinator_->IsImGuiDemoVisible()) {
538 bool visible = true;
539 ImGui::ShowDemoWindow(&visible);
540 if (!visible)
541 ui_coordinator_->SetImGuiDemoVisible(false);
542 }
543
544 if (ui_coordinator_->IsImGuiMetricsVisible()) {
545 bool visible = true;
546 ImGui::ShowMetricsWindow(&visible);
547 if (!visible)
548 ui_coordinator_->SetImGuiMetricsVisible(false);
549 }
550 }
551
552 // Legacy window-based editors
553 if (auto* editor_set = GetCurrentEditorSet()) {
554 bool* hex_visibility =
555 window_manager_.GetWindowVisibilityFlag("memory.hex_editor");
556 if (hex_visibility) {
557 if (auto* editor = editor_set->GetEditor(EditorType::kHex)) {
558 // Keep the legacy panel visibility flag in sync with the window close
559 // button (ImGui::Begin will toggle Editor::active_).
560 editor->set_active(*hex_visibility);
561 editor->Update();
562 *hex_visibility = *editor->active();
563 }
564 }
565
566 if (ui_coordinator_ && ui_coordinator_->IsAsmEditorVisible()) {
567 if (auto* editor = editor_set->GetEditor(EditorType::kAssembly)) {
568 editor->set_active(true);
569 editor->Update();
570 if (!*editor->active()) {
571 ui_coordinator_->SetAsmEditorVisible(false);
572 }
573 }
574 }
575 }
576
577 // Project and performance tools
580
581 if (ui_coordinator_ && ui_coordinator_->IsPerformanceDashboardVisible()) {
585 if (!gfx::PerformanceDashboard::Get().IsVisible()) {
586 ui_coordinator_->SetPerformanceDashboardVisible(false);
587 }
588 }
589
590#ifdef YAZE_ENABLE_TESTING
591 if (show_test_dashboard_) {
593 test::TestManager::Get().DrawTestDashboard(&show_test_dashboard_);
594 }
595#endif
596}
597
599 // Update proposal drawer context
601
602 // Agent UI popups
603 agent_ui_->DrawPopups();
604
605 // Resource label management
606 if (ui_coordinator_ && ui_coordinator_->IsResourceLabelManagerVisible() &&
607 GetCurrentRom()) {
608 bool visible = true;
614 }
615 if (!visible)
616 ui_coordinator_->SetResourceLabelManagerVisible(false);
617 }
618
619 // Layout presets
620 if (ui_coordinator_) {
621 ui_coordinator_->DrawLayoutPresets();
622 }
623}
624
626 auto* current_rom = GetCurrentRom();
627 if (!current_rom)
628 return;
629
630 // Visible emulator: lazy-init + render + tick via Run(rom).
631 // Hidden emulator: pause by default so switching editors does not keep game
632 // audio/frame state advancing. Opt-in background keep-alive is a user pref.
633 // MusicPlayer drives its own RunAudioFrame path when needed.
634 if (ui_coordinator_ && ui_coordinator_->IsEmulatorVisible()) {
635 emulator_.Run(current_rom);
636 return;
637 }
638
640 return;
641 }
644 return;
645 }
646
649 } else {
651 }
652}
653
654} // namespace editor
655} // 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:52
project::ResourceLabelManager * resource_label()
Definition rom.h:242
bool dirty() const
Definition rom.h:200
bool is_loaded() const
Definition rom.h:199
static TimingManager & Get()
Definition timing.h:20
float Update()
Update the timing manager (call once per frame)
Definition timing.h:29
float GetElapsedTime() const
Get total elapsed time since first update.
Definition timing.h:70
DungeonEditorV2 - Simplified dungeon editor using component delegation.
std::optional< RomLoadOptionsOwner > rom_load_options_owner_
std::unique_ptr< SessionCoordinator > session_coordinator_
std::unique_ptr< AgentUiController > agent_ui_
project::YazeProject current_project_
std::unique_ptr< RightDrawerManager > right_drawer_manager_
std::string GetPreferredStartupCategory(const std::string &saved_category, const std::vector< std::string > &available_categories) const
Rom * GetCurrentRom() const override
std::unique_ptr< DashboardPanel > dashboard_panel_
std::string CompactPendingUnsavedWorkLabel(size_t session_index) const
void SyncEditorContextForCategory(const std::string &category)
LayoutCoordinator layout_coordinator_
auto GetCurrentEditorSet() const -> EditorSet *
std::string DescribePendingUnsavedWork(size_t session_index) const
std::unique_ptr< MenuOrchestrator > menu_orchestrator_
ProjectFileEditor project_file_editor_
void HandleHostVisibilityChanged(bool visible)
WorkspaceWindowManager window_manager_
RomLoadOptionsDialog rom_load_options_dialog_
std::vector< std::string > startup_panel_hints_
StartupVisibility dashboard_mode_override_
static bool IsPanelBasedEditor(EditorType type)
absl::Status Update()
Main update loop for the editor application.
size_t GetCurrentSessionId() const
Stable workspace identity; unlike the UI index, it does not compact.
std::unique_ptr< ActivityBar > activity_bar_
ShortcutManager shortcut_manager_
bool SessionHasPendingUnsavedWork(size_t session_index) const
std::unique_ptr< BackgroundCommandTask > active_project_build_
std::unique_ptr< ProjectManagementPanel > project_management_panel_
ProjectWorkflowStatus MakeBuildStatus(const std::string &summary, const std::string &detail, ProjectWorkflowState state, const std::string &output_tail="", bool can_cancel=false) const
void CloseRomLoadOptions(const char *reason)
std::unique_ptr< UICoordinator > ui_coordinator_
static std::vector< std::string > GetAllEditorCategories()
Get all editor categories in display order for sidebar.
static std::string GetEditorCategory(EditorType type)
virtual void ContributeStatus(StatusBar *)
Definition editor.h:333
EditorType type() const
Definition editor.h:355
void ProcessLayoutRebuild(EditorType current_editor_type, bool is_emulator_visible)
Process pending layout rebuild requests.
Panel for managing project settings, ROM versions, and snapshots.
void SetBuildStatus(const ProjectWorkflowStatus &status)
void Draw(bool *p_open)
Draw the dialog (wrapper around Show)
A session-aware status bar displayed at the bottom of the application.
Definition status_bar.h:57
void SetBuildStatus(const ProjectWorkflowStatus &status)
Definition status_bar.h:209
void SetSessionInfo(size_t session_id, size_t total_sessions, const std::string &display_name={})
Set session information.
void SetRom(Rom *rom)
Set the current ROM for dirty status and filename display.
Definition status_bar.h:77
void SetActiveEditor(const std::string &name)
Set the active editor category/name for the context strip.
void SetDirtyScope(const std::string &short_label)
Compact dirty-scope chip (ROM / Project / Rooms / …).
void SetCustomSegment(const std::string &key, const std::string &value)
Set a custom segment with key-value pair.
void ClearEditorContributions()
Clear frame-scoped editor contributions.
void Draw()
Draw the status bar.
void SetAgentInfo(const std::string &provider, const std::string &model, bool active)
void Show(const std::string &message, ToastType type=ToastType::kInfo, float ttl_seconds=3.0f, std::string action_label={}, std::function< void()> on_action=nullptr)
void RestoreVisibilityState(size_t session_id, const std::unordered_map< std::string, bool > &state, bool publish_events=false)
Restore panel visibility state from persistence.
void SetActiveCategory(const std::string &category, bool notify=true)
bool * GetWindowVisibilityFlag(size_t session_id, const std::string &base_window_id)
bool is_audio_focus_mode() const
Definition emulator.h:111
bool is_snes_initialized() const
Definition emulator.h:129
void Run(Rom *rom)
Definition emulator.cc:469
auto running() const -> bool
Definition emulator.h:61
static PerformanceDashboard & Get()
void SetVisible(bool visible)
Show/hide the dashboard.
void Update()
Update dashboard with current performance data.
void Render()
Render the performance dashboard UI.
void ClearWorkspaceTransitionState()
Definition animator.cc:89
void SetCurrentRom(Rom *rom)
void DrawTestDashboard(bool *show_dashboard=nullptr)
static TestManager & Get()
#define ICON_MD_MENU
Definition icons.h:1196
#define ICON_MD_MENU_OPEN
Definition icons.h:1198
#define LOG_DEBUG(category, format,...)
Definition log.h:133
#define LOG_WARN(category, format,...)
Definition log.h:138
void AppendWorkflowHistory(const ProjectWorkflowHistoryEntry &entry)
void SetBuildWorkflowStatus(const ProjectWorkflowStatus &status)
void SetBuildWorkflowLog(const std::string &output)
const char * GetDrawerTypeName(RightDrawerManager::DrawerType type)
bool ShouldTickEmulatorWhenHidden(bool keep_running_in_background)
void ExecuteShortcuts(const ShortcutManager &shortcut_manager)
Fire at most one shortcut for this frame's key press.
Animator & GetAnimator()
Definition animator.cc:318
Represents a single session, containing a ROM and its associated editors.
Optional behavior for an interactive status bar segment.
Definition status_bar.h:27
std::function< void()> on_click
Definition status_bar.h:28
std::unordered_map< std::string, std::unordered_map< std::string, bool > > panel_visibility_state
bool project_opened() const
Definition project.h:538
std::string labels_filename
Definition project.h:317