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
110 [this](const std::string& template_name, const std::string& rom_path,
111 const std::string& project_name) -> absl::Status {
112 if (!editor_manager_) {
113 return absl::FailedPreconditionError(
114 "Editor manager is not available");
115 }
117 template_name, rom_path, project_name);
118 if (!status.ok()) {
120 absl::StrFormat("Failed to create project: %s", status.message()),
122 return status;
123 }
124 toast_manager_.Show(absl::StrFormat("Created project \"%s\" from %s",
125 project_name, template_name),
128 return absl::OkStatus();
129 });
130
131 welcome_screen_->SetOpenProjectCallback([this](const std::string& filepath) {
132 if (editor_manager_) {
133 auto status = editor_manager_->OpenRomOrProject(filepath);
134 if (!status.ok()) {
136 absl::StrFormat("Failed to open project: %s", status.message()),
138 } else {
139 // Transition to Dashboard on successful project open
141 }
142 }
143 });
144
145 welcome_screen_->SetOpenProjectManagementCallback([this]() {
146 if (editor_manager_) {
149 }
150 });
151
152 welcome_screen_->SetOpenPrototypeResearchCallback([this]() {
153 if (!editor_manager_) {
154 return;
155 }
157 window_manager_.OpenWindow("graphics.prototype_viewer");
160 });
161
162 welcome_screen_->SetOpenAssemblyEditorNoRomCallback([this]() {
163 if (!editor_manager_) {
164 return;
165 }
167 window_manager_.OpenWindow("assembly.code_editor");
170 });
171}
172
186
188 if (dashboard_behavior_override_ == mode) {
189 return;
190 }
192 if (mode == StartupVisibility::kShow) {
193 // Only transition to dashboard if we're not in welcome
196 }
197 } else if (mode == StartupVisibility::kHide) {
198 // If hiding dashboard, transition to editor state
201 }
202 }
203}
204
206 if (!ImGui::GetCurrentContext()) {
207 return;
208 }
209
210 const ImGuiViewport* viewport = ImGui::GetMainViewport();
211 if (!viewport) {
212 return;
213 }
214
215 ImDrawList* bg_draw_list =
216 ImGui::GetBackgroundDrawList(const_cast<ImGuiViewport*>(viewport));
217
218 auto& theme_manager = gui::ThemeManager::Get();
219 auto current_theme = theme_manager.GetCurrentTheme();
220 auto& bg_renderer = gui::BackgroundRenderer::Get();
221
222 // Draw grid covering the entire main viewport
223 ImVec2 grid_pos = viewport->WorkPos;
224 ImVec2 grid_size = viewport->WorkSize;
225 bg_renderer.RenderDockingBackground(bg_draw_list, grid_pos, grid_size,
226 current_theme.primary);
227}
228
230 // Note: Theme styling is applied by ThemeManager, not here
231 // This is called from EditorManager::Update() - don't call menu bar stuff
232 // here
233
234 // Draw UI windows and dialogs
235 // Session dialogs are drawn by SessionCoordinator separately to avoid
236 // duplication
237 DrawCommandPalette(); // Ctrl+Shift+P (+ Window Finder seeds window:)
238 DrawPanelFinder(); // Legacy modal (kept for direct flag use)
239 DrawShortcutsBrowser(); // Help > Keyboard Shortcuts
240 DrawGlobalSearch(); // Ctrl+Shift+K
241 DrawWorkspacePresetDialogs(); // Save/Load workspace dialogs
242 DrawLayoutPresets(); // Layout preset dialogs
243 DrawWelcomeScreen(); // Welcome screen
244 DrawProjectHelp(); // Project help
245 DrawWindowManagementUI(); // Window management
246
247#ifdef YAZE_BUILD_AGENT_UI
248 if (show_ai_agent_) {
249 if (editor_manager_) {
250 editor_manager_->ShowAIAgent();
251 }
252 show_ai_agent_ = false;
253 }
254
255 if (show_chat_history_) {
256 if (editor_manager_) {
257 editor_manager_->ShowChatHistory();
258 }
259 show_chat_history_ = false;
260 }
261
263 if (editor_manager_) {
264 if (auto* right_panel = editor_manager_->right_drawer_manager()) {
265 right_panel->OpenDrawer(RightDrawerManager::DrawerType::kProposals);
266 }
267 }
268 show_proposal_drawer_ = false;
269 }
270#endif
271
272 // Draw popups and toasts
275}
276
278 const ImGuiViewport* viewport = ImGui::GetMainViewport();
279 if (!viewport) {
280 return false;
281 }
282 const float width = viewport->WorkSize.x;
283#if defined(__APPLE__) && TARGET_OS_IOS == 1
284 // Use hysteresis to avoid layout thrash while iPad windows are being
285 // interactively resized around the compact breakpoint.
286 static bool compact_mode = false;
287 constexpr float kEnterCompactWidth = 900.0f;
288 constexpr float kExitCompactWidth = 940.0f;
289 compact_mode =
290 compact_mode ? (width < kExitCompactWidth) : (width < kEnterCompactWidth);
291 return compact_mode;
292#else
293 return width < 900.0f;
294#endif
295}
296
297// =============================================================================
298// Menu Bar Helpers
299// =============================================================================
300
301bool UICoordinator::DrawMenuBarIconButton(const char* icon, const char* tooltip,
302 bool is_active) {
303 // Consistent button styling: transparent background, themed text
304 gui::StyleColorGuard btn_guard(
305 {{ImGuiCol_Button, ImVec4(0, 0, 0, 0)},
306 {ImGuiCol_ButtonHovered, gui::GetSurfaceContainerHighVec4()},
307 {ImGuiCol_ButtonActive, gui::GetSurfaceContainerHighestVec4()},
308 {ImGuiCol_Text,
310
311 bool clicked = ImGui::SmallButton(icon);
312
313 if (tooltip && ImGui::IsItemHovered()) {
314 ImGui::SetTooltip("%s", tooltip);
315 }
316
317 return clicked;
318}
319
321 // SmallButton width = text width + frame padding * 2
322 const float frame_padding = ImGui::GetStyle().FramePadding.x;
323 // Use a standard icon width (Material Design icons are uniform)
324 const float icon_width = ImGui::CalcTextSize(ICON_MD_SETTINGS).x;
325 return icon_width + frame_padding * 2.0f;
326}
327
329 // Right-aligned status cluster: dirty indicator, session, bell, drawers overflow.
330 // Drawers overflow is positioned using SCREEN coordinates (from viewport) so it
331 // stays fixed even when the dockspace resizes due to panel open/close.
332 //
333 // Layout: [●][📄▾][🔔] [drawers][⬆]
334 // ^^^ shifts with dockspace ^^^ ^^^ fixed screen position ^^^
335
336 auto* current_rom = editor_manager_->GetCurrentRom();
337
338 const float item_spacing = 6.0f;
339 const float padding = 8.0f;
340
341 auto CalcSmallButtonWidth = [](const char* label) -> float {
342 // SmallButton width = text width + frame padding * 2
343 const float frame_padding = ImGui::GetStyle().FramePadding.x;
344 const float text_w = ImGui::CalcTextSize(label).x;
345 return text_w + frame_padding * 2.0f;
346 };
347
348 // Get TRUE viewport dimensions (not affected by dockspace resize)
349 const ImGuiViewport* viewport = ImGui::GetMainViewport();
350 const float true_viewport_right = viewport->WorkPos.x + viewport->WorkSize.x;
351
352 const bool has_panel_toggles =
354 float panel_buttons_width = 0.0f;
355 if (has_panel_toggles) {
357 }
358
359 // Reserve only the real button footprint so compact icon toggles do not
360 // leave a dead gap before the right edge cluster.
361 float panel_region_width = panel_buttons_width;
362#ifdef __EMSCRIPTEN__
363 // WASM hide menu bar toggle (drawn inline after panel buttons).
364 panel_region_width +=
365 CalcSmallButtonWidth(ICON_MD_EXPAND_LESS) + item_spacing;
366#endif
367
368 // Calculate screen X position for panel toggles (fixed at viewport right edge)
369 float panel_screen_x = true_viewport_right - panel_region_width;
373 }
374
375 // Available space for status cluster (dirty, session, bell) ends where the
376 // drawers overflow region begins.
377 const float window_width = ImGui::GetWindowWidth();
378 const float window_screen_x = ImGui::GetWindowPos().x;
379 const float menu_items_end = ImGui::GetCursorPosX() + 16.0f;
380
381 // Convert panel screen X to window-local coordinates for space calculation
382 float panel_local_x = panel_screen_x - window_screen_x;
383 float region_end =
384 std::min(window_width - padding, panel_local_x - item_spacing);
385
386 // Progressive show/hide when space is tight
387 bool has_dirty_rom =
388 current_rom && current_rom->is_loaded() && current_rom->dirty();
389 bool has_multiple_sessions = session_coordinator_.HasMultipleSessions();
390
391 float dirty_width =
392 ImGui::CalcTextSize(ICON_MD_FIBER_MANUAL_RECORD).x + item_spacing;
393 const float session_width = CalcSmallButtonWidth(ICON_MD_LAYERS);
394
395 const float available_width = region_end - menu_items_end - padding;
396
397 // Minimum required width: just the bell (always visible)
398 float required_width = CalcSmallButtonWidth(ICON_MD_NOTIFICATIONS);
399
400 // Priority (highest to lowest): Bell > Dirty > Session
401
402 // Try to fit session button (medium priority)
403 bool show_session =
404 has_multiple_sessions &&
405 (required_width + session_width + item_spacing) <= available_width;
406 if (show_session) {
407 required_width += session_width + item_spacing;
408 }
409
410 // Try to fit dirty indicator (high priority - only hide if extremely tight)
411 bool show_dirty =
412 has_dirty_rom && (required_width + dirty_width) <= available_width;
413 if (show_dirty) {
414 required_width += dirty_width;
415 }
416
417 // Calculate start position (right-align within available space)
418 float start_pos = std::max(menu_items_end, region_end - required_width);
419
420 // =========================================================================
421 // DRAW STATUS CLUSTER (shifts with dockspace)
422 // =========================================================================
423 ImGui::SameLine(start_pos);
424 gui::StyleVarGuard item_spacing_guard(ImGuiStyleVar_ItemSpacing,
425 ImVec2(item_spacing, 0.0f));
426
427 // 1. Dirty badge - warning color dot
428 if (show_dirty) {
429 const auto& theme = gui::ThemeManager::Get().GetCurrentTheme();
431 gui::ConvertColorToImVec4(theme.warning));
432 if (ImGui::IsItemHovered()) {
433 ImGui::SetTooltip(tr("Unsaved changes: %s"),
434 current_rom->short_name().c_str());
435 }
436 ImGui::SameLine();
437 }
438
439 // 2. Session button - layers icon
440 if (show_session) {
442 ImGui::SameLine();
443 }
444
445 // 3. Notification bell (pass visibility flags for enhanced tooltip)
446 DrawNotificationBell(show_dirty, has_dirty_rom, show_session,
447 has_multiple_sessions);
448
449 // =========================================================================
450 // DRAW DRAWERS OVERFLOW (fixed screen position)
451 // =========================================================================
452 if (has_panel_toggles) {
453 float menu_bar_y = ImGui::GetCursorScreenPos().y;
454 ImGui::SetCursorScreenPos(ImVec2(panel_screen_x, menu_bar_y));
456 }
457
458#ifdef __EMSCRIPTEN__
459 // WASM toggle button - also at fixed position
460 ImGui::SameLine();
462 "Hide menu bar (Alt to restore)")) {
463 show_menu_bar_ = false;
464 }
465#endif
466}
467
469 // Only draw when menu bar is hidden (primarily for WASM builds)
470 if (show_menu_bar_) {
471 return;
472 }
473
474 // Small floating button in top-left corner to restore menu bar
475 ImGuiWindowFlags flags =
476 ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize |
477 ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar |
478 ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_AlwaysAutoResize |
479 ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoSavedSettings;
480
481 ImGui::SetNextWindowPos(ImVec2(8, 8));
482 ImGui::SetNextWindowBgAlpha(0.7f);
483
484 if (ImGui::Begin("##MenuBarRestore", nullptr, flags)) {
485 gui::StyleColorGuard btn_guard(
486 {{ImGuiCol_Button, gui::GetSurfaceContainerVec4()},
487 {ImGuiCol_ButtonHovered, gui::GetSurfaceContainerHighVec4()},
488 {ImGuiCol_ButtonActive, gui::GetSurfaceContainerHighestVec4()},
489 {ImGuiCol_Text, gui::GetPrimaryVec4()}});
490
491 if (ImGui::Button(ICON_MD_FULLSCREEN_EXIT, ImVec2(32, 32))) {
492 show_menu_bar_ = true;
493 }
494
495 if (ImGui::IsItemHovered()) {
496 ImGui::SetTooltip(tr("Show menu bar (Alt)"));
497 }
498 }
499 ImGui::End();
500
501 // Also check for Alt key to restore menu bar
502 if (ImGui::IsKeyPressed(ImGuiKey_LeftAlt) ||
503 ImGui::IsKeyPressed(ImGuiKey_RightAlt)) {
504 show_menu_bar_ = true;
505 }
506}
507
508void UICoordinator::DrawNotificationBell(bool show_dirty, bool has_dirty_rom,
509 bool show_session,
510 bool has_multiple_sessions) {
511 size_t unread = toast_manager_.GetUnreadCount();
512 auto* current_rom = editor_manager_->GetCurrentRom();
513 auto* right_panel = editor_manager_->right_drawer_manager();
514
515 bool is_active =
516 right_panel && right_panel->IsDrawerActive(
518
520 unread > 0 || is_active)) {
521 if (right_panel) {
522 right_panel->ToggleDrawer(RightDrawerManager::DrawerType::kNotifications);
524 }
525 }
526
527 // Enhanced tooltip showing notifications + hidden status items
528 if (ImGui::IsItemHovered()) {
529 ImGui::BeginTooltip();
530
531 // Notifications
532 if (unread > 0) {
533 gui::ColoredTextF(gui::GetPrimaryVec4(), "%s %zu new notification%s",
534 ICON_MD_NOTIFICATIONS, unread, unread == 1 ? "" : "s");
535 } else {
536 gui::ColoredText(ICON_MD_NOTIFICATIONS " No new notifications",
538 }
539
540 ImGui::TextDisabled(tr("Click to open Notifications panel"));
541
542 // Show hidden status items if any
543 if (!show_dirty && has_dirty_rom) {
544 ImGui::Separator();
546 gui::ThemeManager::Get().GetCurrentTheme().warning),
547 ICON_MD_FIBER_MANUAL_RECORD " Unsaved changes: %s",
548 current_rom->short_name().c_str());
549 }
550
551 if (!show_session && has_multiple_sessions) {
552 if (!show_dirty && has_dirty_rom) {
553 // Already had a separator
554 } else {
555 ImGui::Separator();
556 }
558 ICON_MD_LAYERS " %zu sessions active",
560 }
561
562 ImGui::EndTooltip();
563 }
564}
565
567 auto* current_rom = editor_manager_->GetCurrentRom();
568
569 // Store button position for popup anchoring
570 ImVec2 button_min = ImGui::GetCursorScreenPos();
571
572 std::string tooltip =
573 current_rom && current_rom->is_loaded()
574 ? absl::StrFormat("%s\n%zu sessions open (Ctrl+Tab)",
575 current_rom->short_name().c_str(),
577 : absl::StrFormat("No ROM loaded\n%zu sessions open (Ctrl+Tab)",
579
580 if (DrawMenuBarIconButton(ICON_MD_LAYERS, tooltip.c_str(), false)) {
581 ImGui::OpenPopup("##SessionSwitcherPopup");
582 }
583
584 ImVec2 button_max = ImGui::GetItemRectMax();
585
586 // Anchor popup to right edge - position so right edge aligns with button
587 const float popup_width = 250.0f;
588 const float screen_width = ImGui::GetIO().DisplaySize.x;
589 const float popup_x =
590 std::min(button_min.x, screen_width - popup_width - 10.0f);
591
592 ImGui::SetNextWindowPos(ImVec2(popup_x, button_max.y + 2.0f),
593 ImGuiCond_Appearing);
594
595 // Session switcher popup
596 if (ImGui::BeginPopup("##SessionSwitcherPopup")) {
597 ImGui::Text(ICON_MD_LAYERS " Sessions");
598 ImGui::Separator();
599
600 for (size_t i = 0; i < session_coordinator_.GetTotalSessionCount(); ++i) {
602 continue;
603
604 auto* session =
606 if (!session)
607 continue;
608
609 Rom* rom = &session->rom;
610 ImGui::PushID(static_cast<int>(i));
611
612 bool is_current = (rom == current_rom);
613 std::optional<gui::StyleColorGuard> current_guard;
614 if (is_current) {
615 current_guard.emplace(ImGuiCol_Text, gui::GetPrimaryVec4());
616 }
617
618 std::string label =
619 rom->is_loaded()
620 ? absl::StrFormat("%s %s", ICON_MD_DESCRIPTION,
621 rom->short_name().c_str())
622 : absl::StrFormat("%s Session %zu", ICON_MD_DESCRIPTION, i + 1);
623
624 if (ImGui::Selectable(label.c_str(), is_current)) {
626 }
627
628 ImGui::PopID();
629 }
630
631 ImGui::EndPopup();
632 }
633}
634
635// ============================================================================
636// Session UI Delegation
637// ============================================================================
638// All session-related UI is now managed by SessionCoordinator to eliminate
639// duplication. UICoordinator methods delegate to SessionCoordinator.
640
644
648
650 if (visible) {
652 } else {
654 }
655}
656
657void UICoordinator::ShowCommandPalette(const char* initial_query) {
658 const size_t session_id = session_coordinator_.GetActiveSessionId();
660 RefreshCommandPalette(session_id);
661 } else {
662 InitializeCommandPalette(session_id);
663 }
664 if (initial_query != nullptr) {
665 std::snprintf(command_palette_query_, sizeof(command_palette_query_), "%s",
666 initial_query);
668 }
670}
671
673 // Consolidate window discovery into the command palette (`window:` prefix).
674 ShowCommandPalette("window: ");
675}
676
678 if (visible) {
680 return;
681 }
682 show_command_palette_ = false;
683}
684
685// Emulator visibility delegates to WorkspaceWindowManager (single source of truth)
687 size_t session_id = session_coordinator_.GetActiveSessionId();
688 auto emulator_windows =
689 window_manager_.GetWindowsInCategory(session_id, "Emulator");
690 for (const auto& window : emulator_windows) {
691 if (window.visibility_flag && *window.visibility_flag) {
692 return true;
693 }
694 }
695 return false;
696}
697
699 size_t session_id = session_coordinator_.GetActiveSessionId();
700 if (visible) {
701 auto default_windows =
703 for (const auto& window_id : default_windows) {
704 window_manager_.OpenWindow(session_id, window_id);
705 }
706 } else {
707 window_manager_.HideAllWindowsInCategory(session_id, "Emulator");
708 }
709}
710
711// Assembly editor visibility: same delegation pattern as the emulator. The
712// `show_asm_editor_` boolean was a parallel source of truth that could drift
713// when the user closed a single Assembly panel via its window close button;
714// reading through the category means the user's last interaction always wins.
716 size_t session_id = session_coordinator_.GetActiveSessionId();
717 auto windows = window_manager_.GetWindowsInCategory(session_id, "Assembly");
718 for (const auto& window : windows) {
719 if (window.visibility_flag && *window.visibility_flag) {
720 return true;
721 }
722 }
723 return false;
724}
725
727 size_t session_id = session_coordinator_.GetActiveSessionId();
728 if (visible) {
729 auto default_windows =
731 for (const auto& window_id : default_windows) {
732 window_manager_.OpenWindow(session_id, window_id);
733 }
734 } else {
735 window_manager_.HideAllWindowsInCategory(session_id, "Assembly");
736 }
737}
738
739// ============================================================================
740// Layout and Window Management UI
741// ============================================================================
742
744 // TODO: [EditorManagerRefactor] Implement full layout preset UI with
745 // save/load For now, this is accessed via Window menu items that call
746 // workspace_manager directly
747}
748
750 // ============================================================================
751 // CENTRALIZED WELCOME SCREEN LOGIC (using StartupSurface state)
752 // ============================================================================
753 // Uses ShouldShowWelcome() as single source of truth
754 // Auto-transitions to Dashboard on ROM load
755 // Activity Bar hidden when welcome is visible
756 // ============================================================================
757
758 if (!editor_manager_) {
759 LOG_ERROR("UICoordinator",
760 "EditorManager is null - cannot check ROM state");
761 return;
762 }
763
764 if (!welcome_screen_) {
765 LOG_ERROR("UICoordinator", "WelcomeScreen object is null - cannot render");
766 return;
767 }
768
769 // Check ROM state and update startup surface accordingly
770 auto* current_rom = editor_manager_->GetCurrentRom();
771 bool rom_is_loaded = current_rom && current_rom->is_loaded();
772
773 // Auto-transition: ROM loaded -> Dashboard or Editor
774 if (rom_is_loaded && current_startup_surface_ == StartupSurface::kWelcome) {
777 } else {
779 }
780 }
781
782 // Auto-transition: ROM unloaded -> Welcome (reset to welcome state)
783 if (!rom_is_loaded && current_startup_surface_ != StartupSurface::kWelcome &&
786 }
787
788 // Use centralized visibility check
789 if (!ShouldShowWelcome()) {
790 // Project creation can start from the menu or command palette while the
791 // welcome surface is hidden.
793 return;
794 }
795
796 // Provide context state for first-run guidance.
797 welcome_screen_->SetContextState(rom_is_loaded);
798
799 // Update recent projects before showing (cheap no-op when the
800 // RecentFilesManager generation counter hasn't changed).
801 welcome_screen_->RefreshRecentProjects();
802
803 // Pass layout offsets so welcome screen centers within dockspace region
804 // Note: Activity Bar is hidden when welcome is shown, so left_offset = 0
805 float left_offset =
807 float right_offset = editor_manager_->GetRightLayoutOffset();
808 welcome_screen_->SetLayoutOffsets(left_offset, right_offset);
809
810 // Show the welcome screen window
811 bool is_open = true;
812 welcome_screen_->Show(&is_open);
813
814 // Draw after the welcome window so the modal layers above it.
816
817 // If user closed it via X button, respect that and transition to appropriate state
818 if (!is_open) {
820 // Transition to Dashboard if ROM loaded, stay in Editor state otherwise
821 if (rom_is_loaded) {
823 }
824 }
825}
826
828 // TODO: [EditorManagerRefactor] Implement project help dialog
829 // Show context-sensitive help based on current editor and ROM state
830}
831
834 ImGui::Begin("Save Workspace Preset", &show_save_workspace_preset_,
835 ImGuiWindowFlags_AlwaysAutoResize);
836 static char preset_name[128] = "";
837 ImGui::InputText(tr("Name"), preset_name, IM_ARRAYSIZE(preset_name));
838 if (ImGui::Button(tr("Save"), gui::kDefaultModalSize)) {
839 if (strlen(preset_name) > 0) {
843 preset_name[0] = '\0';
844 }
845 }
846 ImGui::SameLine();
847 if (ImGui::Button(tr("Cancel"), gui::kDefaultModalSize)) {
849 preset_name[0] = '\0';
850 }
851 ImGui::End();
852 }
853
855 ImGui::Begin("Load Workspace Preset", &show_load_workspace_preset_,
856 ImGuiWindowFlags_AlwaysAutoResize);
857
858 // Lazy load workspace presets when UI is accessed
860
861 if (auto* workspace_manager = editor_manager_->workspace_manager()) {
862 for (const auto& name : workspace_manager->workspace_presets()) {
863 if (ImGui::Selectable(name.c_str())) {
867 }
868 }
869 if (workspace_manager->workspace_presets().empty())
870 ImGui::Text(tr("No presets found"));
871 }
872 ImGui::End();
873 }
874}
875
877 // TODO: [EditorManagerRefactor] Implement window management dialog
878 // Provide UI for toggling window visibility, managing docking, etc.
879}
880
882 // Draw all registered popups
884}
885
886void UICoordinator::ShowPopup(const std::string& popup_name) {
887 popup_manager_.Show(popup_name.c_str());
888}
889
890void UICoordinator::HidePopup(const std::string& popup_name) {
891 popup_manager_.Hide(popup_name.c_str());
892}
893
895 // Display Settings is now a popup managed by PopupManager
896 // Delegate directly to PopupManager instead of UICoordinator
898}
899
900// ============================================================================
901// Sidebar visibility delegates to WorkspaceWindowManager
902// ============================================================================
903
907
911
915
919
923
924// Material Design component helpers
925void UICoordinator::DrawMaterialButton(const std::string& text,
926 const std::string& icon,
927 const ImVec4& color,
928 std::function<void()> callback,
929 bool enabled) {
930 std::optional<gui::StyleColorGuard> disabled_guard;
931 if (!enabled) {
932 disabled_guard.emplace(std::initializer_list<gui::StyleColorGuard::Entry>{
933 {ImGuiCol_Button, gui::GetSurfaceContainerHighestVec4()},
934 {ImGuiCol_Text, gui::GetOnSurfaceVariantVec4()}});
935 }
936
937 std::string button_text =
938 absl::StrFormat("%s %s", icon.c_str(), text.c_str());
939 if (ImGui::Button(button_text.c_str())) {
940 if (enabled && callback) {
941 callback();
942 }
943 }
944}
945
946// Layout and positioning helpers
947void UICoordinator::CenterWindow(const std::string& window_name) {
948 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(),
949 ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
950}
951
952void UICoordinator::PositionWindow(const std::string& window_name, float x,
953 float y) {
954 ImGui::SetNextWindowPos(ImVec2(x, y), ImGuiCond_Appearing);
955}
956
957void UICoordinator::SetWindowSize(const std::string& window_name, float width,
958 float height) {
959 ImGui::SetNextWindowSize(ImVec2(width, height), ImGuiCond_FirstUseEver);
960}
961
964 return;
965
966 // Initialize command palette on first use
969 }
970
971 using namespace ImGui;
972 auto& theme = gui::ThemeManager::Get().GetCurrentTheme();
973
974 SetNextWindowPos(GetMainViewport()->GetCenter(), ImGuiCond_Appearing,
975 ImVec2(0.5f, 0.5f));
976 SetNextWindowSize(ImVec2(800, 600), ImGuiCond_FirstUseEver);
977
978 bool show_palette = true;
979 if (Begin(absl::StrFormat("%s Command Palette", ICON_MD_SEARCH).c_str(),
980 &show_palette, ImGuiWindowFlags_NoCollapse)) {
981 // Search input with focus management
982 SetNextItemWidth(-100);
983 if (IsWindowAppearing()) {
984 SetKeyboardFocusHere();
986 }
987
988 bool input_changed = InputTextWithHint(
989 "##cmd_query",
990 absl::StrFormat(
991 "%s Search… try drawer: window: layout: (or shortcut names)",
993 .c_str(),
995
996 SameLine();
997 if (Button(absl::StrFormat("%s Clear", ICON_MD_CLEAR).c_str())) {
998 command_palette_query_[0] = '\0';
999 input_changed = true;
1001 }
1002
1003 Separator();
1004
1005 // Unified command list structure
1006 struct ScoredCommand {
1007 int score;
1008 std::string name;
1009 std::string category;
1010 std::string shortcut;
1011 std::function<void()> callback;
1012 };
1013 std::vector<ScoredCommand> scored_commands;
1014
1015 std::string query_lower = command_palette_query_;
1016 std::transform(query_lower.begin(), query_lower.end(), query_lower.begin(),
1017 ::tolower);
1018
1019 auto score_text = [&query_lower](const std::string& text) -> int {
1020 std::string text_lower = text;
1021 std::transform(text_lower.begin(), text_lower.end(), text_lower.begin(),
1022 ::tolower);
1023
1024 if (query_lower.empty())
1025 return 1;
1026 if (text_lower.find(query_lower) == 0)
1027 return 1000;
1028 if (text_lower.find(query_lower) != std::string::npos)
1029 return 500;
1030
1031 // Fuzzy match
1032 size_t text_idx = 0, query_idx = 0;
1033 int score = 0;
1034 while (text_idx < text_lower.length() &&
1035 query_idx < query_lower.length()) {
1036 if (text_lower[text_idx] == query_lower[query_idx]) {
1037 score += 10;
1038 query_idx++;
1039 }
1040 text_idx++;
1041 }
1042 return (query_idx == query_lower.length()) ? score : 0;
1043 };
1044
1045 // Add shortcuts from ShortcutManager
1046 for (const auto& [name, shortcut] : shortcut_manager_.GetShortcuts()) {
1047 int score = score_text(name);
1048 if (score > 0) {
1049 std::string shortcut_text =
1050 shortcut.keys.empty()
1051 ? ""
1052 : absl::StrFormat("(%s)", PrintShortcut(shortcut.keys).c_str());
1053 scored_commands.push_back(
1054 {score, name, "Shortcuts", shortcut_text, shortcut.callback});
1055 }
1056 }
1057
1058 // Add commands from CommandPalette
1059 for (const auto& entry : command_palette_.GetAllCommands()) {
1060 int score = score_text(entry.name);
1061 // Also search category and description
1062 score += score_text(entry.category) / 2;
1063 score += score_text(entry.description) / 4;
1064
1065 if (score > 0) {
1066 scored_commands.push_back({score, entry.name, entry.category,
1067 entry.shortcut, entry.callback});
1068 }
1069 }
1070
1071 // Sort by score descending
1072 std::sort(scored_commands.begin(), scored_commands.end(),
1073 [](const auto& a, const auto& b) { return a.score > b.score; });
1074
1075 // Display results with categories
1076 if (gui::BeginThemedTabBar("CommandCategories")) {
1077 if (BeginTabItem(
1078 absl::StrFormat("%s All Commands", ICON_MD_LIST).c_str())) {
1080 "CommandPaletteTable", 4,
1081 ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg |
1082 ImGuiTableFlags_SizingStretchProp,
1083 ImVec2(0, -30))) {
1084 TableSetupColumn("Command", ImGuiTableColumnFlags_WidthStretch,
1085 0.45f);
1086 TableSetupColumn("Category", ImGuiTableColumnFlags_WidthStretch,
1087 0.2f);
1088 TableSetupColumn("Shortcut", ImGuiTableColumnFlags_WidthStretch,
1089 0.2f);
1090 TableSetupColumn("Score", ImGuiTableColumnFlags_WidthStretch, 0.15f);
1091 TableHeadersRow();
1092
1093 for (size_t i = 0; i < scored_commands.size(); ++i) {
1094 const auto& cmd = scored_commands[i];
1095
1096 TableNextRow();
1097 TableNextColumn();
1098
1099 PushID(static_cast<int>(i));
1100 bool is_selected =
1101 (static_cast<int>(i) == command_palette_selected_idx_);
1102 if (Selectable(cmd.name.c_str(), is_selected,
1103 ImGuiSelectableFlags_SpanAllColumns)) {
1105 if (cmd.callback) {
1106 cmd.callback();
1107 show_command_palette_ = false;
1108 // Record usage for frecency
1109 command_palette_.RecordUsage(cmd.name);
1110 }
1111 }
1112 PopID();
1113
1114 TableNextColumn();
1115 gui::ColoredText(cmd.category.c_str(),
1116 gui::ConvertColorToImVec4(theme.text_secondary));
1117
1118 TableNextColumn();
1119 gui::ColoredText(cmd.shortcut.c_str(),
1120 gui::ConvertColorToImVec4(theme.text_secondary));
1121
1122 TableNextColumn();
1123 gui::ColoredTextF(gui::ConvertColorToImVec4(theme.text_disabled),
1124 "%d", cmd.score);
1125 }
1126
1128 }
1129 EndTabItem();
1130 }
1131
1132 if (BeginTabItem(absl::StrFormat("%s Recent", ICON_MD_HISTORY).c_str())) {
1133 auto recent = command_palette_.GetRecentCommands(10);
1134 if (recent.empty()) {
1135 Text(tr("No recent commands yet."));
1136 } else {
1137 for (const auto& entry : recent) {
1138 if (Selectable(entry.name.c_str())) {
1139 if (entry.callback) {
1140 entry.callback();
1141 show_command_palette_ = false;
1142 command_palette_.RecordUsage(entry.name);
1143 }
1144 }
1145 }
1146 }
1147 EndTabItem();
1148 }
1149
1150 if (BeginTabItem(absl::StrFormat("%s Frequent", ICON_MD_STAR).c_str())) {
1151 auto frequent = command_palette_.GetFrequentCommands(10);
1152 if (frequent.empty()) {
1153 Text(tr("No frequently used commands yet."));
1154 } else {
1155 for (const auto& entry : frequent) {
1156 if (Selectable(absl::StrFormat("%s (%d uses)", entry.name,
1157 entry.usage_count)
1158 .c_str())) {
1159 if (entry.callback) {
1160 entry.callback();
1161 show_command_palette_ = false;
1162 command_palette_.RecordUsage(entry.name);
1163 }
1164 }
1165 }
1166 }
1167 EndTabItem();
1168 }
1169
1171 }
1172
1173 // Status bar with tips
1174 Separator();
1175 Text(tr("%s %zu commands | Prefixes: drawer: window: layout:"),
1176 ICON_MD_INFO, scored_commands.size());
1177 SameLine();
1178 gui::ColoredText("| ↑↓=Navigate | Enter=Execute | Esc=Close",
1179 gui::ConvertColorToImVec4(theme.text_disabled));
1180 }
1181 End();
1182
1183 // Update visibility state - save history when closing
1184 if (!show_palette) {
1185 show_command_palette_ = false;
1186 // Save command usage history on close
1187 auto config_dir = util::PlatformPaths::GetConfigDirectory();
1188 if (config_dir.ok()) {
1189 std::filesystem::path history_file = *config_dir / "command_history.json";
1190 command_palette_.SaveHistory(history_file.string());
1191 }
1192 }
1193}
1194
1196 if (!show_panel_finder_)
1197 return;
1198
1199 using namespace ImGui;
1200 const size_t session_id = window_manager_.GetActiveSessionId();
1201
1202 // B6: Responsive modal sizing with dim overlay
1203 const ImGuiViewport* viewport = GetMainViewport();
1204 ImDrawList* bg_list = GetBackgroundDrawList();
1205 bg_list->AddRectFilled(viewport->WorkPos,
1206 ImVec2(viewport->WorkPos.x + viewport->WorkSize.x,
1207 viewport->WorkPos.y + viewport->WorkSize.y),
1208 IM_COL32(0, 0, 0, 100));
1209
1210 SetNextWindowPos(viewport->GetCenter(), ImGuiCond_Appearing,
1211 ImVec2(0.5f, 0.3f));
1212 if (IsCompactLayout()) {
1213 SetNextWindowSize(
1214 ImVec2(viewport->WorkSize.x * 0.95f, viewport->WorkSize.y * 0.70f),
1215 ImGuiCond_Appearing);
1216 } else {
1217 SetNextWindowSize(ImVec2(600, 420), ImGuiCond_Appearing);
1218 }
1219
1220 const ImGuiWindowFlags finder_flags =
1221 ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove |
1222 ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoDocking |
1223 ImGuiWindowFlags_NoSavedSettings;
1224
1225 bool show = true;
1226 if (Begin(ICON_MD_DASHBOARD " Window Finder", &show, finder_flags)) {
1227 // Auto-focus search on open
1228 if (IsWindowAppearing()) {
1229 SetKeyboardFocusHere();
1231 }
1232
1233 SetNextItemWidth(-1);
1234 bool input_changed = InputTextWithHint(
1235 "##panel_finder_query", ICON_MD_SEARCH " Find window...",
1237
1238 if (input_changed) {
1240 }
1241
1242 Separator();
1243
1244 // Build filtered + scored list
1245 struct WindowEntry {
1246 std::string card_id;
1247 std::string display_name;
1248 std::string icon;
1249 std::string category;
1250 bool visible;
1251 bool pinned;
1252 int score;
1253 };
1254 std::vector<WindowEntry> entries;
1255
1256 std::string query(panel_finder_query_);
1257
1258 // B2: Fuzzy scoring via CommandPalette::FuzzyScore
1259 for (const auto& card_id :
1261 const auto* desc =
1262 window_manager_.GetWindowDescriptor(session_id, card_id);
1263 if (!desc) {
1264 continue;
1265 }
1266 int score = 0;
1267 if (!query.empty()) {
1268 score = CommandPalette::FuzzyScore(desc->display_name, query) +
1269 CommandPalette::FuzzyScore(desc->category, query) / 2;
1270 if (score <= 0)
1271 continue;
1272 }
1273
1274 bool vis = desc->visibility_flag ? *desc->visibility_flag : false;
1275 bool pin = window_manager_.IsWindowPinned(session_id, card_id);
1276 entries.push_back({card_id, desc->display_name, desc->icon,
1277 desc->category, vis, pin, score});
1278 }
1279
1280 // B3: MRU + fuzzy sort
1281 if (query.empty()) {
1282 // Empty query: pinned first, then MRU order (higher time = more recent)
1283 std::sort(
1284 entries.begin(), entries.end(),
1285 [this](const WindowEntry& lhs, const WindowEntry& rhs) {
1286 if (lhs.pinned != rhs.pinned)
1287 return lhs.pinned > rhs.pinned;
1288 uint64_t lhs_t = window_manager_.GetWindowMRUTime(lhs.card_id);
1289 uint64_t rhs_t = window_manager_.GetWindowMRUTime(rhs.card_id);
1290 if (lhs_t != rhs_t)
1291 return lhs_t > rhs_t;
1292 return lhs.display_name < rhs.display_name;
1293 });
1294 } else {
1295 // With query: sort by score descending, pinned tiebreaker
1296 std::sort(entries.begin(), entries.end(),
1297 [](const WindowEntry& lhs, const WindowEntry& rhs) {
1298 if (lhs.score != rhs.score)
1299 return lhs.score > rhs.score;
1300 if (lhs.pinned != rhs.pinned)
1301 return lhs.pinned > rhs.pinned;
1302 return lhs.display_name < rhs.display_name;
1303 });
1304 }
1305
1306 // Keyboard navigation
1307 if (IsKeyPressed(ImGuiKey_DownArrow) &&
1308 panel_finder_selected_idx_ < static_cast<int>(entries.size()) - 1) {
1310 }
1311 if (IsKeyPressed(ImGuiKey_UpArrow) && panel_finder_selected_idx_ > 0) {
1313 }
1314 bool enter_pressed = IsKeyPressed(ImGuiKey_Enter);
1315
1316 // B5: Touch-aware item sizing
1317 const bool is_touch = gui::LayoutHelpers::IsTouchDevice();
1318 if (is_touch) {
1319 PushStyleVar(ImGuiStyleVar_ItemSpacing,
1320 ImVec2(GetStyle().ItemSpacing.x, 10.0f));
1321 }
1322
1323 // Draw panel list
1324 BeginChild("##PanelFinderList");
1325 for (int i = 0; i < static_cast<int>(entries.size()); ++i) {
1326 const auto& entry = entries[i];
1327 bool is_selected = (i == panel_finder_selected_idx_);
1328
1329 // B4: Visibility indicator icon
1330 const char* vis_icon =
1332
1333 // Dim hidden panels
1334 std::optional<gui::StyleColorGuard> dim_guard;
1335 if (!entry.visible) {
1336 ImVec4 dimmed = GetStyleColorVec4(ImGuiCol_Text);
1337 dimmed.w *= 0.5f;
1338 dim_guard.emplace(ImGuiCol_Text, dimmed);
1339 }
1340
1341 std::string label =
1342 absl::StrFormat("%s %s %s", vis_icon, entry.icon.c_str(),
1343 entry.display_name.c_str());
1344
1345 // Touch: ensure selectable meets 44px minimum height
1346 float item_h =
1347 is_touch ? std::max(GetTextLineHeightWithSpacing(), 44.0f) : 0.0f;
1348
1349 PushID(entry.card_id.c_str());
1350 if (Selectable(label.c_str(), is_selected, ImGuiSelectableFlags_None,
1351 ImVec2(0, item_h))) {
1352 window_manager_.OpenWindow(session_id, entry.card_id);
1354 show_panel_finder_ = false;
1355 }
1356 // Show category badge on the same line
1357 SameLine(GetContentRegionAvail().x - 80);
1358 TextDisabled("%s", entry.category.c_str());
1359 if (entry.pinned) {
1360 SameLine();
1361 TextDisabled(ICON_MD_PUSH_PIN);
1362 }
1363 PopID();
1364
1365 // Enter to activate selected
1366 if (is_selected && enter_pressed) {
1367 window_manager_.OpenWindow(session_id, entry.card_id);
1369 show_panel_finder_ = false;
1370 }
1371
1372 // Scroll selected into view
1373 if (is_selected && (IsKeyPressed(ImGuiKey_DownArrow) ||
1374 IsKeyPressed(ImGuiKey_UpArrow))) {
1375 SetScrollHereY();
1376 }
1377 }
1378 EndChild();
1379
1380 if (is_touch) {
1381 PopStyleVar();
1382 }
1383 }
1384 End();
1385
1386 // Escape or close button
1387 if (!show || ImGui::IsKeyPressed(ImGuiKey_Escape)) {
1388 show_panel_finder_ = false;
1389 panel_finder_query_[0] = '\0';
1390 }
1391}
1392
1395 return;
1396 }
1397
1398 using namespace ImGui;
1399 const ImGuiViewport* viewport = GetMainViewport();
1400 SetNextWindowPos(viewport->GetCenter(), ImGuiCond_Appearing,
1401 ImVec2(0.5f, 0.5f));
1402 if (IsCompactLayout()) {
1403 SetNextWindowSize(
1404 ImVec2(viewport->WorkSize.x * 0.95f, viewport->WorkSize.y * 0.80f),
1405 ImGuiCond_Appearing);
1406 } else {
1407 SetNextWindowSize(ImVec2(720, 520), ImGuiCond_Appearing);
1408 }
1409
1410 bool show = true;
1411 if (Begin(absl::StrFormat("%s Keyboard Shortcuts", ICON_MD_KEYBOARD).c_str(),
1412 &show, ImGuiWindowFlags_NoCollapse)) {
1413 if (IsWindowAppearing()) {
1414 SetKeyboardFocusHere();
1415 }
1416
1417 SetNextItemWidth(-180.0f);
1418 InputTextWithHint(
1419 "##shortcuts_query",
1420 ICON_MD_SEARCH " Filter by name, keys, or group (File, Drawers, …)",
1422 SameLine();
1423 if (Button(ICON_MD_TUNE " Edit bindings")) {
1425 if (editor_manager_) {
1427 }
1428 }
1429
1430 Separator();
1431
1432 std::string query = shortcuts_browser_query_;
1433 std::transform(query.begin(), query.end(), query.begin(), ::tolower);
1434
1435 std::map<std::string, std::vector<const Shortcut*>> grouped;
1436 static const char* kGroupOrder[] = {"File", "Edit", "View", "Drawers",
1437 "Windows", "Tools", "Layout", "Editor",
1438 "Help", "Other"};
1439
1440 for (const auto& [name, shortcut] : shortcut_manager_.GetShortcuts()) {
1441 const std::string group = InferShortcutGroup(name);
1442 const std::string keys = PrintShortcut(shortcut.keys);
1443 if (!query.empty()) {
1444 std::string hay =
1445 absl::AsciiStrToLower(absl::StrCat(name, " ", keys, " ", group));
1446 if (hay.find(query) == std::string::npos) {
1447 continue;
1448 }
1449 }
1450 grouped[group].push_back(&shortcut);
1451 }
1452
1453 BeginChild("##ShortcutsBrowserList");
1454 for (const char* group_name : kGroupOrder) {
1455 auto it = grouped.find(group_name);
1456 if (it == grouped.end() || it->second.empty()) {
1457 continue;
1458 }
1459 if (!TreeNodeEx(group_name, ImGuiTreeNodeFlags_DefaultOpen)) {
1460 continue;
1461 }
1462 if (BeginTable(
1463 absl::StrFormat("##sc_%s", group_name).c_str(), 2,
1464 ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingStretchProp)) {
1465 TableSetupColumn("Action", ImGuiTableColumnFlags_WidthStretch, 0.65f);
1466 TableSetupColumn("Keys", ImGuiTableColumnFlags_WidthStretch, 0.35f);
1467 for (const Shortcut* sc : it->second) {
1468 TableNextRow();
1469 TableNextColumn();
1470 if (Selectable(sc->name.c_str(), false,
1471 ImGuiSelectableFlags_SpanAllColumns)) {
1472 if (sc->callback) {
1473 sc->callback();
1475 }
1476 }
1477 TableNextColumn();
1478 const std::string keys = PrintShortcut(sc->keys);
1479 TextDisabled("%s", keys.empty() ? "—" : keys.c_str());
1480 }
1481 EndTable();
1482 }
1483 TreePop();
1484 }
1485 EndChild();
1486
1487 Separator();
1488 TextDisabled(
1489 "%s Tip: Command Palette (Ctrl+Shift+P) also lists these actions. "
1490 "Try drawer: / window: / layout:",
1491 ICON_MD_INFO);
1492 }
1493 End();
1494
1495 if (!show || IsKeyPressed(ImGuiKey_Escape)) {
1497 shortcuts_browser_query_[0] = '\0';
1498 }
1499}
1500
1504
1505 // Register panel commands
1507 std::make_unique<PanelCommandsProvider>(&window_manager_, session_id));
1509 std::make_unique<WorkflowCommandsProvider>(&window_manager_, session_id));
1510 if (editor_manager_) {
1511 command_palette_.RegisterProvider(std::make_unique<SidebarCommandsProvider>(
1512 &window_manager_, &editor_manager_->user_settings(), session_id));
1513
1514 if (auto* drawers = editor_manager_->right_drawer_manager()) {
1516 std::make_unique<DrawerCommandsProvider>(
1517 [drawers](int drawer_type) {
1518 drawers->ToggleDrawer(
1519 static_cast<RightDrawerManager::DrawerType>(drawer_type));
1520 },
1521 [drawers]() { drawers->CycleToNextDrawer(); },
1522 [drawers]() { drawers->CycleToPreviousDrawer(); }));
1523 }
1524 }
1525
1526 // Register editor switch commands
1527 command_palette_.RegisterProvider(std::make_unique<EditorCommandsProvider>(
1528 [this](const std::string& category) {
1529 auto type = EditorRegistry::GetEditorTypeFromCategory(category);
1530 if (type != EditorType::kSettings && editor_manager_) {
1532 }
1533 }));
1534
1535 // Register layout/profile commands (includes layout: prefixes)
1536 if (editor_manager_) {
1538 std::make_unique<LayoutCommandsProvider>([this](const std::string& id) {
1539 if (!editor_manager_) {
1540 return;
1541 }
1542 if (absl::StartsWith(id, "profile:")) {
1543 editor_manager_->ApplyLayoutProfile(id.substr(8));
1544 } else if (id == "session:capture") {
1546 } else if (id == "session:restore") {
1548 } else if (id == "session:clear") {
1550 } else {
1552 }
1553 }));
1554 }
1555
1556 // Register recent files commands
1558 std::make_unique<RecentFilesCommandsProvider>(
1559 [this](const std::string& filepath) {
1560 if (editor_manager_) {
1561 auto status = editor_manager_->OpenRomOrProject(filepath);
1562 if (!status.ok()) {
1564 absl::StrFormat("Failed to open: %s", status.message()),
1566 }
1567 }
1568 }));
1569
1570 // Dungeon navigation helpers (room jump by id/label).
1572 std::make_unique<DungeonRoomCommandsProvider>(session_id));
1573
1574 // Welcome-screen-scoped commands: per-entry pin/remove, undo, template
1575 // creation, and visibility toggles. Recent-entry iteration pulls from the
1576 // same model the welcome screen renders, so the palette and the cards stay
1577 // in sync without a separate refresh path. We rebuild these whenever
1578 // RefreshCommandPalette() is invoked after recents mutate.
1579 if (welcome_screen_) {
1580 WelcomeCommandsProvider::Callbacks welcome_callbacks;
1581 welcome_callbacks.model = &welcome_screen_->recent_projects();
1582 welcome_callbacks.template_names = {
1583 "Vanilla ROM Hack", "ZSCustomOverworld v3", "ZSCustomOverworld v2",
1584 "Randomizer Compatible"};
1585 welcome_callbacks.remove = [this](const std::string& path) {
1586 if (!welcome_screen_)
1587 return;
1588 welcome_screen_->recent_projects().RemoveRecent(path);
1589 welcome_screen_->RefreshRecentProjects(/*force=*/true);
1591 absl::StrFormat("Removed %s from recents",
1592 std::filesystem::path(path).filename().string()),
1594 };
1595 welcome_callbacks.toggle_pin = [this](const std::string& path) {
1596 if (!welcome_screen_)
1597 return;
1598 auto& model = welcome_screen_->recent_projects();
1599 bool currently_pinned = false;
1600 for (const auto& entry : model.entries()) {
1601 if (entry.filepath == path) {
1602 currently_pinned = entry.pinned;
1603 break;
1604 }
1605 }
1606 model.SetPinned(path, !currently_pinned);
1607 welcome_screen_->RefreshRecentProjects(/*force=*/true);
1608 };
1609 welcome_callbacks.undo_remove = [this]() {
1610 if (!welcome_screen_)
1611 return;
1612 if (!welcome_screen_->recent_projects().UndoLastRemoval()) {
1613 toast_manager_.Show("Nothing to undo — the last removal has expired.",
1615 return;
1616 }
1617 welcome_screen_->RefreshRecentProjects(/*force=*/true);
1618 };
1619 welcome_callbacks.clear_recents = [this]() {
1620 if (!welcome_screen_)
1621 return;
1622 welcome_screen_->recent_projects().ClearAll();
1623 welcome_screen_->RefreshRecentProjects(/*force=*/true);
1624 };
1625 welcome_callbacks.create_from_template =
1626 [this](const std::string& template_name) {
1627 new_project_dialog_.Open(template_name);
1628 };
1629 welcome_callbacks.dismiss_welcome = [this]() {
1633 }
1634 };
1635 welcome_callbacks.show_welcome = [this]() {
1638 };
1639 command_palette_.RegisterProvider(std::make_unique<WelcomeCommandsProvider>(
1640 std::move(welcome_callbacks)));
1641 }
1642
1643 // Load command usage history
1644 auto config_dir = util::PlatformPaths::GetConfigDirectory();
1645 if (config_dir.ok()) {
1646 std::filesystem::path history_file = *config_dir / "command_history.json";
1647 command_palette_.LoadHistory(history_file.string());
1648 }
1649
1651}
1652
1655
1659 {.id = "workflow.project.build",
1660 .group = "Build & Run",
1661 .label = "Build Project",
1662 .description = "Run the active project's configured build command",
1663 .shortcut = "",
1664 .priority = 5,
1665 .callback = [this]() {
1666 if (editor_manager_) {
1668 }
1669 }});
1671 {.id = "workflow.project.run",
1672 .group = "Build & Run",
1673 .label = "Run Project Output",
1674 .description = "Open the active project's configured run target in a "
1675 "test session",
1676 .shortcut = "",
1677 .priority = 10,
1678 .callback = [this]() {
1679 if (editor_manager_) {
1681 }
1682 }});
1683 }
1684
1685#ifdef YAZE_WITH_GRPC
1686 auto* emu_backend = Application::Instance().GetEmulatorBackend();
1687 if (emu_backend) {
1689 {.id = "workflow.mesen.connect",
1690 .group = "Live Debugging",
1691 .label = "Connect Mesen2",
1692 .description =
1693 "Auto-discover and connect to the active Mesen2 backend",
1694 .shortcut = "",
1695 .priority = 10,
1696 .callback = [this]() {
1697 auto* backend = Application::Instance().GetEmulatorBackend();
1698 if (backend) {
1699 toast_manager_.Show("Mesen2 connection attempt queued",
1701 }
1702 }});
1704 {.id = "workflow.mesen.step_over",
1705 .group = "Live Debugging",
1706 .label = "Mesen2 Step Over",
1707 .description = "Execute one instruction without entering subroutines",
1708 .shortcut = "F10",
1709 .priority = 20,
1710 .callback = [emu_backend]() { emu_backend->StepOver(); }});
1712 {.id = "workflow.mesen.step_out",
1713 .group = "Live Debugging",
1714 .label = "Mesen2 Step Out",
1715 .description = "Run until return from the current subroutine",
1716 .shortcut = "Shift+F11",
1717 .priority = 30,
1718 .callback = [emu_backend]() { emu_backend->StepOut(); }});
1720 {.id = "workflow.mesen.overlay_on",
1721 .group = "Live Debugging",
1722 .label = "Enable Collision Overlay",
1723 .description = "Show collision overlays in the active Mesen2 backend",
1724 .shortcut = "",
1725 .priority = 40,
1726 .callback = [emu_backend]() {
1727 emu_backend->SetCollisionOverlay(true);
1728 }});
1730 {.id = "workflow.mesen.overlay_off",
1731 .group = "Live Debugging",
1732 .label = "Disable Collision Overlay",
1733 .description = "Hide collision overlays in the active Mesen2 backend",
1734 .shortcut = "",
1735 .priority = 50,
1736 .callback = [emu_backend]() {
1737 emu_backend->SetCollisionOverlay(false);
1738 }});
1739 }
1740#endif
1741}
1742
1744 InitializeCommandPalette(session_id);
1745}
1746
1749 return;
1750
1751 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(),
1752 ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
1753 ImGui::SetNextWindowSize(ImVec2(800, 600), ImGuiCond_FirstUseEver);
1754
1755 bool show_search = true;
1756 if (ImGui::Begin(
1757 absl::StrFormat("%s Global Search", ICON_MD_MANAGE_SEARCH).c_str(),
1758 &show_search, ImGuiWindowFlags_NoCollapse)) {
1759 // Enhanced search input with focus management
1760 ImGui::SetNextItemWidth(-100);
1761 if (ImGui::IsWindowAppearing()) {
1762 ImGui::SetKeyboardFocusHere();
1763 }
1764
1765 bool input_changed = ImGui::InputTextWithHint(
1766 "##global_query",
1767 absl::StrFormat("%s Search everything...", ICON_MD_SEARCH).c_str(),
1769
1770 ImGui::SameLine();
1771 if (ImGui::Button(absl::StrFormat("%s Clear", ICON_MD_CLEAR).c_str())) {
1772 global_search_query_[0] = '\0';
1773 input_changed = true;
1774 }
1775
1776 ImGui::Separator();
1777
1778 // Tabbed search results for better organization
1779 if (gui::BeginThemedTabBar("SearchResultTabs")) {
1780 // Recent Files Tab
1781 if (ImGui::BeginTabItem(
1782 absl::StrFormat("%s Recent Files", ICON_MD_HISTORY).c_str())) {
1784 auto recent_files = manager.GetRecentFiles();
1785
1786 if (ImGui::BeginTable("RecentFilesTable", 3,
1787 ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg |
1788 ImGuiTableFlags_SizingStretchProp)) {
1789 ImGui::TableSetupColumn("File", ImGuiTableColumnFlags_WidthStretch,
1790 0.6f);
1791 ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed,
1792 80.0f);
1793 ImGui::TableSetupColumn("Action", ImGuiTableColumnFlags_WidthFixed,
1794 100.0f);
1795 ImGui::TableHeadersRow();
1796
1797 for (const auto& file : recent_files) {
1798 if (global_search_query_[0] != '\0' &&
1799 file.find(global_search_query_) == std::string::npos)
1800 continue;
1801
1802 ImGui::TableNextRow();
1803 ImGui::TableNextColumn();
1804 ImGui::Text("%s", util::GetFileName(file).c_str());
1805
1806 ImGui::TableNextColumn();
1807 std::string ext = util::GetFileExtension(file);
1808 if (ext == "sfc" || ext == "smc") {
1809 ImGui::TextColored(ImVec4(0.2f, 0.8f, 0.2f, 1.0f), tr("%s ROM"),
1811 } else if (ext == "yaze") {
1812 ImGui::TextColored(ImVec4(0.2f, 0.6f, 0.8f, 1.0f),
1813 tr("%s Project"), ICON_MD_FOLDER);
1814 } else {
1815 ImGui::Text(tr("%s File"), ICON_MD_DESCRIPTION);
1816 }
1817
1818 ImGui::TableNextColumn();
1819 ImGui::PushID(file.c_str());
1820 if (ImGui::Button(tr("Open"))) {
1821 auto status = editor_manager_->OpenRomOrProject(file);
1822 if (!status.ok()) {
1824 absl::StrCat("Failed to open: ", status.message()),
1826 }
1828 }
1829 ImGui::PopID();
1830 }
1831
1832 ImGui::EndTable();
1833 }
1834 ImGui::EndTabItem();
1835 }
1836
1837 // Labels Tab (only if ROM is loaded)
1838 auto* current_rom = editor_manager_->GetCurrentRom();
1839 if (current_rom && current_rom->resource_label()) {
1840 if (ImGui::BeginTabItem(
1841 absl::StrFormat("%s Labels", ICON_MD_LABEL).c_str())) {
1842 auto& labels = current_rom->resource_label()->labels_;
1843
1844 if (ImGui::BeginTable("LabelsTable", 3,
1845 ImGuiTableFlags_ScrollY |
1846 ImGuiTableFlags_RowBg |
1847 ImGuiTableFlags_SizingStretchProp)) {
1848 ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed,
1849 100.0f);
1850 ImGui::TableSetupColumn("Label", ImGuiTableColumnFlags_WidthStretch,
1851 0.4f);
1852 ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthStretch,
1853 0.6f);
1854 ImGui::TableHeadersRow();
1855
1856 for (const auto& type_pair : labels) {
1857 for (const auto& kv : type_pair.second) {
1858 if (global_search_query_[0] != '\0' &&
1859 kv.first.find(global_search_query_) == std::string::npos &&
1860 kv.second.find(global_search_query_) == std::string::npos)
1861 continue;
1862
1863 ImGui::TableNextRow();
1864 ImGui::TableNextColumn();
1865 ImGui::Text("%s", type_pair.first.c_str());
1866
1867 ImGui::TableNextColumn();
1868 if (ImGui::Selectable(kv.first.c_str(), false,
1869 ImGuiSelectableFlags_SpanAllColumns)) {
1870 // Future: navigate to related editor/location
1871 }
1872
1873 ImGui::TableNextColumn();
1874 ImGui::TextDisabled("%s", kv.second.c_str());
1875 }
1876 }
1877
1878 ImGui::EndTable();
1879 }
1880 ImGui::EndTabItem();
1881 }
1882 }
1883
1884 // Sessions Tab
1886 if (ImGui::BeginTabItem(
1887 absl::StrFormat("%s Sessions", ICON_MD_TAB).c_str())) {
1888 ImGui::Text(tr("Search and switch between active sessions:"));
1889
1890 for (size_t i = 0; i < session_coordinator_.GetTotalSessionCount();
1891 ++i) {
1892 std::string session_info =
1894 if (session_info == "[CLOSED SESSION]")
1895 continue;
1896
1897 if (global_search_query_[0] != '\0' &&
1898 session_info.find(global_search_query_) == std::string::npos)
1899 continue;
1900
1901 bool is_current =
1903 std::optional<gui::StyleColorGuard> current_guard;
1904 if (is_current) {
1905 current_guard.emplace(ImGuiCol_Text,
1906 ImVec4(0.2f, 0.8f, 0.2f, 1.0f));
1907 }
1908
1909 if (ImGui::Selectable(absl::StrFormat("%s %s %s", ICON_MD_TAB,
1910 session_info.c_str(),
1911 is_current ? "(Current)" : "")
1912 .c_str())) {
1913 if (!is_current) {
1916 }
1917 }
1918 }
1919 ImGui::EndTabItem();
1920 }
1921 }
1922
1924 }
1925
1926 // Status bar
1927 ImGui::Separator();
1928 ImGui::Text(tr("%s Global search across all YAZE data"), ICON_MD_INFO);
1929 }
1930 ImGui::End();
1931
1932 // Update visibility state
1933 if (!show_search) {
1935 }
1936}
1937
1938// ============================================================================
1939// Startup Surface Management (Single Source of Truth)
1940// ============================================================================
1941
1944 current_startup_surface_ = surface;
1945
1946 // Log state transitions for debugging
1947 const char* surface_names[] = {"Welcome", "Dashboard", "Editor"};
1948 LOG_INFO("UICoordinator", "Startup surface: %s -> %s",
1949 surface_names[static_cast<int>(old_surface)],
1950 surface_names[static_cast<int>(surface)]);
1951
1952 // Update dependent visibility flags
1953 switch (surface) {
1955 show_welcome_screen_ = true;
1956 show_editor_selection_ = false; // Dashboard hidden
1957 // Activity Bar will be hidden (checked via ShouldShowActivityBar)
1958 break;
1960 show_welcome_screen_ = false;
1961 show_editor_selection_ = true; // Dashboard shown
1962 break;
1964 show_welcome_screen_ = false;
1965 show_editor_selection_ = false; // Dashboard hidden
1966 break;
1967 }
1968}
1969
1971 // Respect CLI overrides
1973 return false;
1974 }
1976 return true;
1977 }
1978
1979 // Default: show welcome only when in welcome state and not manually closed
1982}
1983
1985 // Consulted by SetEditorSelectionVisible, the choke point every automatic
1986 // entry into the dashboard passes through.
1987 //
1988 // This used to also require current_startup_surface_ == kDashboard, and
1989 // nothing called it at all — so --startup_dashboard=hide did nothing while
1990 // its sibling --startup_welcome worked, because ShouldShowWelcome() IS
1991 // consulted. The surface test is dropped on purpose: by the time a ROM
1992 // finishes loading the surface has already advanced past kDashboard, so
1993 // keying on it would suppress the very chooser the flag exists to govern.
1995}
1996
1998 // Sidebar would consume the entire screen on compact (iPhone portrait)
1999 if (IsCompactLayout()) {
2000 return false;
2001 }
2002
2003 // Activity Bar hidden on cold start (welcome screen)
2004 // Only show after ROM is loaded
2006 return false;
2007 }
2008
2009 // Check if ROM is actually loaded
2010 if (editor_manager_) {
2011 auto* current_rom = editor_manager_->GetCurrentRom();
2012 if (!current_rom || !current_rom->is_loaded()) {
2013 return false;
2014 }
2015 }
2016
2017 return true;
2018}
2019
2020} // namespace editor
2021} // 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:180
auto short_name() const
Definition rom.h:177
bool is_loaded() const
Definition rom.h:155
void SaveHistory(const std::string &filepath)
Save command usage history to disk.
void Clear()
Clear all commands (and forget every registered provider).
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)
void RestoreTemporaryLayoutSnapshot(bool clear_after_restore=false)
bool ApplyLayoutProfile(const std::string &profile_id)
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 the single Drawers overflow control for the status cluster.
static float GetDrawerToggleClusterWidth()
Menu-bar width reserved for the Drawers overflow control.
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 ShowCommandPalette(const char *initial_query=nullptr)
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)
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_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_TUNE
Definition icons.h:2022
#define ICON_MD_LABEL
Definition icons.h:1053
#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_KEYBOARD
Definition icons.h:1028
#define ICON_MD_CLEAR
Definition icons.h:416
#define ICON_MD_DESCRIPTION
Definition icons.h:539
#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_HISTORY
Definition icons.h:946
#define LOG_ERROR(category, format,...)
Definition log.h:110
#define LOG_INFO(category, format,...)
Definition log.h:106
Definition input.cc:30
constexpr const char * kDisplaySettings
StartupSurface
Represents the current startup surface state.
std::string InferShortcutGroup(absl::string_view name)
Menu-IA group for a shortcut/command name (File, View, Drawers, …).
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 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.
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