yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
ui_coordinator.cc
Go to the documentation of this file.
2#include "util/i18n/tr.h"
3
4#include <algorithm>
5#include <filesystem>
6#include <functional>
7#include <memory>
8#include <string>
9#include <vector>
10
11#include "absl/strings/match.h"
12#include "absl/strings/str_format.h"
13
14#ifdef __EMSCRIPTEN__
15#include <emscripten.h>
16#endif
17#include "app/application.h"
19#include "app/editor/editor.h"
33#include "app/gui/core/icons.h"
34#include "app/gui/core/input.h"
36#include "app/gui/core/style.h"
41#include "core/project.h"
42#include "imgui/imgui.h"
43#include "util/file_util.h"
44#include "util/platform_paths.h"
45
46namespace yaze {
47namespace editor {
48
50 EditorManager* editor_manager, RomFileManager& rom_manager,
51 ProjectManager& project_manager, EditorRegistry& editor_registry,
52 WorkspaceWindowManager& window_manager,
53 SessionCoordinator& session_coordinator, WindowDelegate& window_delegate,
54 ToastManager& toast_manager, PopupManager& popup_manager,
55 ShortcutManager& shortcut_manager)
56 : editor_manager_(editor_manager),
57 rom_manager_(rom_manager),
58 project_manager_(project_manager),
59 editor_registry_(editor_registry),
60 window_manager_(window_manager),
61 session_coordinator_(session_coordinator),
62 window_delegate_(window_delegate),
63 toast_manager_(toast_manager),
64 popup_manager_(popup_manager),
65 shortcut_manager_(shortcut_manager) {
66 // Initialize welcome screen with proper callbacks
67 welcome_screen_ = std::make_unique<WelcomeScreen>();
68
69 // Bind persistent prefs so animation tweaks survive restart.
70 if (editor_manager_) {
71 welcome_screen_->SetUserSettings(&editor_manager_->user_settings());
72 }
73
74 // Wire welcome screen callbacks to EditorManager
75 welcome_screen_->SetOpenRomCallback([this]() {
76#ifdef __EMSCRIPTEN__
77 // In web builds, trigger the file input element directly
78 // The file input handler in app.js will handle the file selection
79 // and call LoadRomFromWeb, which will update the ROM
80 EM_ASM({
81 var romInput = document.getElementById('rom-input');
82 if (romInput) {
83 romInput.click();
84 }
85 });
86 // Don't hide welcome screen yet - it will be hidden when ROM loads
87 // (DrawWelcomeScreen auto-transitions to Dashboard on ROM load)
88#else
89 if (editor_manager_) {
90 auto status = editor_manager_->LoadRom();
91 if (!status.ok()) {
93 absl::StrFormat("Failed to load ROM: %s", status.message()),
95 }
96#if !(defined(__APPLE__) && TARGET_OS_IOS == 1)
97 else {
98 // Transition to Dashboard on successful ROM load
100 }
101#endif
102 }
103#endif
104 });
105
106 welcome_screen_->SetNewProjectCallback(
107 [this]() { new_project_dialog_.Open("Vanilla ROM Hack"); });
108
109 // Template-driven creation now opens the guided dialog instead of chaining
110 // straight into CreateNewProject -> LoadRom. The dialog calls back into
111 // EditorManager once the user has a ROM path and project name picked.
112 welcome_screen_->SetNewProjectWithTemplateCallback(
113 [this](const std::string& template_name) {
114 new_project_dialog_.Open(template_name);
115 });
116
118 [this](const std::string& template_name, const std::string& rom_path,
119 const std::string& project_name) -> absl::Status {
120 if (!editor_manager_) {
121 return absl::FailedPreconditionError(
122 "Editor manager is not available");
123 }
125 template_name, rom_path, project_name);
126 if (!status.ok()) {
128 absl::StrFormat("Failed to create project: %s", status.message()),
130 return status;
131 }
132 toast_manager_.Show(absl::StrFormat("Created project \"%s\" from %s",
133 project_name, template_name),
136 return absl::OkStatus();
137 });
138
139 welcome_screen_->SetOpenProjectCallback([this](const std::string& filepath) {
140 if (editor_manager_) {
141 auto status = editor_manager_->OpenRomOrProject(filepath);
142 if (!status.ok()) {
144 absl::StrFormat("Failed to open project: %s", status.message()),
146 } else {
147 // Transition to Dashboard on successful project open
149 }
150 }
151 });
152
153 welcome_screen_->SetOpenAgentCallback([this]() {
154 if (editor_manager_) {
155#ifdef YAZE_BUILD_AGENT_UI
156 editor_manager_->ShowAIAgent();
157#endif
158 // Exit welcome so the agent panels can be interacted with
160 }
161 });
162
163 welcome_screen_->SetOpenProjectDialogCallback([this]() {
164 if (editor_manager_) {
165 auto status = editor_manager_->OpenProject();
166 if (!status.ok()) {
168 absl::StrFormat("Failed to open project: %s", status.message()),
170 } else {
172 }
173 }
174 });
175
176 welcome_screen_->SetOpenProjectManagementCallback([this]() {
177 if (editor_manager_) {
180 }
181 });
182
183 welcome_screen_->SetOpenProjectFileEditorCallback([this]() {
184 if (!editor_manager_) {
185 return;
186 }
187 const auto* project = editor_manager_->GetCurrentProject();
188 if (!project || project->filepath.empty()) {
189 toast_manager_.Show("No project file to edit", ToastType::kInfo);
190 return;
191 }
194 });
195
196 welcome_screen_->SetOpenPrototypeResearchCallback([this]() {
197 if (!editor_manager_) {
198 return;
199 }
201 window_manager_.OpenWindow("graphics.prototype_viewer");
204 });
205
206 welcome_screen_->SetOpenAssemblyEditorNoRomCallback([this]() {
207 if (!editor_manager_) {
208 return;
209 }
211 window_manager_.OpenWindow("assembly.code_editor");
214 });
215}
216
230
232 if (dashboard_behavior_override_ == mode) {
233 return;
234 }
236 if (mode == StartupVisibility::kShow) {
237 // Only transition to dashboard if we're not in welcome
240 }
241 } else if (mode == StartupVisibility::kHide) {
242 // If hiding dashboard, transition to editor state
245 }
246 }
247}
248
250 if (!ImGui::GetCurrentContext()) {
251 return;
252 }
253
254 const ImGuiViewport* viewport = ImGui::GetMainViewport();
255 if (!viewport) {
256 return;
257 }
258
259 ImDrawList* bg_draw_list =
260 ImGui::GetBackgroundDrawList(const_cast<ImGuiViewport*>(viewport));
261
262 auto& theme_manager = gui::ThemeManager::Get();
263 auto current_theme = theme_manager.GetCurrentTheme();
264 auto& bg_renderer = gui::BackgroundRenderer::Get();
265
266 // Draw grid covering the entire main viewport
267 ImVec2 grid_pos = viewport->WorkPos;
268 ImVec2 grid_size = viewport->WorkSize;
269 bg_renderer.RenderDockingBackground(bg_draw_list, grid_pos, grid_size,
270 current_theme.primary);
271}
272
274 // Note: Theme styling is applied by ThemeManager, not here
275 // This is called from EditorManager::Update() - don't call menu bar stuff
276 // here
277
278 // Draw UI windows and dialogs
279 // Session dialogs are drawn by SessionCoordinator separately to avoid
280 // duplication
281 DrawCommandPalette(); // Ctrl+Shift+P
282 DrawPanelFinder(); // Ctrl+P
283 DrawGlobalSearch(); // Ctrl+Shift+K
284 DrawWorkspacePresetDialogs(); // Save/Load workspace dialogs
285 DrawLayoutPresets(); // Layout preset dialogs
286 DrawWelcomeScreen(); // Welcome screen
287 DrawProjectHelp(); // Project help
288 DrawWindowManagementUI(); // Window management
289
290#ifdef YAZE_BUILD_AGENT_UI
291 if (show_ai_agent_) {
292 if (editor_manager_) {
293 editor_manager_->ShowAIAgent();
294 }
295 show_ai_agent_ = false;
296 }
297
298 if (show_chat_history_) {
299 if (editor_manager_) {
300 editor_manager_->ShowChatHistory();
301 }
302 show_chat_history_ = false;
303 }
304
306 if (editor_manager_) {
307 if (auto* right_panel = editor_manager_->right_drawer_manager()) {
308 right_panel->OpenDrawer(RightDrawerManager::DrawerType::kProposals);
309 }
310 }
311 show_proposal_drawer_ = false;
312 }
313#endif
314
315 // Draw popups and toasts
318}
319
321 const ImGuiViewport* viewport = ImGui::GetMainViewport();
322 if (!viewport) {
323 return false;
324 }
325 const float width = viewport->WorkSize.x;
326#if defined(__APPLE__) && TARGET_OS_IOS == 1
327 // Use hysteresis to avoid layout thrash while iPad windows are being
328 // interactively resized around the compact breakpoint.
329 static bool compact_mode = false;
330 constexpr float kEnterCompactWidth = 900.0f;
331 constexpr float kExitCompactWidth = 940.0f;
332 compact_mode =
333 compact_mode ? (width < kExitCompactWidth) : (width < kEnterCompactWidth);
334 return compact_mode;
335#else
336 return width < 900.0f;
337#endif
338}
339
340// =============================================================================
341// Menu Bar Helpers
342// =============================================================================
343
344bool UICoordinator::DrawMenuBarIconButton(const char* icon, const char* tooltip,
345 bool is_active) {
346 // Consistent button styling: transparent background, themed text
347 gui::StyleColorGuard btn_guard(
348 {{ImGuiCol_Button, ImVec4(0, 0, 0, 0)},
349 {ImGuiCol_ButtonHovered, gui::GetSurfaceContainerHighVec4()},
350 {ImGuiCol_ButtonActive, gui::GetSurfaceContainerHighestVec4()},
351 {ImGuiCol_Text,
353
354 bool clicked = ImGui::SmallButton(icon);
355
356 if (tooltip && ImGui::IsItemHovered()) {
357 ImGui::SetTooltip("%s", tooltip);
358 }
359
360 return clicked;
361}
362
364 // SmallButton width = text width + frame padding * 2
365 const float frame_padding = ImGui::GetStyle().FramePadding.x;
366 // Use a standard icon width (Material Design icons are uniform)
367 const float icon_width = ImGui::CalcTextSize(ICON_MD_SETTINGS).x;
368 return icon_width + frame_padding * 2.0f;
369}
370
372 // Right-aligned status cluster: Version, dirty indicator, session, bell, panel toggles
373 // Panel toggles are positioned using SCREEN coordinates (from viewport) so they
374 // stay fixed even when the dockspace resizes due to panel open/close.
375 //
376 // Layout: [v0.x.x][●][📄▾][🔔] [panels][⬆]
377 // ^^^ shifts with dockspace ^^^ ^^^ fixed screen position ^^^
378
379 auto* current_rom = editor_manager_->GetCurrentRom();
380 const std::string full_version =
381 absl::StrFormat("v%s", editor_manager_->version().c_str());
382
383 const float item_spacing = 6.0f;
384 const float padding = 8.0f;
385
386 auto CalcSmallButtonWidth = [](const char* label) -> float {
387 // SmallButton width = text width + frame padding * 2
388 const float frame_padding = ImGui::GetStyle().FramePadding.x;
389 const float text_w = ImGui::CalcTextSize(label).x;
390 return text_w + frame_padding * 2.0f;
391 };
392
393 // Get TRUE viewport dimensions (not affected by dockspace resize)
394 const ImGuiViewport* viewport = ImGui::GetMainViewport();
395 const float true_viewport_right = viewport->WorkPos.x + viewport->WorkSize.x;
396
397 // Calculate panel toggle region width
398 // Keep this in sync with RightDrawerManager::DrawDrawerToggleButtons().
399 const bool has_panel_toggles =
401 float panel_buttons_width = 0.0f;
402 if (has_panel_toggles) {
403 const char* kIcons[] = {
404 ICON_MD_FOLDER_SPECIAL, // Project
405 ICON_MD_SMART_TOY, // Agent
406 ICON_MD_HELP_OUTLINE, // Help
407 ICON_MD_SETTINGS, // Settings
408 ICON_MD_LIST_ALT, // Properties
409 };
410 constexpr size_t kIconCount = sizeof(kIcons) / sizeof(kIcons[0]);
411
412 for (size_t i = 0; i < kIconCount; ++i) {
413 panel_buttons_width += CalcSmallButtonWidth(kIcons[i]);
414 if (i + 1 < kIconCount) {
415 panel_buttons_width += item_spacing;
416 }
417 }
418 }
419
420 // Reserve only the real button footprint so compact icon toggles do not
421 // leave a dead gap before the right edge cluster.
422 float panel_region_width = panel_buttons_width;
423#ifdef __EMSCRIPTEN__
424 // WASM hide menu bar toggle (drawn inline after panel buttons).
425 panel_region_width +=
426 CalcSmallButtonWidth(ICON_MD_EXPAND_LESS) + item_spacing;
427#endif
428
429 // Calculate screen X position for panel toggles (fixed at viewport right edge)
430 float panel_screen_x = true_viewport_right - panel_region_width;
434 }
435
436 // Calculate available space for status cluster (version, dirty, session, bell)
437 // This ends where the panel toggle region begins
438 const float window_width = ImGui::GetWindowWidth();
439 const float window_screen_x = ImGui::GetWindowPos().x;
440 const float menu_items_end = ImGui::GetCursorPosX() + 16.0f;
441
442 // Convert panel screen X to window-local coordinates for space calculation
443 float panel_local_x = panel_screen_x - window_screen_x;
444 float region_end =
445 std::min(window_width - padding, panel_local_x - item_spacing);
446
447 // Calculate what elements to show - progressive hiding when space is tight
448 bool has_dirty_rom =
449 current_rom && current_rom->is_loaded() && current_rom->dirty();
450 bool has_multiple_sessions = session_coordinator_.HasMultipleSessions();
451
452 float version_width = ImGui::CalcTextSize(full_version.c_str()).x;
453 float dirty_width =
454 ImGui::CalcTextSize(ICON_MD_FIBER_MANUAL_RECORD).x + item_spacing;
455 const float session_width = CalcSmallButtonWidth(ICON_MD_LAYERS);
456
457 const float available_width = region_end - menu_items_end - padding;
458
459 // Minimum required width: just the bell (always visible)
460 float required_width = CalcSmallButtonWidth(ICON_MD_NOTIFICATIONS);
461
462 // Progressive show/hide based on available space
463 // Priority (highest to lowest): Bell > Dirty > Session > Version
464
465 // Try to fit version (lowest priority - hide first when tight)
466 bool show_version =
467 (required_width + version_width + item_spacing) <= available_width;
468 if (show_version) {
469 required_width += version_width + item_spacing;
470 }
471
472 // Try to fit session button (medium priority)
473 bool show_session =
474 has_multiple_sessions &&
475 (required_width + session_width + item_spacing) <= available_width;
476 if (show_session) {
477 required_width += session_width + item_spacing;
478 }
479
480 // Try to fit dirty indicator (high priority - only hide if extremely tight)
481 bool show_dirty =
482 has_dirty_rom && (required_width + dirty_width) <= available_width;
483 if (show_dirty) {
484 required_width += dirty_width;
485 }
486
487 // Calculate start position (right-align within available space)
488 float start_pos = std::max(menu_items_end, region_end - required_width);
489
490 // =========================================================================
491 // DRAW STATUS CLUSTER (shifts with dockspace)
492 // =========================================================================
493 ImGui::SameLine(start_pos);
494 gui::StyleVarGuard item_spacing_guard(ImGuiStyleVar_ItemSpacing,
495 ImVec2(item_spacing, 0.0f));
496
497 // 1. Version - subdued gray text
498 if (show_version) {
499 gui::ColoredText(full_version.c_str(), gui::GetTextDisabledVec4());
500 ImGui::SameLine();
501 }
502
503 // 2. Dirty badge - warning color dot
504 if (show_dirty) {
505 const auto& theme = gui::ThemeManager::Get().GetCurrentTheme();
507 gui::ConvertColorToImVec4(theme.warning));
508 if (ImGui::IsItemHovered()) {
509 ImGui::SetTooltip(tr("Unsaved changes: %s"),
510 current_rom->short_name().c_str());
511 }
512 ImGui::SameLine();
513 }
514
515 // 3. Session button - layers icon
516 if (show_session) {
518 ImGui::SameLine();
519 }
520
521 // 4. Notification bell (pass visibility flags for enhanced tooltip)
522 DrawNotificationBell(show_dirty, has_dirty_rom, show_session,
523 has_multiple_sessions);
524
525 // =========================================================================
526 // DRAW PANEL TOGGLES (fixed screen position, unaffected by dockspace resize)
527 // =========================================================================
528 if (has_panel_toggles) {
529 // Get current Y position within menu bar
530 float menu_bar_y = ImGui::GetCursorScreenPos().y;
531
532 // Position at fixed screen coordinates
533 ImGui::SetCursorScreenPos(ImVec2(panel_screen_x, menu_bar_y));
534
535 // Draw panel toggle buttons
537 }
538
539#ifdef __EMSCRIPTEN__
540 // WASM toggle button - also at fixed position
541 ImGui::SameLine();
543 "Hide menu bar (Alt to restore)")) {
544 show_menu_bar_ = false;
545 }
546#endif
547}
548
550 // Only draw when menu bar is hidden (primarily for WASM builds)
551 if (show_menu_bar_) {
552 return;
553 }
554
555 // Small floating button in top-left corner to restore menu bar
556 ImGuiWindowFlags flags =
557 ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize |
558 ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar |
559 ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_AlwaysAutoResize |
560 ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoSavedSettings;
561
562 ImGui::SetNextWindowPos(ImVec2(8, 8));
563 ImGui::SetNextWindowBgAlpha(0.7f);
564
565 if (ImGui::Begin("##MenuBarRestore", nullptr, flags)) {
566 gui::StyleColorGuard btn_guard(
567 {{ImGuiCol_Button, gui::GetSurfaceContainerVec4()},
568 {ImGuiCol_ButtonHovered, gui::GetSurfaceContainerHighVec4()},
569 {ImGuiCol_ButtonActive, gui::GetSurfaceContainerHighestVec4()},
570 {ImGuiCol_Text, gui::GetPrimaryVec4()}});
571
572 if (ImGui::Button(ICON_MD_FULLSCREEN_EXIT, ImVec2(32, 32))) {
573 show_menu_bar_ = true;
574 }
575
576 if (ImGui::IsItemHovered()) {
577 ImGui::SetTooltip(tr("Show menu bar (Alt)"));
578 }
579 }
580 ImGui::End();
581
582 // Also check for Alt key to restore menu bar
583 if (ImGui::IsKeyPressed(ImGuiKey_LeftAlt) ||
584 ImGui::IsKeyPressed(ImGuiKey_RightAlt)) {
585 show_menu_bar_ = true;
586 }
587}
588
589void UICoordinator::DrawNotificationBell(bool show_dirty, bool has_dirty_rom,
590 bool show_session,
591 bool has_multiple_sessions) {
592 size_t unread = toast_manager_.GetUnreadCount();
593 auto* current_rom = editor_manager_->GetCurrentRom();
594 auto* right_panel = editor_manager_->right_drawer_manager();
595
596 // Check if notifications panel is active
597 bool is_active =
598 right_panel && right_panel->IsDrawerActive(
600
601 // Bell icon with accent color when there are unread notifications or panel is active
602 ImVec4 bell_text_color = (unread > 0 || is_active)
605 gui::StyleColorGuard bell_guard(
606 {{ImGuiCol_Text, bell_text_color},
607 {ImGuiCol_Button, ImVec4(0, 0, 0, 0)},
608 {ImGuiCol_ButtonHovered, gui::GetSurfaceContainerHighVec4()},
609 {ImGuiCol_ButtonActive, gui::GetSurfaceContainerHighestVec4()}});
610
611 // Bell button - opens notifications panel in right sidebar
612 if (ImGui::SmallButton(ICON_MD_NOTIFICATIONS)) {
613 if (right_panel) {
614 right_panel->ToggleDrawer(RightDrawerManager::DrawerType::kNotifications);
616 }
617 }
618
619 // Enhanced tooltip showing notifications + hidden status items
620 if (ImGui::IsItemHovered()) {
621 ImGui::BeginTooltip();
622
623 // Notifications
624 if (unread > 0) {
625 gui::ColoredTextF(gui::GetPrimaryVec4(), "%s %zu new notification%s",
626 ICON_MD_NOTIFICATIONS, unread, unread == 1 ? "" : "s");
627 } else {
628 gui::ColoredText(ICON_MD_NOTIFICATIONS " No new notifications",
630 }
631
632 ImGui::TextDisabled(tr("Click to open Notifications panel"));
633
634 // Show hidden status items if any
635 if (!show_dirty && has_dirty_rom) {
636 ImGui::Separator();
638 gui::ThemeManager::Get().GetCurrentTheme().warning),
639 ICON_MD_FIBER_MANUAL_RECORD " Unsaved changes: %s",
640 current_rom->short_name().c_str());
641 }
642
643 if (!show_session && has_multiple_sessions) {
644 if (!show_dirty && has_dirty_rom) {
645 // Already had a separator
646 } else {
647 ImGui::Separator();
648 }
650 ICON_MD_LAYERS " %zu sessions active",
652 }
653
654 ImGui::EndTooltip();
655 }
656}
657
659 auto* current_rom = editor_manager_->GetCurrentRom();
660
661 // Consistent button styling with other menubar buttons
662 gui::StyleColorGuard session_btn_guard(
663 {{ImGuiCol_Button, ImVec4(0, 0, 0, 0)},
664 {ImGuiCol_ButtonHovered, gui::GetSurfaceContainerHighVec4()},
665 {ImGuiCol_ButtonActive, gui::GetSurfaceContainerHighestVec4()},
666 {ImGuiCol_Text, gui::GetTextSecondaryVec4()}});
667
668 // Store button position for popup anchoring
669 ImVec2 button_min = ImGui::GetCursorScreenPos();
670
671 if (ImGui::SmallButton(ICON_MD_LAYERS)) {
672 ImGui::OpenPopup("##SessionSwitcherPopup");
673 }
674
675 ImVec2 button_max = ImGui::GetItemRectMax();
676
677 if (ImGui::IsItemHovered()) {
678 std::string tooltip = current_rom && current_rom->is_loaded()
679 ? current_rom->short_name()
680 : "No ROM loaded";
681 ImGui::SetTooltip(tr("%s\n%zu sessions open (Ctrl+Tab)"), tooltip.c_str(),
683 }
684
685 // Anchor popup to right edge - position so right edge aligns with button
686 const float popup_width = 250.0f;
687 const float screen_width = ImGui::GetIO().DisplaySize.x;
688 const float popup_x =
689 std::min(button_min.x, screen_width - popup_width - 10.0f);
690
691 ImGui::SetNextWindowPos(ImVec2(popup_x, button_max.y + 2.0f),
692 ImGuiCond_Appearing);
693
694 // Session switcher popup
695 if (ImGui::BeginPopup("##SessionSwitcherPopup")) {
696 ImGui::Text(ICON_MD_LAYERS " Sessions");
697 ImGui::Separator();
698
699 for (size_t i = 0; i < session_coordinator_.GetTotalSessionCount(); ++i) {
701 continue;
702
703 auto* session =
705 if (!session)
706 continue;
707
708 Rom* rom = &session->rom;
709 ImGui::PushID(static_cast<int>(i));
710
711 bool is_current = (rom == current_rom);
712 std::optional<gui::StyleColorGuard> current_guard;
713 if (is_current) {
714 current_guard.emplace(ImGuiCol_Text, gui::GetPrimaryVec4());
715 }
716
717 std::string label =
718 rom->is_loaded()
719 ? absl::StrFormat("%s %s", ICON_MD_DESCRIPTION,
720 rom->short_name().c_str())
721 : absl::StrFormat("%s Session %zu", ICON_MD_DESCRIPTION, i + 1);
722
723 if (ImGui::Selectable(label.c_str(), is_current)) {
725 }
726
727 ImGui::PopID();
728 }
729
730 ImGui::EndPopup();
731 }
732}
733
734// ============================================================================
735// Session UI Delegation
736// ============================================================================
737// All session-related UI is now managed by SessionCoordinator to eliminate
738// duplication. UICoordinator methods delegate to SessionCoordinator.
739
743
747
749 if (visible) {
751 } else {
753 }
754}
755
757 const size_t session_id = session_coordinator_.GetActiveSessionId();
759 RefreshCommandPalette(session_id);
760 } else {
761 InitializeCommandPalette(session_id);
762 }
764}
765
767 if (visible) {
769 return;
770 }
771 show_command_palette_ = false;
772}
773
774// Emulator visibility delegates to WorkspaceWindowManager (single source of truth)
776 size_t session_id = session_coordinator_.GetActiveSessionId();
777 auto emulator_windows =
778 window_manager_.GetWindowsInCategory(session_id, "Emulator");
779 for (const auto& window : emulator_windows) {
780 if (window.visibility_flag && *window.visibility_flag) {
781 return true;
782 }
783 }
784 return false;
785}
786
788 size_t session_id = session_coordinator_.GetActiveSessionId();
789 if (visible) {
790 auto default_windows =
792 for (const auto& window_id : default_windows) {
793 window_manager_.OpenWindow(session_id, window_id);
794 }
795 } else {
796 window_manager_.HideAllWindowsInCategory(session_id, "Emulator");
797 }
798}
799
800// Assembly editor visibility: same delegation pattern as the emulator. The
801// `show_asm_editor_` boolean was a parallel source of truth that could drift
802// when the user closed a single Assembly panel via its window close button;
803// reading through the category means the user's last interaction always wins.
805 size_t session_id = session_coordinator_.GetActiveSessionId();
806 auto windows = window_manager_.GetWindowsInCategory(session_id, "Assembly");
807 for (const auto& window : windows) {
808 if (window.visibility_flag && *window.visibility_flag) {
809 return true;
810 }
811 }
812 return false;
813}
814
816 size_t session_id = session_coordinator_.GetActiveSessionId();
817 if (visible) {
818 auto default_windows =
820 for (const auto& window_id : default_windows) {
821 window_manager_.OpenWindow(session_id, window_id);
822 }
823 } else {
824 window_manager_.HideAllWindowsInCategory(session_id, "Assembly");
825 }
826}
827
828// ============================================================================
829// Layout and Window Management UI
830// ============================================================================
831
833 // TODO: [EditorManagerRefactor] Implement full layout preset UI with
834 // save/load For now, this is accessed via Window menu items that call
835 // workspace_manager directly
836}
837
839 // ============================================================================
840 // CENTRALIZED WELCOME SCREEN LOGIC (using StartupSurface state)
841 // ============================================================================
842 // Uses ShouldShowWelcome() as single source of truth
843 // Auto-transitions to Dashboard on ROM load
844 // Activity Bar hidden when welcome is visible
845 // ============================================================================
846
847 if (!editor_manager_) {
848 LOG_ERROR("UICoordinator",
849 "EditorManager is null - cannot check ROM state");
850 return;
851 }
852
853 if (!welcome_screen_) {
854 LOG_ERROR("UICoordinator", "WelcomeScreen object is null - cannot render");
855 return;
856 }
857
858 // Check ROM state and update startup surface accordingly
859 auto* current_rom = editor_manager_->GetCurrentRom();
860 bool rom_is_loaded = current_rom && current_rom->is_loaded();
861
862 // Auto-transition: ROM loaded -> Dashboard or Editor
863 if (rom_is_loaded && current_startup_surface_ == StartupSurface::kWelcome) {
866 } else {
868 }
869 }
870
871 // Auto-transition: ROM unloaded -> Welcome (reset to welcome state)
872 if (!rom_is_loaded && current_startup_surface_ != StartupSurface::kWelcome &&
875 }
876
877 // Use centralized visibility check
878 if (!ShouldShowWelcome()) {
879 // Project creation can start from the menu or command palette while the
880 // welcome surface is hidden.
882 return;
883 }
884
885 // Provide context state for gating actions
886 welcome_screen_->SetContextState(rom_is_loaded,
888
889 // Update recent projects before showing (cheap no-op when the
890 // RecentFilesManager generation counter hasn't changed).
891 welcome_screen_->RefreshRecentProjects();
892
893 // Pass layout offsets so welcome screen centers within dockspace region
894 // Note: Activity Bar is hidden when welcome is shown, so left_offset = 0
895 float left_offset =
897 float right_offset = editor_manager_->GetRightLayoutOffset();
898 welcome_screen_->SetLayoutOffsets(left_offset, right_offset);
899
900 // Show the welcome screen window
901 bool is_open = true;
902 welcome_screen_->Show(&is_open);
903
904 // Draw after the welcome window so the modal layers above it.
906
907 // If user closed it via X button, respect that and transition to appropriate state
908 if (!is_open) {
910 // Transition to Dashboard if ROM loaded, stay in Editor state otherwise
911 if (rom_is_loaded) {
913 }
914 }
915}
916
918 // TODO: [EditorManagerRefactor] Implement project help dialog
919 // Show context-sensitive help based on current editor and ROM state
920}
921
924 ImGui::Begin("Save Workspace Preset", &show_save_workspace_preset_,
925 ImGuiWindowFlags_AlwaysAutoResize);
926 static char preset_name[128] = "";
927 ImGui::InputText(tr("Name"), preset_name, IM_ARRAYSIZE(preset_name));
928 if (ImGui::Button(tr("Save"), gui::kDefaultModalSize)) {
929 if (strlen(preset_name) > 0) {
933 preset_name[0] = '\0';
934 }
935 }
936 ImGui::SameLine();
937 if (ImGui::Button(tr("Cancel"), gui::kDefaultModalSize)) {
939 preset_name[0] = '\0';
940 }
941 ImGui::End();
942 }
943
945 ImGui::Begin("Load Workspace Preset", &show_load_workspace_preset_,
946 ImGuiWindowFlags_AlwaysAutoResize);
947
948 // Lazy load workspace presets when UI is accessed
950
951 if (auto* workspace_manager = editor_manager_->workspace_manager()) {
952 for (const auto& name : workspace_manager->workspace_presets()) {
953 if (ImGui::Selectable(name.c_str())) {
957 }
958 }
959 if (workspace_manager->workspace_presets().empty())
960 ImGui::Text(tr("No presets found"));
961 }
962 ImGui::End();
963 }
964}
965
967 // TODO: [EditorManagerRefactor] Implement window management dialog
968 // Provide UI for toggling window visibility, managing docking, etc.
969}
970
972 // Draw all registered popups
974}
975
976void UICoordinator::ShowPopup(const std::string& popup_name) {
977 popup_manager_.Show(popup_name.c_str());
978}
979
980void UICoordinator::HidePopup(const std::string& popup_name) {
981 popup_manager_.Hide(popup_name.c_str());
982}
983
985 // Display Settings is now a popup managed by PopupManager
986 // Delegate directly to PopupManager instead of UICoordinator
988}
989
990// ============================================================================
991// Sidebar visibility delegates to WorkspaceWindowManager
992// ============================================================================
993
997
1001
1005
1009
1013
1014// Material Design component helpers
1015void UICoordinator::DrawMaterialButton(const std::string& text,
1016 const std::string& icon,
1017 const ImVec4& color,
1018 std::function<void()> callback,
1019 bool enabled) {
1020 std::optional<gui::StyleColorGuard> disabled_guard;
1021 if (!enabled) {
1022 disabled_guard.emplace(std::initializer_list<gui::StyleColorGuard::Entry>{
1023 {ImGuiCol_Button, gui::GetSurfaceContainerHighestVec4()},
1024 {ImGuiCol_Text, gui::GetOnSurfaceVariantVec4()}});
1025 }
1026
1027 std::string button_text =
1028 absl::StrFormat("%s %s", icon.c_str(), text.c_str());
1029 if (ImGui::Button(button_text.c_str())) {
1030 if (enabled && callback) {
1031 callback();
1032 }
1033 }
1034}
1035
1036// Layout and positioning helpers
1037void UICoordinator::CenterWindow(const std::string& window_name) {
1038 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(),
1039 ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
1040}
1041
1042void UICoordinator::PositionWindow(const std::string& window_name, float x,
1043 float y) {
1044 ImGui::SetNextWindowPos(ImVec2(x, y), ImGuiCond_Appearing);
1045}
1046
1047void UICoordinator::SetWindowSize(const std::string& window_name, float width,
1048 float height) {
1049 ImGui::SetNextWindowSize(ImVec2(width, height), ImGuiCond_FirstUseEver);
1050}
1051
1054 return;
1055
1056 // Initialize command palette on first use
1059 }
1060
1061 using namespace ImGui;
1062 auto& theme = gui::ThemeManager::Get().GetCurrentTheme();
1063
1064 SetNextWindowPos(GetMainViewport()->GetCenter(), ImGuiCond_Appearing,
1065 ImVec2(0.5f, 0.5f));
1066 SetNextWindowSize(ImVec2(800, 600), ImGuiCond_FirstUseEver);
1067
1068 bool show_palette = true;
1069 if (Begin(absl::StrFormat("%s Command Palette", ICON_MD_SEARCH).c_str(),
1070 &show_palette, ImGuiWindowFlags_NoCollapse)) {
1071 // Search input with focus management
1072 SetNextItemWidth(-100);
1073 if (IsWindowAppearing()) {
1074 SetKeyboardFocusHere();
1076 }
1077
1078 bool input_changed = InputTextWithHint(
1079 "##cmd_query",
1080 absl::StrFormat("%s Search commands (fuzzy matching enabled)...",
1082 .c_str(),
1084
1085 SameLine();
1086 if (Button(absl::StrFormat("%s Clear", ICON_MD_CLEAR).c_str())) {
1087 command_palette_query_[0] = '\0';
1088 input_changed = true;
1090 }
1091
1092 Separator();
1093
1094 // Unified command list structure
1095 struct ScoredCommand {
1096 int score;
1097 std::string name;
1098 std::string category;
1099 std::string shortcut;
1100 std::function<void()> callback;
1101 };
1102 std::vector<ScoredCommand> scored_commands;
1103
1104 std::string query_lower = command_palette_query_;
1105 std::transform(query_lower.begin(), query_lower.end(), query_lower.begin(),
1106 ::tolower);
1107
1108 auto score_text = [&query_lower](const std::string& text) -> int {
1109 std::string text_lower = text;
1110 std::transform(text_lower.begin(), text_lower.end(), text_lower.begin(),
1111 ::tolower);
1112
1113 if (query_lower.empty())
1114 return 1;
1115 if (text_lower.find(query_lower) == 0)
1116 return 1000;
1117 if (text_lower.find(query_lower) != std::string::npos)
1118 return 500;
1119
1120 // Fuzzy match
1121 size_t text_idx = 0, query_idx = 0;
1122 int score = 0;
1123 while (text_idx < text_lower.length() &&
1124 query_idx < query_lower.length()) {
1125 if (text_lower[text_idx] == query_lower[query_idx]) {
1126 score += 10;
1127 query_idx++;
1128 }
1129 text_idx++;
1130 }
1131 return (query_idx == query_lower.length()) ? score : 0;
1132 };
1133
1134 // Add shortcuts from ShortcutManager
1135 for (const auto& [name, shortcut] : shortcut_manager_.GetShortcuts()) {
1136 int score = score_text(name);
1137 if (score > 0) {
1138 std::string shortcut_text =
1139 shortcut.keys.empty()
1140 ? ""
1141 : absl::StrFormat("(%s)", PrintShortcut(shortcut.keys).c_str());
1142 scored_commands.push_back(
1143 {score, name, "Shortcuts", shortcut_text, shortcut.callback});
1144 }
1145 }
1146
1147 // Add commands from CommandPalette
1148 for (const auto& entry : command_palette_.GetAllCommands()) {
1149 int score = score_text(entry.name);
1150 // Also search category and description
1151 score += score_text(entry.category) / 2;
1152 score += score_text(entry.description) / 4;
1153
1154 if (score > 0) {
1155 scored_commands.push_back({score, entry.name, entry.category,
1156 entry.shortcut, entry.callback});
1157 }
1158 }
1159
1160 // Sort by score descending
1161 std::sort(scored_commands.begin(), scored_commands.end(),
1162 [](const auto& a, const auto& b) { return a.score > b.score; });
1163
1164 // Display results with categories
1165 if (gui::BeginThemedTabBar("CommandCategories")) {
1166 if (BeginTabItem(
1167 absl::StrFormat("%s All Commands", ICON_MD_LIST).c_str())) {
1169 "CommandPaletteTable", 4,
1170 ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg |
1171 ImGuiTableFlags_SizingStretchProp,
1172 ImVec2(0, -30))) {
1173 TableSetupColumn("Command", ImGuiTableColumnFlags_WidthStretch,
1174 0.45f);
1175 TableSetupColumn("Category", ImGuiTableColumnFlags_WidthStretch,
1176 0.2f);
1177 TableSetupColumn("Shortcut", ImGuiTableColumnFlags_WidthStretch,
1178 0.2f);
1179 TableSetupColumn("Score", ImGuiTableColumnFlags_WidthStretch, 0.15f);
1180 TableHeadersRow();
1181
1182 for (size_t i = 0; i < scored_commands.size(); ++i) {
1183 const auto& cmd = scored_commands[i];
1184
1185 TableNextRow();
1186 TableNextColumn();
1187
1188 PushID(static_cast<int>(i));
1189 bool is_selected =
1190 (static_cast<int>(i) == command_palette_selected_idx_);
1191 if (Selectable(cmd.name.c_str(), is_selected,
1192 ImGuiSelectableFlags_SpanAllColumns)) {
1194 if (cmd.callback) {
1195 cmd.callback();
1196 show_command_palette_ = false;
1197 // Record usage for frecency
1198 command_palette_.RecordUsage(cmd.name);
1199 }
1200 }
1201 PopID();
1202
1203 TableNextColumn();
1204 gui::ColoredText(cmd.category.c_str(),
1205 gui::ConvertColorToImVec4(theme.text_secondary));
1206
1207 TableNextColumn();
1208 gui::ColoredText(cmd.shortcut.c_str(),
1209 gui::ConvertColorToImVec4(theme.text_secondary));
1210
1211 TableNextColumn();
1212 gui::ColoredTextF(gui::ConvertColorToImVec4(theme.text_disabled),
1213 "%d", cmd.score);
1214 }
1215
1217 }
1218 EndTabItem();
1219 }
1220
1221 if (BeginTabItem(absl::StrFormat("%s Recent", ICON_MD_HISTORY).c_str())) {
1222 auto recent = command_palette_.GetRecentCommands(10);
1223 if (recent.empty()) {
1224 Text(tr("No recent commands yet."));
1225 } else {
1226 for (const auto& entry : recent) {
1227 if (Selectable(entry.name.c_str())) {
1228 if (entry.callback) {
1229 entry.callback();
1230 show_command_palette_ = false;
1231 command_palette_.RecordUsage(entry.name);
1232 }
1233 }
1234 }
1235 }
1236 EndTabItem();
1237 }
1238
1239 if (BeginTabItem(absl::StrFormat("%s Frequent", ICON_MD_STAR).c_str())) {
1240 auto frequent = command_palette_.GetFrequentCommands(10);
1241 if (frequent.empty()) {
1242 Text(tr("No frequently used commands yet."));
1243 } else {
1244 for (const auto& entry : frequent) {
1245 if (Selectable(absl::StrFormat("%s (%d uses)", entry.name,
1246 entry.usage_count)
1247 .c_str())) {
1248 if (entry.callback) {
1249 entry.callback();
1250 show_command_palette_ = false;
1251 command_palette_.RecordUsage(entry.name);
1252 }
1253 }
1254 }
1255 }
1256 EndTabItem();
1257 }
1258
1260 }
1261
1262 // Status bar with tips
1263 Separator();
1264 Text(tr("%s %zu commands | Score: fuzzy match"), ICON_MD_INFO,
1265 scored_commands.size());
1266 SameLine();
1267 gui::ColoredText("| ↑↓=Navigate | Enter=Execute | Esc=Close",
1268 gui::ConvertColorToImVec4(theme.text_disabled));
1269 }
1270 End();
1271
1272 // Update visibility state - save history when closing
1273 if (!show_palette) {
1274 show_command_palette_ = false;
1275 // Save command usage history on close
1276 auto config_dir = util::PlatformPaths::GetConfigDirectory();
1277 if (config_dir.ok()) {
1278 std::filesystem::path history_file = *config_dir / "command_history.json";
1279 command_palette_.SaveHistory(history_file.string());
1280 }
1281 }
1282}
1283
1285 if (!show_panel_finder_)
1286 return;
1287
1288 using namespace ImGui;
1289 const size_t session_id = window_manager_.GetActiveSessionId();
1290
1291 // B6: Responsive modal sizing with dim overlay
1292 const ImGuiViewport* viewport = GetMainViewport();
1293 ImDrawList* bg_list = GetBackgroundDrawList();
1294 bg_list->AddRectFilled(viewport->WorkPos,
1295 ImVec2(viewport->WorkPos.x + viewport->WorkSize.x,
1296 viewport->WorkPos.y + viewport->WorkSize.y),
1297 IM_COL32(0, 0, 0, 100));
1298
1299 SetNextWindowPos(viewport->GetCenter(), ImGuiCond_Appearing,
1300 ImVec2(0.5f, 0.3f));
1301 if (IsCompactLayout()) {
1302 SetNextWindowSize(
1303 ImVec2(viewport->WorkSize.x * 0.95f, viewport->WorkSize.y * 0.70f),
1304 ImGuiCond_Appearing);
1305 } else {
1306 SetNextWindowSize(ImVec2(600, 420), ImGuiCond_Appearing);
1307 }
1308
1309 const ImGuiWindowFlags finder_flags =
1310 ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove |
1311 ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoDocking |
1312 ImGuiWindowFlags_NoSavedSettings;
1313
1314 bool show = true;
1315 if (Begin(ICON_MD_DASHBOARD " Window Finder", &show, finder_flags)) {
1316 // Auto-focus search on open
1317 if (IsWindowAppearing()) {
1318 SetKeyboardFocusHere();
1320 }
1321
1322 SetNextItemWidth(-1);
1323 bool input_changed = InputTextWithHint(
1324 "##panel_finder_query", ICON_MD_SEARCH " Find window...",
1326
1327 if (input_changed) {
1329 }
1330
1331 Separator();
1332
1333 // Build filtered + scored list
1334 struct WindowEntry {
1335 std::string card_id;
1336 std::string display_name;
1337 std::string icon;
1338 std::string category;
1339 bool visible;
1340 bool pinned;
1341 int score;
1342 };
1343 std::vector<WindowEntry> entries;
1344
1345 std::string query(panel_finder_query_);
1346
1347 // B2: Fuzzy scoring via CommandPalette::FuzzyScore
1348 for (const auto& card_id :
1350 const auto* desc =
1351 window_manager_.GetWindowDescriptor(session_id, card_id);
1352 if (!desc) {
1353 continue;
1354 }
1355 int score = 0;
1356 if (!query.empty()) {
1357 score = CommandPalette::FuzzyScore(desc->display_name, query) +
1358 CommandPalette::FuzzyScore(desc->category, query) / 2;
1359 if (score <= 0)
1360 continue;
1361 }
1362
1363 bool vis = desc->visibility_flag ? *desc->visibility_flag : false;
1364 bool pin = window_manager_.IsWindowPinned(session_id, card_id);
1365 entries.push_back({card_id, desc->display_name, desc->icon,
1366 desc->category, vis, pin, score});
1367 }
1368
1369 // B3: MRU + fuzzy sort
1370 if (query.empty()) {
1371 // Empty query: pinned first, then MRU order (higher time = more recent)
1372 std::sort(
1373 entries.begin(), entries.end(),
1374 [this](const WindowEntry& lhs, const WindowEntry& rhs) {
1375 if (lhs.pinned != rhs.pinned)
1376 return lhs.pinned > rhs.pinned;
1377 uint64_t lhs_t = window_manager_.GetWindowMRUTime(lhs.card_id);
1378 uint64_t rhs_t = window_manager_.GetWindowMRUTime(rhs.card_id);
1379 if (lhs_t != rhs_t)
1380 return lhs_t > rhs_t;
1381 return lhs.display_name < rhs.display_name;
1382 });
1383 } else {
1384 // With query: sort by score descending, pinned tiebreaker
1385 std::sort(entries.begin(), entries.end(),
1386 [](const WindowEntry& lhs, const WindowEntry& rhs) {
1387 if (lhs.score != rhs.score)
1388 return lhs.score > rhs.score;
1389 if (lhs.pinned != rhs.pinned)
1390 return lhs.pinned > rhs.pinned;
1391 return lhs.display_name < rhs.display_name;
1392 });
1393 }
1394
1395 // Keyboard navigation
1396 if (IsKeyPressed(ImGuiKey_DownArrow) &&
1397 panel_finder_selected_idx_ < static_cast<int>(entries.size()) - 1) {
1399 }
1400 if (IsKeyPressed(ImGuiKey_UpArrow) && panel_finder_selected_idx_ > 0) {
1402 }
1403 bool enter_pressed = IsKeyPressed(ImGuiKey_Enter);
1404
1405 // B5: Touch-aware item sizing
1406 const bool is_touch = gui::LayoutHelpers::IsTouchDevice();
1407 if (is_touch) {
1408 PushStyleVar(ImGuiStyleVar_ItemSpacing,
1409 ImVec2(GetStyle().ItemSpacing.x, 10.0f));
1410 }
1411
1412 // Draw panel list
1413 BeginChild("##PanelFinderList");
1414 for (int i = 0; i < static_cast<int>(entries.size()); ++i) {
1415 const auto& entry = entries[i];
1416 bool is_selected = (i == panel_finder_selected_idx_);
1417
1418 // B4: Visibility indicator icon
1419 const char* vis_icon =
1421
1422 // Dim hidden panels
1423 std::optional<gui::StyleColorGuard> dim_guard;
1424 if (!entry.visible) {
1425 ImVec4 dimmed = GetStyleColorVec4(ImGuiCol_Text);
1426 dimmed.w *= 0.5f;
1427 dim_guard.emplace(ImGuiCol_Text, dimmed);
1428 }
1429
1430 std::string label =
1431 absl::StrFormat("%s %s %s", vis_icon, entry.icon.c_str(),
1432 entry.display_name.c_str());
1433
1434 // Touch: ensure selectable meets 44px minimum height
1435 float item_h =
1436 is_touch ? std::max(GetTextLineHeightWithSpacing(), 44.0f) : 0.0f;
1437
1438 PushID(entry.card_id.c_str());
1439 if (Selectable(label.c_str(), is_selected, ImGuiSelectableFlags_None,
1440 ImVec2(0, item_h))) {
1441 window_manager_.OpenWindow(session_id, entry.card_id);
1443 show_panel_finder_ = false;
1444 }
1445 // Show category badge on the same line
1446 SameLine(GetContentRegionAvail().x - 80);
1447 TextDisabled("%s", entry.category.c_str());
1448 if (entry.pinned) {
1449 SameLine();
1450 TextDisabled(ICON_MD_PUSH_PIN);
1451 }
1452 PopID();
1453
1454 // Enter to activate selected
1455 if (is_selected && enter_pressed) {
1456 window_manager_.OpenWindow(session_id, entry.card_id);
1458 show_panel_finder_ = false;
1459 }
1460
1461 // Scroll selected into view
1462 if (is_selected && (IsKeyPressed(ImGuiKey_DownArrow) ||
1463 IsKeyPressed(ImGuiKey_UpArrow))) {
1464 SetScrollHereY();
1465 }
1466 }
1467 EndChild();
1468
1469 if (is_touch) {
1470 PopStyleVar();
1471 }
1472 }
1473 End();
1474
1475 // Escape or close button
1476 if (!show || ImGui::IsKeyPressed(ImGuiKey_Escape)) {
1477 show_panel_finder_ = false;
1478 panel_finder_query_[0] = '\0';
1479 }
1480}
1481
1485
1486 // Register panel commands
1488 std::make_unique<PanelCommandsProvider>(&window_manager_, session_id));
1490 std::make_unique<WorkflowCommandsProvider>(&window_manager_, session_id));
1491 if (editor_manager_) {
1492 command_palette_.RegisterProvider(std::make_unique<SidebarCommandsProvider>(
1493 &window_manager_, &editor_manager_->user_settings(), session_id));
1494 }
1495
1496 // Register editor switch commands
1497 command_palette_.RegisterProvider(std::make_unique<EditorCommandsProvider>(
1498 [this](const std::string& category) {
1499 auto type = EditorRegistry::GetEditorTypeFromCategory(category);
1500 if (type != EditorType::kSettings && editor_manager_) {
1502 }
1503 }));
1504
1505 // Register layout/profile commands
1506 command_palette_.AddCommand("Apply: Minimal Layout", CommandCategory::kLayout,
1507 "Switch to essential cards only", "", [this]() {
1508 if (editor_manager_) {
1510 }
1511 });
1512
1514 "Apply: Logic Debugger Layout", CommandCategory::kLayout,
1515 "Switch to debug and development focused layout", "", [this]() {
1516 if (editor_manager_) {
1517 editor_manager_->ApplyLayoutPreset("Logic Debugger");
1518 }
1519 });
1520
1522 "Apply: Overworld Artist Layout", CommandCategory::kLayout,
1523 "Switch to visual and overworld focused layout", "", [this]() {
1524 if (editor_manager_) {
1525 editor_manager_->ApplyLayoutPreset("Overworld Artist");
1526 }
1527 });
1528
1530 "Apply: Dungeon Master Layout", CommandCategory::kLayout,
1531 "Switch to comprehensive dungeon editing layout", "", [this]() {
1532 if (editor_manager_) {
1533 editor_manager_->ApplyLayoutPreset("Dungeon Master");
1534 }
1535 });
1536
1538 "Apply: Audio Engineer Layout", CommandCategory::kLayout,
1539 "Switch to music and sound editing layout", "", [this]() {
1540 if (editor_manager_) {
1541 editor_manager_->ApplyLayoutPreset("Audio Engineer");
1542 }
1543 });
1544
1545 // Register recent files commands
1547 std::make_unique<RecentFilesCommandsProvider>(
1548 [this](const std::string& filepath) {
1549 if (editor_manager_) {
1550 auto status = editor_manager_->OpenRomOrProject(filepath);
1551 if (!status.ok()) {
1553 absl::StrFormat("Failed to open: %s", status.message()),
1555 }
1556 }
1557 }));
1558
1559 // Dungeon navigation helpers (room jump by id/label).
1561 std::make_unique<DungeonRoomCommandsProvider>(session_id));
1562
1563 // Welcome-screen-scoped commands: per-entry pin/remove, undo, template
1564 // creation, and visibility toggles. Recent-entry iteration pulls from the
1565 // same model the welcome screen renders, so the palette and the cards stay
1566 // in sync without a separate refresh path. We rebuild these whenever
1567 // RefreshCommandPalette() is invoked after recents mutate.
1568 if (welcome_screen_) {
1569 WelcomeCommandsProvider::Callbacks welcome_callbacks;
1570 welcome_callbacks.model = &welcome_screen_->recent_projects();
1571 welcome_callbacks.template_names = {
1572 "Vanilla ROM Hack", "ZSCustomOverworld v3", "ZSCustomOverworld v2",
1573 "Randomizer Compatible"};
1574 welcome_callbacks.remove = [this](const std::string& path) {
1575 if (!welcome_screen_)
1576 return;
1577 welcome_screen_->recent_projects().RemoveRecent(path);
1578 welcome_screen_->RefreshRecentProjects(/*force=*/true);
1580 absl::StrFormat("Removed %s from recents",
1581 std::filesystem::path(path).filename().string()),
1583 };
1584 welcome_callbacks.toggle_pin = [this](const std::string& path) {
1585 if (!welcome_screen_)
1586 return;
1587 auto& model = welcome_screen_->recent_projects();
1588 bool currently_pinned = false;
1589 for (const auto& entry : model.entries()) {
1590 if (entry.filepath == path) {
1591 currently_pinned = entry.pinned;
1592 break;
1593 }
1594 }
1595 model.SetPinned(path, !currently_pinned);
1596 welcome_screen_->RefreshRecentProjects(/*force=*/true);
1597 };
1598 welcome_callbacks.undo_remove = [this]() {
1599 if (!welcome_screen_)
1600 return;
1601 if (!welcome_screen_->recent_projects().UndoLastRemoval()) {
1602 toast_manager_.Show("Nothing to undo — the last removal has expired.",
1604 return;
1605 }
1606 welcome_screen_->RefreshRecentProjects(/*force=*/true);
1607 };
1608 welcome_callbacks.clear_recents = [this]() {
1609 if (!welcome_screen_)
1610 return;
1611 welcome_screen_->recent_projects().ClearAll();
1612 welcome_screen_->RefreshRecentProjects(/*force=*/true);
1613 };
1614 welcome_callbacks.create_from_template =
1615 [this](const std::string& template_name) {
1616 new_project_dialog_.Open(template_name);
1617 };
1618 welcome_callbacks.dismiss_welcome = [this]() {
1622 }
1623 };
1624 welcome_callbacks.show_welcome = [this]() {
1627 };
1628 command_palette_.RegisterProvider(std::make_unique<WelcomeCommandsProvider>(
1629 std::move(welcome_callbacks)));
1630 }
1631
1632 // Load command usage history
1633 auto config_dir = util::PlatformPaths::GetConfigDirectory();
1634 if (config_dir.ok()) {
1635 std::filesystem::path history_file = *config_dir / "command_history.json";
1636 command_palette_.LoadHistory(history_file.string());
1637 }
1638
1640}
1641
1644
1648 {.id = "workflow.project.build",
1649 .group = "Build & Run",
1650 .label = "Build Project",
1651 .description = "Run the active project's configured build command",
1652 .shortcut = "",
1653 .priority = 5,
1654 .callback = [this]() {
1655 if (editor_manager_) {
1657 }
1658 }});
1660 {.id = "workflow.project.run",
1661 .group = "Build & Run",
1662 .label = "Run Project Output",
1663 .description = "Open the active project's configured run target in a "
1664 "test session",
1665 .shortcut = "",
1666 .priority = 10,
1667 .callback = [this]() {
1668 if (editor_manager_) {
1670 }
1671 }});
1672 }
1673
1674#ifdef YAZE_WITH_GRPC
1675 auto* emu_backend = Application::Instance().GetEmulatorBackend();
1676 if (emu_backend) {
1678 {.id = "workflow.mesen.connect",
1679 .group = "Live Debugging",
1680 .label = "Connect Mesen2",
1681 .description =
1682 "Auto-discover and connect to the active Mesen2 backend",
1683 .shortcut = "",
1684 .priority = 10,
1685 .callback = [this]() {
1686 auto* backend = Application::Instance().GetEmulatorBackend();
1687 if (backend) {
1688 toast_manager_.Show("Mesen2 connection attempt queued",
1690 }
1691 }});
1693 {.id = "workflow.mesen.step_over",
1694 .group = "Live Debugging",
1695 .label = "Mesen2 Step Over",
1696 .description = "Execute one instruction without entering subroutines",
1697 .shortcut = "F10",
1698 .priority = 20,
1699 .callback = [emu_backend]() { emu_backend->StepOver(); }});
1701 {.id = "workflow.mesen.step_out",
1702 .group = "Live Debugging",
1703 .label = "Mesen2 Step Out",
1704 .description = "Run until return from the current subroutine",
1705 .shortcut = "Shift+F11",
1706 .priority = 30,
1707 .callback = [emu_backend]() { emu_backend->StepOut(); }});
1709 {.id = "workflow.mesen.overlay_on",
1710 .group = "Live Debugging",
1711 .label = "Enable Collision Overlay",
1712 .description = "Show collision overlays in the active Mesen2 backend",
1713 .shortcut = "",
1714 .priority = 40,
1715 .callback = [emu_backend]() {
1716 emu_backend->SetCollisionOverlay(true);
1717 }});
1719 {.id = "workflow.mesen.overlay_off",
1720 .group = "Live Debugging",
1721 .label = "Disable Collision Overlay",
1722 .description = "Hide collision overlays in the active Mesen2 backend",
1723 .shortcut = "",
1724 .priority = 50,
1725 .callback = [emu_backend]() {
1726 emu_backend->SetCollisionOverlay(false);
1727 }});
1728 }
1729#endif
1730}
1731
1733 InitializeCommandPalette(session_id);
1734}
1735
1738 return;
1739
1740 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(),
1741 ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
1742 ImGui::SetNextWindowSize(ImVec2(800, 600), ImGuiCond_FirstUseEver);
1743
1744 bool show_search = true;
1745 if (ImGui::Begin(
1746 absl::StrFormat("%s Global Search", ICON_MD_MANAGE_SEARCH).c_str(),
1747 &show_search, ImGuiWindowFlags_NoCollapse)) {
1748 // Enhanced search input with focus management
1749 ImGui::SetNextItemWidth(-100);
1750 if (ImGui::IsWindowAppearing()) {
1751 ImGui::SetKeyboardFocusHere();
1752 }
1753
1754 bool input_changed = ImGui::InputTextWithHint(
1755 "##global_query",
1756 absl::StrFormat("%s Search everything...", ICON_MD_SEARCH).c_str(),
1758
1759 ImGui::SameLine();
1760 if (ImGui::Button(absl::StrFormat("%s Clear", ICON_MD_CLEAR).c_str())) {
1761 global_search_query_[0] = '\0';
1762 input_changed = true;
1763 }
1764
1765 ImGui::Separator();
1766
1767 // Tabbed search results for better organization
1768 if (gui::BeginThemedTabBar("SearchResultTabs")) {
1769 // Recent Files Tab
1770 if (ImGui::BeginTabItem(
1771 absl::StrFormat("%s Recent Files", ICON_MD_HISTORY).c_str())) {
1773 auto recent_files = manager.GetRecentFiles();
1774
1775 if (ImGui::BeginTable("RecentFilesTable", 3,
1776 ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg |
1777 ImGuiTableFlags_SizingStretchProp)) {
1778 ImGui::TableSetupColumn("File", ImGuiTableColumnFlags_WidthStretch,
1779 0.6f);
1780 ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed,
1781 80.0f);
1782 ImGui::TableSetupColumn("Action", ImGuiTableColumnFlags_WidthFixed,
1783 100.0f);
1784 ImGui::TableHeadersRow();
1785
1786 for (const auto& file : recent_files) {
1787 if (global_search_query_[0] != '\0' &&
1788 file.find(global_search_query_) == std::string::npos)
1789 continue;
1790
1791 ImGui::TableNextRow();
1792 ImGui::TableNextColumn();
1793 ImGui::Text("%s", util::GetFileName(file).c_str());
1794
1795 ImGui::TableNextColumn();
1796 std::string ext = util::GetFileExtension(file);
1797 if (ext == "sfc" || ext == "smc") {
1798 ImGui::TextColored(ImVec4(0.2f, 0.8f, 0.2f, 1.0f), tr("%s ROM"),
1800 } else if (ext == "yaze") {
1801 ImGui::TextColored(ImVec4(0.2f, 0.6f, 0.8f, 1.0f),
1802 tr("%s Project"), ICON_MD_FOLDER);
1803 } else {
1804 ImGui::Text(tr("%s File"), ICON_MD_DESCRIPTION);
1805 }
1806
1807 ImGui::TableNextColumn();
1808 ImGui::PushID(file.c_str());
1809 if (ImGui::Button(tr("Open"))) {
1810 auto status = editor_manager_->OpenRomOrProject(file);
1811 if (!status.ok()) {
1813 absl::StrCat("Failed to open: ", status.message()),
1815 }
1817 }
1818 ImGui::PopID();
1819 }
1820
1821 ImGui::EndTable();
1822 }
1823 ImGui::EndTabItem();
1824 }
1825
1826 // Labels Tab (only if ROM is loaded)
1827 auto* current_rom = editor_manager_->GetCurrentRom();
1828 if (current_rom && current_rom->resource_label()) {
1829 if (ImGui::BeginTabItem(
1830 absl::StrFormat("%s Labels", ICON_MD_LABEL).c_str())) {
1831 auto& labels = current_rom->resource_label()->labels_;
1832
1833 if (ImGui::BeginTable("LabelsTable", 3,
1834 ImGuiTableFlags_ScrollY |
1835 ImGuiTableFlags_RowBg |
1836 ImGuiTableFlags_SizingStretchProp)) {
1837 ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed,
1838 100.0f);
1839 ImGui::TableSetupColumn("Label", ImGuiTableColumnFlags_WidthStretch,
1840 0.4f);
1841 ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthStretch,
1842 0.6f);
1843 ImGui::TableHeadersRow();
1844
1845 for (const auto& type_pair : labels) {
1846 for (const auto& kv : type_pair.second) {
1847 if (global_search_query_[0] != '\0' &&
1848 kv.first.find(global_search_query_) == std::string::npos &&
1849 kv.second.find(global_search_query_) == std::string::npos)
1850 continue;
1851
1852 ImGui::TableNextRow();
1853 ImGui::TableNextColumn();
1854 ImGui::Text("%s", type_pair.first.c_str());
1855
1856 ImGui::TableNextColumn();
1857 if (ImGui::Selectable(kv.first.c_str(), false,
1858 ImGuiSelectableFlags_SpanAllColumns)) {
1859 // Future: navigate to related editor/location
1860 }
1861
1862 ImGui::TableNextColumn();
1863 ImGui::TextDisabled("%s", kv.second.c_str());
1864 }
1865 }
1866
1867 ImGui::EndTable();
1868 }
1869 ImGui::EndTabItem();
1870 }
1871 }
1872
1873 // Sessions Tab
1875 if (ImGui::BeginTabItem(
1876 absl::StrFormat("%s Sessions", ICON_MD_TAB).c_str())) {
1877 ImGui::Text(tr("Search and switch between active sessions:"));
1878
1879 for (size_t i = 0; i < session_coordinator_.GetTotalSessionCount();
1880 ++i) {
1881 std::string session_info =
1883 if (session_info == "[CLOSED SESSION]")
1884 continue;
1885
1886 if (global_search_query_[0] != '\0' &&
1887 session_info.find(global_search_query_) == std::string::npos)
1888 continue;
1889
1890 bool is_current =
1892 std::optional<gui::StyleColorGuard> current_guard;
1893 if (is_current) {
1894 current_guard.emplace(ImGuiCol_Text,
1895 ImVec4(0.2f, 0.8f, 0.2f, 1.0f));
1896 }
1897
1898 if (ImGui::Selectable(absl::StrFormat("%s %s %s", ICON_MD_TAB,
1899 session_info.c_str(),
1900 is_current ? "(Current)" : "")
1901 .c_str())) {
1902 if (!is_current) {
1905 }
1906 }
1907 }
1908 ImGui::EndTabItem();
1909 }
1910 }
1911
1913 }
1914
1915 // Status bar
1916 ImGui::Separator();
1917 ImGui::Text(tr("%s Global search across all YAZE data"), ICON_MD_INFO);
1918 }
1919 ImGui::End();
1920
1921 // Update visibility state
1922 if (!show_search) {
1924 }
1925}
1926
1927// ============================================================================
1928// Startup Surface Management (Single Source of Truth)
1929// ============================================================================
1930
1933 current_startup_surface_ = surface;
1934
1935 // Log state transitions for debugging
1936 const char* surface_names[] = {"Welcome", "Dashboard", "Editor"};
1937 LOG_INFO("UICoordinator", "Startup surface: %s -> %s",
1938 surface_names[static_cast<int>(old_surface)],
1939 surface_names[static_cast<int>(surface)]);
1940
1941 // Update dependent visibility flags
1942 switch (surface) {
1944 show_welcome_screen_ = true;
1945 show_editor_selection_ = false; // Dashboard hidden
1946 // Activity Bar will be hidden (checked via ShouldShowActivityBar)
1947 break;
1949 show_welcome_screen_ = false;
1950 show_editor_selection_ = true; // Dashboard shown
1951 break;
1953 show_welcome_screen_ = false;
1954 show_editor_selection_ = false; // Dashboard hidden
1955 break;
1956 }
1957}
1958
1960 // Respect CLI overrides
1962 return false;
1963 }
1965 return true;
1966 }
1967
1968 // Default: show welcome only when in welcome state and not manually closed
1971}
1972
1974 // Respect CLI overrides
1976 return false;
1977 }
1979 return true;
1980 }
1981
1982 // Default: show dashboard only when in dashboard state
1984}
1985
1987 // Sidebar would consume the entire screen on compact (iPhone portrait)
1988 if (IsCompactLayout()) {
1989 return false;
1990 }
1991
1992 // Activity Bar hidden on cold start (welcome screen)
1993 // Only show after ROM is loaded
1995 return false;
1996 }
1997
1998 // Check if ROM is actually loaded
1999 if (editor_manager_) {
2000 auto* current_rom = editor_manager_->GetCurrentRom();
2001 if (!current_rom || !current_rom->is_loaded()) {
2002 return false;
2003 }
2004 }
2005
2006 return true;
2007}
2008
2009} // namespace editor
2010} // namespace yaze
static Application & Instance()
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
Definition rom.h:28
project::ResourceLabelManager * resource_label()
Definition rom.h:162
auto short_name() const
Definition rom.h:159
bool is_loaded() const
Definition rom.h:144
void SaveHistory(const std::string &filepath)
Save command usage history to disk.
void Clear()
Clear all commands (and forget every registered provider).
void AddCommand(const std::string &name, const std::string &category, const std::string &description, const std::string &shortcut, std::function< void()> callback)
void LoadHistory(const std::string &filepath)
Load command usage history from disk.
std::vector< CommandEntry > GetAllCommands() const
Get all registered commands.
void RecordUsage(const std::string &name)
void RegisterProvider(std::unique_ptr< CommandProvider > provider)
std::vector< CommandEntry > GetRecentCommands(int limit=10)
std::vector< CommandEntry > GetFrequentCommands(int limit=10)
static int FuzzyScore(const std::string &text, const std::string &query)
The EditorManager controls the main editor window and manages the various editor classes.
void SaveWorkspacePreset(const std::string &name)
void SwitchToEditor(EditorType editor_type, bool force_visible=false, bool from_dialog=false) override
void SwitchToSession(size_t index)
Rom * GetCurrentRom() const override
WorkspaceManager * workspace_manager()
void LoadWorkspacePreset(const std::string &name)
void ShowProjectManagement()
Injects dependencies into all editors within an EditorSet.
absl::Status CreateNewProjectFromRom(const std::string &template_name, const std::string &rom_path, const std::string &project_name, const std::string &project_path=std::string())
RightDrawerManager * right_drawer_manager()
void ApplyLayoutPreset(const std::string &preset_name)
absl::Status LoadRom()
Load a ROM file into a new or existing session.
project::YazeProject * GetCurrentProject()
absl::Status OpenRomOrProject(const std::string &filename)
Manages editor types, categories, and lifecycle.
static EditorType GetEditorTypeFromCategory(const std::string &category)
static std::vector< std::string > GetDefaultWindows(EditorType type)
void Open(const std::string &initial_template="")
void SetCreateCallback(CreateCallback cb)
void Show(const char *name)
void Hide(const char *name)
Handles all project file operations with ROM-first workflow.
bool DrawDrawerToggleButtons()
Draw drawer toggle buttons for the status cluster.
bool IsDrawerActive(DrawerType type) const
Check if a specific drawer is active.
bool IsDrawerExpanded() const
Check if any drawer is currently expanded (or animating closed)
float GetDrawerWidth() const
Get the width of the drawer when expanded.
Handles all ROM file I/O operations.
High-level orchestrator for multi-session UI.
void * GetSession(size_t index) const
size_t GetActiveSessionIndex() const
Compact zero-based UI position in sessions_.
std::string GetSessionDisplayName(size_t index) const
bool IsSessionClosed(size_t index) const
size_t GetActiveSessionId() const
Stable workspace identity that is never reused while this coordinator lives.
const std::unordered_map< std::string, Shortcut > & GetShortcuts() const
void Show(const std::string &message, ToastType type=ToastType::kInfo, float ttl_seconds=3.0f)
ShortcutManager & shortcut_manager_
void DrawMaterialButton(const std::string &text, const std::string &icon, const ImVec4 &color, std::function< void()> callback, bool enabled=true)
void SetPanelSidebarVisible(bool visible)
void SetSessionSwitcherVisible(bool visible)
void SetGlobalSearchVisible(bool visible)
void HidePopup(const std::string &popup_name)
void SetStartupSurface(StartupSurface surface)
SessionCoordinator & session_coordinator_
void InitializeCommandPalette(size_t session_id)
Initialize command palette with all discoverable commands.
void RefreshCommandPalette(size_t session_id)
Refresh command palette commands (call after session switch)
void SetWelcomeScreenManuallyClosed(bool closed)
void SetEmulatorVisible(bool visible)
void DrawNotificationBell(bool show_dirty, bool has_dirty_rom, bool show_session, bool has_multiple_sessions)
void SetAsmEditorVisible(bool visible)
void SetWelcomeScreenVisible(bool visible)
WindowDelegate & window_delegate_
bool DrawMenuBarIconButton(const char *icon, const char *tooltip, bool is_active=false)
void ShowPopup(const std::string &popup_name)
ProjectManager & project_manager_
StartupVisibility welcome_behavior_override_
StartupVisibility dashboard_behavior_override_
StartupSurface current_startup_surface_
void PositionWindow(const std::string &window_name, float x, float y)
void SetWindowSize(const std::string &window_name, float width, float height)
NewProjectDialog new_project_dialog_
void SetWelcomeScreenBehavior(StartupVisibility mode)
WorkspaceWindowManager & window_manager_
void SetDashboardBehavior(StartupVisibility mode)
void SetCommandPaletteVisible(bool visible)
std::unique_ptr< WelcomeScreen > welcome_screen_
static float GetMenuBarIconButtonWidth()
UICoordinator(EditorManager *editor_manager, RomFileManager &rom_manager, ProjectManager &project_manager, EditorRegistry &editor_registry, WorkspaceWindowManager &card_registry, SessionCoordinator &session_coordinator, WindowDelegate &window_delegate, ToastManager &toast_manager, PopupManager &popup_manager, ShortcutManager &shortcut_manager)
void CenterWindow(const std::string &window_name)
Low-level window operations with minimal dependencies.
Central registry for all editor cards with session awareness and dependency injection.
void HideAllWindowsInCategory(size_t session_id, const std::string &category)
void SetSidebarVisible(bool visible, bool notify=true)
std::vector< WindowDescriptor > GetWindowsInCategory(size_t session_id, const std::string &category) const
const WindowDescriptor * GetWindowDescriptor(size_t session_id, const std::string &base_window_id) const
std::vector< std::string > GetWindowsInSession(size_t session_id) const
bool OpenWindow(size_t session_id, const std::string &base_window_id)
bool IsWindowPinned(size_t session_id, const std::string &base_window_id) const
void MarkWindowRecentlyUsed(const std::string &window_id)
static BackgroundRenderer & Get()
static void EndTableWithTheming()
static bool BeginTableWithTheming(const char *str_id, int columns, ImGuiTableFlags flags=0, const ImVec2 &outer_size=ImVec2(0, 0), float inner_width=0.0f)
RAII guard for ImGui style colors.
Definition style_guard.h:27
RAII guard for ImGui style vars.
Definition style_guard.h:68
const Theme & GetCurrentTheme() const
static ThemeManager & Get()
static RecentFilesManager & GetInstance()
Definition project.h:441
static absl::StatusOr< std::filesystem::path > GetConfigDirectory()
Get the user-specific configuration directory for YAZE.
#define ICON_MD_NOTIFICATIONS
Definition icons.h:1335
#define ICON_MD_SETTINGS
Definition icons.h:1699
#define ICON_MD_INFO
Definition icons.h:993
#define ICON_MD_FOLDER_SPECIAL
Definition icons.h:815
#define ICON_MD_SEARCH
Definition icons.h:1673
#define ICON_MD_STAR
Definition icons.h:1848
#define ICON_MD_FULLSCREEN_EXIT
Definition icons.h:862
#define ICON_MD_EXPAND_LESS
Definition icons.h:702
#define ICON_MD_LABEL
Definition icons.h:1053
#define ICON_MD_LIST_ALT
Definition icons.h:1095
#define ICON_MD_VIDEOGAME_ASSET
Definition icons.h:2076
#define ICON_MD_VISIBILITY
Definition icons.h:2101
#define ICON_MD_LIST
Definition icons.h:1094
#define ICON_MD_MANAGE_SEARCH
Definition icons.h:1172
#define ICON_MD_VISIBILITY_OFF
Definition icons.h:2102
#define ICON_MD_LAYERS
Definition icons.h:1068
#define ICON_MD_CLEAR
Definition icons.h:416
#define ICON_MD_DESCRIPTION
Definition icons.h:539
#define ICON_MD_HELP_OUTLINE
Definition icons.h:935
#define ICON_MD_DASHBOARD
Definition icons.h:517
#define ICON_MD_TAB
Definition icons.h:1930
#define ICON_MD_FOLDER
Definition icons.h:809
#define ICON_MD_PUSH_PIN
Definition icons.h:1529
#define ICON_MD_FIBER_MANUAL_RECORD
Definition icons.h:739
#define ICON_MD_SMART_TOY
Definition icons.h:1781
#define ICON_MD_HISTORY
Definition icons.h:946
#define LOG_ERROR(category, format,...)
Definition log.h:109
#define LOG_INFO(category, format,...)
Definition log.h:105
Definition input.cc:23
constexpr const char * kDisplaySettings
StartupSurface
Represents the current startup surface state.
std::string PrintShortcut(const std::vector< ImGuiKey > &keys)
ImVec4 ConvertColorToImVec4(const Color &color)
Definition color.h:134
void ColoredText(const char *text, const ImVec4 &color)
ImVec4 GetSurfaceContainerHighestVec4()
bool BeginThemedTabBar(const char *id, ImGuiTabBarFlags flags)
A stylized tab bar with "Mission Control" branding.
void EndThemedTabBar()
ImVec4 GetPrimaryVec4()
ImVec4 GetTextDisabledVec4()
ImVec4 GetTextSecondaryVec4()
void ColoredTextF(const ImVec4 &color, const char *fmt,...)
ImVec4 GetSurfaceContainerHighVec4()
constexpr ImVec2 kDefaultModalSize
Definition input.h:21
ImVec4 GetOnSurfaceVariantVec4()
ImVec4 GetSurfaceContainerVec4()
std::string GetFileName(const std::string &filename)
Gets the filename from a full path.
Definition file_util.cc:19
std::string GetFileExtension(const std::string &filename)
Gets the file extension from a filename.
Definition file_util.cc:15
StartupVisibility
Tri-state toggle used for startup UI visibility controls.
static constexpr const char * kLayout
Represents a single session, containing a ROM and its associated editors.
std::function< void(const std::string &) toggle_pin)
std::function< void(const std::string &) remove)
std::function< void(const std::string &) create_from_template)
std::unordered_map< std::string, std::unordered_map< std::string, std::string > > labels_
Definition project.h:432
bool project_opened() const
Definition project.h:348