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
3#include <algorithm>
4#include <functional>
5#include <memory>
6#include <string>
7#include <vector>
8
9#include "absl/strings/str_format.h"
10
11#ifdef __EMSCRIPTEN__
12#include <emscripten.h>
13#endif
14#ifdef __APPLE__
15#include <TargetConditionals.h>
16#endif
17#if defined(__APPLE__) && TARGET_OS_IOS == 1
19#endif
20#include "app/editor/editor.h"
32#include "app/gui/core/icons.h"
34#include "app/gui/core/style.h"
36#include "core/project.h"
37#include "imgui/imgui.h"
38#include "util/file_util.h"
39
40namespace yaze {
41namespace editor {
42
44 EditorManager* editor_manager, RomFileManager& rom_manager,
45 ProjectManager& project_manager, EditorRegistry& editor_registry,
46 PanelManager& panel_manager, SessionCoordinator& session_coordinator,
47 WindowDelegate& window_delegate, ToastManager& toast_manager,
48 PopupManager& popup_manager, ShortcutManager& shortcut_manager)
49 : editor_manager_(editor_manager),
50 rom_manager_(rom_manager),
51 project_manager_(project_manager),
52 editor_registry_(editor_registry),
53 panel_manager_(panel_manager),
54 session_coordinator_(session_coordinator),
55 window_delegate_(window_delegate),
56 toast_manager_(toast_manager),
57 popup_manager_(popup_manager),
58 shortcut_manager_(shortcut_manager) {
59 // Initialize welcome screen with proper callbacks
60 welcome_screen_ = std::make_unique<WelcomeScreen>();
61
62 // Wire welcome screen callbacks to EditorManager
63 welcome_screen_->SetOpenRomCallback([this]() {
64#ifdef __EMSCRIPTEN__
65 // In web builds, trigger the file input element directly
66 // The file input handler in app.js will handle the file selection
67 // and call LoadRomFromWeb, which will update the ROM
68 EM_ASM({
69 var romInput = document.getElementById('rom-input');
70 if (romInput) {
71 romInput.click();
72 }
73 });
74 // Don't hide welcome screen yet - it will be hidden when ROM loads
75 // (DrawWelcomeScreen auto-transitions to Dashboard on ROM load)
76#else
77 if (editor_manager_) {
78 auto status = editor_manager_->LoadRom();
79 if (!status.ok()) {
81 absl::StrFormat("Failed to load ROM: %s", status.message()),
83 }
84#if !(defined(__APPLE__) && TARGET_OS_IOS == 1)
85 else {
86 // Transition to Dashboard on successful ROM load
88 }
89#endif
90 }
91#endif
92 });
93
94 welcome_screen_->SetNewProjectCallback([this]() {
95 if (editor_manager_) {
96 auto status = editor_manager_->CreateNewProject();
97 if (!status.ok()) {
99 absl::StrFormat("Failed to create project: %s", status.message()),
101 } else {
102 // Transition to Dashboard on successful project creation
104 }
105 }
106 });
107
108 welcome_screen_->SetOpenProjectCallback([this](const std::string& filepath) {
109 if (editor_manager_) {
110 auto status = editor_manager_->OpenRomOrProject(filepath);
111 if (!status.ok()) {
113 absl::StrFormat("Failed to open project: %s", status.message()),
115 } else {
116 // Transition to Dashboard on successful project open
118 }
119 }
120 });
121
122 welcome_screen_->SetOpenAgentCallback([this]() {
123 if (editor_manager_) {
124#ifdef YAZE_BUILD_AGENT_UI
125 editor_manager_->ShowAIAgent();
126#endif
127 // Keep welcome screen visible - user may want to do other things
128 }
129 });
130}
131
145
147 if (dashboard_behavior_override_ == mode) {
148 return;
149 }
151 if (mode == StartupVisibility::kShow) {
152 // Only transition to dashboard if we're not in welcome
155 }
156 } else if (mode == StartupVisibility::kHide) {
157 // If hiding dashboard, transition to editor state
160 }
161 }
162}
163
165 if (ImGui::GetCurrentContext()) {
166 ImDrawList* bg_draw_list = ImGui::GetBackgroundDrawList();
167 const ImGuiViewport* viewport = ImGui::GetMainViewport();
168
169 auto& theme_manager = gui::ThemeManager::Get();
170 auto current_theme = theme_manager.GetCurrentTheme();
171 auto& bg_renderer = gui::BackgroundRenderer::Get();
172
173 // Draw grid covering the entire main viewport
174 ImVec2 grid_pos = viewport->WorkPos;
175 ImVec2 grid_size = viewport->WorkSize;
176 bg_renderer.RenderDockingBackground(bg_draw_list, grid_pos, grid_size,
177 current_theme.primary);
178 }
179}
180
182 // Note: Theme styling is applied by ThemeManager, not here
183 // This is called from EditorManager::Update() - don't call menu bar stuff
184 // here
185
186 // Draw UI windows and dialogs
187 // Session dialogs are drawn by SessionCoordinator separately to avoid
188 // duplication
189 DrawCommandPalette(); // Ctrl+Shift+P
190 DrawGlobalSearch(); // Ctrl+Shift+K
191 DrawWorkspacePresetDialogs(); // Save/Load workspace dialogs
192 DrawLayoutPresets(); // Layout preset dialogs
193 DrawWelcomeScreen(); // Welcome screen
194 DrawProjectHelp(); // Project help
195 DrawWindowManagementUI(); // Window management
196
197 // Draw popups and toasts
201}
202
204 const ImGuiViewport* viewport = ImGui::GetMainViewport();
205 if (!viewport) {
206 return false;
207 }
208 const float width = viewport->WorkSize.x;
209#if defined(__APPLE__) && TARGET_OS_IOS == 1
210 return true;
211#else
212 return width < 900.0f;
213#endif
214}
215
217 if (!IsCompactLayout()) {
218 return;
219 }
220
221 const ImGuiViewport* viewport = ImGui::GetMainViewport();
222 if (!viewport) {
223 return;
224 }
225
226 const ImGuiStyle& style = ImGui::GetStyle();
227 ImVec2 safe = style.DisplaySafeAreaPadding;
228#if defined(__APPLE__) && TARGET_OS_IOS == 1
229 const auto safe_area = ::yaze::platform::ios::GetSafeAreaInsets();
230 if (safe_area.left != 0.0f || safe_area.right != 0.0f ||
231 safe_area.top != 0.0f || safe_area.bottom != 0.0f) {
232 safe = ImVec2(safe_area.right, safe_area.bottom);
233 }
234#endif
235 const float button_size = std::max(44.0f, ImGui::GetFontSize() * 2.1f);
236 const float padding = style.WindowPadding.x;
237 const ImVec2 pos(viewport->WorkPos.x + viewport->WorkSize.x - safe.x -
238 padding - button_size,
239 viewport->WorkPos.y + viewport->WorkSize.y - safe.y -
240 padding - button_size);
241
242 const auto& theme = gui::ThemeManager::Get().GetCurrentTheme();
243 const ImVec4 button_color = gui::ConvertColorToImVec4(theme.button);
244 const ImVec4 button_hovered = gui::ConvertColorToImVec4(theme.button_hovered);
245 const ImVec4 button_active = gui::ConvertColorToImVec4(theme.button_active);
246 const ImVec4 button_text = gui::ConvertColorToImVec4(theme.accent);
247
248 ImGui::SetNextWindowPos(pos, ImGuiCond_Always);
249 ImGui::SetNextWindowSize(ImVec2(button_size, button_size), ImGuiCond_Always);
250
251 ImGuiWindowFlags flags =
252 ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize |
253 ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse |
254 ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoFocusOnAppearing |
255 ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoSavedSettings |
256 ImGuiWindowFlags_NoBackground;
257
258 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
259 if (ImGui::Begin("##MobileNavButton", nullptr, flags)) {
260 ImGui::PushStyleColor(ImGuiCol_Button, button_color);
261 ImGui::PushStyleColor(ImGuiCol_ButtonHovered, button_hovered);
262 ImGui::PushStyleColor(ImGuiCol_ButtonActive, button_active);
263 ImGui::PushStyleColor(ImGuiCol_Text, button_text);
264
265 if (ImGui::Button(ICON_MD_APPS, ImVec2(button_size, button_size))) {
266 ImGui::OpenPopup("##MobileNavPopup");
267 }
268 if (ImGui::IsItemHovered()) {
269 ImGui::SetTooltip("Navigation");
270 }
271
272 ImGui::PopStyleColor(4);
273 }
274 ImGui::End();
275 ImGui::PopStyleVar();
276
277 ImGui::PushStyleColor(ImGuiCol_PopupBg,
278 gui::ConvertColorToImVec4(theme.surface));
279 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(12.0f, 10.0f));
280 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8.0f, 6.0f));
281
282 if (ImGui::BeginPopup("##MobileNavPopup")) {
283 bool has_rom = false;
284 if (editor_manager_) {
285 auto* current_rom = editor_manager_->GetCurrentRom();
286 has_rom = current_rom && current_rom->is_loaded();
287 }
288
289 if (ImGui::MenuItem(ICON_MD_FOLDER_OPEN " Open ROM")) {
290 if (editor_manager_) {
291 auto status = editor_manager_->LoadRom();
292 if (!status.ok()) {
294 absl::StrFormat("Failed to load ROM: %s", status.message()),
296 }
297#if !(defined(__APPLE__) && TARGET_OS_IOS == 1)
298 else {
300 }
301#endif
302 }
303 }
304
305 if (has_rom) {
306 if (ImGui::MenuItem(
307 ICON_MD_DASHBOARD " Dashboard", nullptr,
310 }
311 if (ImGui::MenuItem(
312 ICON_MD_EDIT " Editor", nullptr,
315 }
316 }
317
318 const bool sidebar_visible = panel_manager_.IsSidebarVisible();
319 if (ImGui::MenuItem(ICON_MD_VIEW_SIDEBAR " Toggle Sidebar", nullptr,
320 sidebar_visible)) {
322 }
323
324 if (editor_manager_) {
325 auto* right_panel = editor_manager_->right_panel_manager();
326 if (right_panel) {
327 const bool settings_active =
329 if (ImGui::MenuItem(ICON_MD_SETTINGS " Settings", nullptr,
330 settings_active)) {
331 right_panel->TogglePanel(RightPanelManager::PanelType::kSettings);
332 }
333 }
334 }
335
336 ImGui::EndPopup();
337 }
338
339 ImGui::PopStyleVar(2);
340 ImGui::PopStyleColor();
341}
342
343// =============================================================================
344// Menu Bar Helpers
345// =============================================================================
346
347bool UICoordinator::DrawMenuBarIconButton(const char* icon, const char* tooltip,
348 bool is_active) {
349 // Push consistent button styling
350 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
351 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
353 ImGui::PushStyleColor(ImGuiCol_ButtonActive,
355
356 // Active state uses primary color, inactive uses secondary text
357 if (is_active) {
358 ImGui::PushStyleColor(ImGuiCol_Text, gui::GetPrimaryVec4());
359 } else {
360 ImGui::PushStyleColor(ImGuiCol_Text, gui::GetTextSecondaryVec4());
361 }
362
363 bool clicked = ImGui::SmallButton(icon);
364
365 ImGui::PopStyleColor(4);
366
367 if (tooltip && ImGui::IsItemHovered()) {
368 ImGui::SetTooltip("%s", tooltip);
369 }
370
371 return clicked;
372}
373
375 // SmallButton width = text width + frame padding * 2
376 const float frame_padding = ImGui::GetStyle().FramePadding.x;
377 // Use a standard icon width (Material Design icons are uniform)
378 const float icon_width = ImGui::CalcTextSize(ICON_MD_SETTINGS).x;
379 return icon_width + frame_padding * 2.0f;
380}
381
383 // Right-aligned status cluster: Version, dirty indicator, session, bell, panel toggles
384 // Panel toggles are positioned using SCREEN coordinates (from viewport) so they
385 // stay fixed even when the dockspace resizes due to panel open/close.
386 //
387 // Layout: [v0.x.x][●][📄▾][🔔] [panels][⬆]
388 // ^^^ shifts with dockspace ^^^ ^^^ fixed screen position ^^^
389
390 auto* current_rom = editor_manager_->GetCurrentRom();
391 const std::string full_version =
392 absl::StrFormat("v%s", editor_manager_->version().c_str());
393
394 const float item_spacing = 6.0f;
395 const float button_width = GetMenuBarIconButtonWidth();
396 const float padding = 8.0f;
397
398 // Get TRUE viewport dimensions (not affected by dockspace resize)
399 const ImGuiViewport* viewport = ImGui::GetMainViewport();
400 const float true_viewport_right = viewport->WorkPos.x + viewport->WorkSize.x;
401
402 // Calculate panel toggle region width
403 // Buttons: Project, Agent (GRPC only), Help, Settings, Properties
404 int panel_button_count = 0;
406#ifdef YAZE_WITH_GRPC
407 panel_button_count = 5; // Project, Agent, Help, Settings, Properties
408#else
409 panel_button_count = 4; // Project, Help, Settings, Properties
410#endif
411 }
412
413 float panel_region_width = 0.0f;
414 if (panel_button_count > 0) {
415 panel_region_width = (button_width * panel_button_count) +
416 (item_spacing * (panel_button_count - 1)) + padding;
417 }
418#ifdef __EMSCRIPTEN__
419 panel_region_width += button_width + item_spacing; // WASM toggle
420#endif
421
422 // Calculate screen X position for panel toggles (fixed at viewport right edge)
423 float panel_screen_x = true_viewport_right - panel_region_width;
426 panel_screen_x -= editor_manager_->right_panel_manager()->GetPanelWidth();
427 }
428
429 // Calculate available space for status cluster (version, dirty, session, bell)
430 // This ends where the panel toggle region begins
431 const float window_width = ImGui::GetWindowWidth();
432 const float window_screen_x = ImGui::GetWindowPos().x;
433 const float menu_items_end = ImGui::GetCursorPosX() + 16.0f;
434
435 // Convert panel screen X to window-local coordinates for space calculation
436 float panel_local_x = panel_screen_x - window_screen_x;
437 float region_end =
438 std::min(window_width - padding, panel_local_x - item_spacing);
439
440 // Calculate what elements to show - progressive hiding when space is tight
441 bool has_dirty_rom =
442 current_rom && current_rom->is_loaded() && current_rom->dirty();
443 bool has_multiple_sessions = session_coordinator_.HasMultipleSessions();
444
445 float version_width = ImGui::CalcTextSize(full_version.c_str()).x;
446 float dirty_width =
447 ImGui::CalcTextSize(ICON_MD_FIBER_MANUAL_RECORD).x + item_spacing;
448 float session_width = button_width;
449
450 const float available_width = region_end - menu_items_end - padding;
451
452 // Minimum required width: just the bell (always visible)
453 float required_width = button_width;
454
455 // Progressive show/hide based on available space
456 // Priority (highest to lowest): Bell > Dirty > Session > Version
457
458 // Try to fit version (lowest priority - hide first when tight)
459 bool show_version =
460 (required_width + version_width + item_spacing) <= available_width;
461 if (show_version) {
462 required_width += version_width + item_spacing;
463 }
464
465 // Try to fit session button (medium priority)
466 bool show_session =
467 has_multiple_sessions &&
468 (required_width + session_width + item_spacing) <= available_width;
469 if (show_session) {
470 required_width += session_width + item_spacing;
471 }
472
473 // Try to fit dirty indicator (high priority - only hide if extremely tight)
474 bool show_dirty =
475 has_dirty_rom && (required_width + dirty_width) <= available_width;
476 if (show_dirty) {
477 required_width += dirty_width;
478 }
479
480 // Calculate start position (right-align within available space)
481 float start_pos = std::max(menu_items_end, region_end - required_width);
482
483 // =========================================================================
484 // DRAW STATUS CLUSTER (shifts with dockspace)
485 // =========================================================================
486 ImGui::SameLine(start_pos);
487 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(item_spacing, 0.0f));
488
489 // 1. Version - subdued gray text
490 if (show_version) {
491 ImGui::PushStyleColor(ImGuiCol_Text, gui::GetTextDisabledVec4());
492 ImGui::Text("%s", full_version.c_str());
493 ImGui::PopStyleColor();
494 ImGui::SameLine();
495 }
496
497 // 2. Dirty badge - warning color dot
498 if (show_dirty) {
499 const auto& theme = gui::ThemeManager::Get().GetCurrentTheme();
500 ImGui::PushStyleColor(ImGuiCol_Text,
501 gui::ConvertColorToImVec4(theme.warning));
502 ImGui::Text(ICON_MD_FIBER_MANUAL_RECORD);
503 ImGui::PopStyleColor();
504 if (ImGui::IsItemHovered()) {
505 ImGui::SetTooltip("Unsaved changes: %s",
506 current_rom->short_name().c_str());
507 }
508 ImGui::SameLine();
509 }
510
511 // 3. Session button - layers icon
512 if (show_session) {
514 ImGui::SameLine();
515 }
516
517 // 4. Notification bell (pass visibility flags for enhanced tooltip)
518 DrawNotificationBell(show_dirty, has_dirty_rom, show_session,
519 has_multiple_sessions);
520
521 // =========================================================================
522 // DRAW PANEL TOGGLES (fixed screen position, unaffected by dockspace resize)
523 // =========================================================================
524 if (panel_button_count > 0) {
525 // Get current Y position within menu bar
526 float menu_bar_y = ImGui::GetCursorScreenPos().y;
527
528 // Position at fixed screen coordinates
529 ImGui::SetCursorScreenPos(ImVec2(panel_screen_x, menu_bar_y));
530
531 // Draw panel toggle buttons
533 }
534
535#ifdef __EMSCRIPTEN__
536 // WASM toggle button - also at fixed position
537 ImGui::SameLine();
539 "Hide menu bar (Alt to restore)")) {
540 show_menu_bar_ = false;
541 }
542#endif
543
544 ImGui::PopStyleVar(); // ItemSpacing
545}
546
548 // Only draw when menu bar is hidden (primarily for WASM builds)
549 if (show_menu_bar_) {
550 return;
551 }
552
553 // Small floating button in top-left corner to restore menu bar
554 ImGuiWindowFlags flags =
555 ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize |
556 ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar |
557 ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_AlwaysAutoResize |
558 ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoSavedSettings;
559
560 ImGui::SetNextWindowPos(ImVec2(8, 8));
561 ImGui::SetNextWindowBgAlpha(0.7f);
562
563 if (ImGui::Begin("##MenuBarRestore", nullptr, flags)) {
564 ImGui::PushStyleColor(ImGuiCol_Button, gui::GetSurfaceContainerVec4());
565 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
567 ImGui::PushStyleColor(ImGuiCol_ButtonActive,
569 ImGui::PushStyleColor(ImGuiCol_Text, gui::GetPrimaryVec4());
570
571 if (ImGui::Button(ICON_MD_FULLSCREEN_EXIT, ImVec2(32, 32))) {
572 show_menu_bar_ = true;
573 }
574
575 ImGui::PopStyleColor(4);
576
577 if (ImGui::IsItemHovered()) {
578 ImGui::SetTooltip("Show menu bar (Alt)");
579 }
580 }
581 ImGui::End();
582
583 // Also check for Alt key to restore menu bar
584 if (ImGui::IsKeyPressed(ImGuiKey_LeftAlt) ||
585 ImGui::IsKeyPressed(ImGuiKey_RightAlt)) {
586 show_menu_bar_ = true;
587 }
588}
589
590void UICoordinator::DrawNotificationBell(bool show_dirty, bool has_dirty_rom,
591 bool show_session,
592 bool has_multiple_sessions) {
593 size_t unread = toast_manager_.GetUnreadCount();
594 auto* current_rom = editor_manager_->GetCurrentRom();
595 auto* right_panel = editor_manager_->right_panel_manager();
596
597 // Check if notifications panel is active
598 bool is_active =
599 right_panel &&
601
602 // Bell icon with accent color when there are unread notifications or panel is active
603 if (unread > 0 || is_active) {
604 ImGui::PushStyleColor(ImGuiCol_Text, gui::GetPrimaryVec4());
605 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
606 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
608 ImGui::PushStyleColor(ImGuiCol_ButtonActive,
610 } else {
611 ImGui::PushStyleColor(ImGuiCol_Text, gui::GetTextSecondaryVec4());
612 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
613 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
615 ImGui::PushStyleColor(ImGuiCol_ButtonActive,
617 }
618
619 // Bell button - opens notifications panel in right sidebar
620 if (ImGui::SmallButton(ICON_MD_NOTIFICATIONS)) {
621 if (right_panel) {
622 right_panel->TogglePanel(RightPanelManager::PanelType::kNotifications);
624 }
625 }
626
627 ImGui::PopStyleColor(4);
628
629 // Enhanced tooltip showing notifications + hidden status items
630 if (ImGui::IsItemHovered()) {
631 ImGui::BeginTooltip();
632
633 // Notifications
634 if (unread > 0) {
635 ImGui::PushStyleColor(ImGuiCol_Text, gui::GetPrimaryVec4());
636 ImGui::Text("%s %zu new notification%s", ICON_MD_NOTIFICATIONS, unread,
637 unread == 1 ? "" : "s");
638 ImGui::PopStyleColor();
639 } else {
640 ImGui::PushStyleColor(ImGuiCol_Text, gui::GetTextSecondaryVec4());
641 ImGui::Text(ICON_MD_NOTIFICATIONS " No new notifications");
642 ImGui::PopStyleColor();
643 }
644
645 ImGui::TextDisabled("Click to open Notifications panel");
646
647 // Show hidden status items if any
648 if (!show_dirty && has_dirty_rom) {
649 ImGui::Separator();
650 ImGui::PushStyleColor(
651 ImGuiCol_Text,
653 gui::ThemeManager::Get().GetCurrentTheme().warning));
654 ImGui::Text(ICON_MD_FIBER_MANUAL_RECORD " Unsaved changes: %s",
655 current_rom->short_name().c_str());
656 ImGui::PopStyleColor();
657 }
658
659 if (!show_session && has_multiple_sessions) {
660 if (!show_dirty && has_dirty_rom) {
661 // Already had a separator
662 } else {
663 ImGui::Separator();
664 }
665 ImGui::PushStyleColor(ImGuiCol_Text, gui::GetTextSecondaryVec4());
666 ImGui::Text(ICON_MD_LAYERS " %zu sessions active",
668 ImGui::PopStyleColor();
669 }
670
671 ImGui::EndTooltip();
672 }
673}
674
676 auto* current_rom = editor_manager_->GetCurrentRom();
677
678 // Consistent button styling with other menubar buttons
679 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
680 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
682 ImGui::PushStyleColor(ImGuiCol_ButtonActive,
684 ImGui::PushStyleColor(ImGuiCol_Text, gui::GetTextSecondaryVec4());
685
686 // Store button position for popup anchoring
687 ImVec2 button_min = ImGui::GetCursorScreenPos();
688
689 if (ImGui::SmallButton(ICON_MD_LAYERS)) {
690 ImGui::OpenPopup("##SessionSwitcherPopup");
691 }
692
693 ImVec2 button_max = ImGui::GetItemRectMax();
694
695 ImGui::PopStyleColor(4);
696
697 if (ImGui::IsItemHovered()) {
698 std::string tooltip = current_rom && current_rom->is_loaded()
699 ? current_rom->short_name()
700 : "No ROM loaded";
701 ImGui::SetTooltip("%s\n%zu sessions open (Ctrl+Tab)", tooltip.c_str(),
703 }
704
705 // Anchor popup to right edge - position so right edge aligns with button
706 const float popup_width = 250.0f;
707 const float screen_width = ImGui::GetIO().DisplaySize.x;
708 const float popup_x =
709 std::min(button_min.x, screen_width - popup_width - 10.0f);
710
711 ImGui::SetNextWindowPos(ImVec2(popup_x, button_max.y + 2.0f),
712 ImGuiCond_Appearing);
713
714 // Session switcher popup
715 if (ImGui::BeginPopup("##SessionSwitcherPopup")) {
716 ImGui::Text(ICON_MD_LAYERS " Sessions");
717 ImGui::Separator();
718
719 for (size_t i = 0; i < session_coordinator_.GetTotalSessionCount(); ++i) {
721 continue;
722
723 auto* session =
725 if (!session)
726 continue;
727
728 Rom* rom = &session->rom;
729 ImGui::PushID(static_cast<int>(i));
730
731 bool is_current = (rom == current_rom);
732 if (is_current) {
733 ImGui::PushStyleColor(ImGuiCol_Text, gui::GetPrimaryVec4());
734 }
735
736 std::string label =
737 rom->is_loaded()
738 ? absl::StrFormat("%s %s", ICON_MD_DESCRIPTION,
739 rom->short_name().c_str())
740 : absl::StrFormat("%s Session %zu", ICON_MD_DESCRIPTION, i + 1);
741
742 if (ImGui::Selectable(label.c_str(), is_current)) {
744 }
745
746 if (is_current) {
747 ImGui::PopStyleColor();
748 }
749
750 ImGui::PopID();
751 }
752
753 ImGui::EndPopup();
754 }
755}
756
757// ============================================================================
758// Session UI Delegation
759// ============================================================================
760// All session-related UI is now managed by SessionCoordinator to eliminate
761// duplication. UICoordinator methods delegate to SessionCoordinator.
762
766
770
772 if (visible) {
774 } else {
776 }
777}
778
779// Emulator visibility delegates to PanelManager (single source of truth)
781 size_t session_id = session_coordinator_.GetActiveSessionIndex();
782 return panel_manager_.IsPanelVisible(session_id, "emulator.cpu_debugger");
783}
784
786 size_t session_id = session_coordinator_.GetActiveSessionIndex();
787 if (visible) {
788 panel_manager_.ShowPanel(session_id, "emulator.cpu_debugger");
789 } else {
790 panel_manager_.HidePanel(session_id, "emulator.cpu_debugger");
791 }
792}
793
794// ============================================================================
795// Layout and Window Management UI
796// ============================================================================
797
799 // TODO: [EditorManagerRefactor] Implement full layout preset UI with
800 // save/load For now, this is accessed via Window menu items that call
801 // workspace_manager directly
802}
803
805 // ============================================================================
806 // CENTRALIZED WELCOME SCREEN LOGIC (using StartupSurface state)
807 // ============================================================================
808 // Uses ShouldShowWelcome() as single source of truth
809 // Auto-transitions to Dashboard on ROM load
810 // Activity Bar hidden when welcome is visible
811 // ============================================================================
812
813 if (!editor_manager_) {
814 LOG_ERROR("UICoordinator",
815 "EditorManager is null - cannot check ROM state");
816 return;
817 }
818
819 if (!welcome_screen_) {
820 LOG_ERROR("UICoordinator", "WelcomeScreen object is null - cannot render");
821 return;
822 }
823
824 // Check ROM state and update startup surface accordingly
825 auto* current_rom = editor_manager_->GetCurrentRom();
826 bool rom_is_loaded = current_rom && current_rom->is_loaded();
827
828 // Auto-transition: ROM loaded -> Dashboard (unless manually closed)
829 if (rom_is_loaded && current_startup_surface_ == StartupSurface::kWelcome &&
832 }
833
834 // Auto-transition: ROM unloaded -> Welcome (reset to welcome state)
835 if (!rom_is_loaded && current_startup_surface_ != StartupSurface::kWelcome &&
838 }
839
840 // Use centralized visibility check
841 if (!ShouldShowWelcome()) {
842 return;
843 }
844
845 // Reset first show flag to override ImGui ini state
846 welcome_screen_->ResetFirstShow();
847
848 // Update recent projects before showing
849 welcome_screen_->RefreshRecentProjects();
850
851 // Pass layout offsets so welcome screen centers within dockspace region
852 // Note: Activity Bar is hidden when welcome is shown, so left_offset = 0
853 float left_offset =
855 float right_offset = editor_manager_->GetRightLayoutOffset();
856 welcome_screen_->SetLayoutOffsets(left_offset, right_offset);
857
858 // Show the welcome screen window
859 bool is_open = true;
860 welcome_screen_->Show(&is_open);
861
862 // If user closed it via X button, respect that and transition to appropriate state
863 if (!is_open) {
865 // Transition to Dashboard if ROM loaded, stay in Editor state otherwise
866 if (rom_is_loaded) {
868 }
869 }
870}
871
873 // TODO: [EditorManagerRefactor] Implement project help dialog
874 // Show context-sensitive help based on current editor and ROM state
875}
876
879 ImGui::Begin("Save Workspace Preset", &show_save_workspace_preset_,
880 ImGuiWindowFlags_AlwaysAutoResize);
881 static char preset_name[128] = "";
882 ImGui::InputText("Name", preset_name, IM_ARRAYSIZE(preset_name));
883 if (ImGui::Button("Save", gui::kDefaultModalSize)) {
884 if (strlen(preset_name) > 0) {
888 preset_name[0] = '\0';
889 }
890 }
891 ImGui::SameLine();
892 if (ImGui::Button("Cancel", gui::kDefaultModalSize)) {
894 preset_name[0] = '\0';
895 }
896 ImGui::End();
897 }
898
900 ImGui::Begin("Load Workspace Preset", &show_load_workspace_preset_,
901 ImGuiWindowFlags_AlwaysAutoResize);
902
903 // Lazy load workspace presets when UI is accessed
905
906 if (auto* workspace_manager = editor_manager_->workspace_manager()) {
907 for (const auto& name : workspace_manager->workspace_presets()) {
908 if (ImGui::Selectable(name.c_str())) {
912 }
913 }
914 if (workspace_manager->workspace_presets().empty())
915 ImGui::Text("No presets found");
916 }
917 ImGui::End();
918 }
919}
920
922 // TODO: [EditorManagerRefactor] Implement window management dialog
923 // Provide UI for toggling window visibility, managing docking, etc.
924}
925
927 // Draw all registered popups
929}
930
931void UICoordinator::ShowPopup(const std::string& popup_name) {
932 popup_manager_.Show(popup_name.c_str());
933}
934
935void UICoordinator::HidePopup(const std::string& popup_name) {
936 popup_manager_.Hide(popup_name.c_str());
937}
938
940 // Display Settings is now a popup managed by PopupManager
941 // Delegate directly to PopupManager instead of UICoordinator
943}
944
946 if (!editor_manager_)
947 return;
948
949 auto* current_editor = editor_manager_->GetCurrentEditor();
950 if (!current_editor)
951 return;
952
953 std::string category =
954 editor_registry_.GetEditorCategory(current_editor->type());
955 size_t session_id = session_coordinator_.GetActiveSessionIndex();
956 panel_manager_.HideAllPanelsInCategory(session_id, category);
957
958 LOG_INFO("UICoordinator", "Hid all panels in category: %s", category.c_str());
959}
960
961// ============================================================================
962// Sidebar Visibility (delegates to PanelManager)
963// ============================================================================
964
968
972
976
980
984
985// Material Design component helpers
986void UICoordinator::DrawMaterialButton(const std::string& text,
987 const std::string& icon,
988 const ImVec4& color,
989 std::function<void()> callback,
990 bool enabled) {
991 if (!enabled) {
992 ImGui::PushStyleColor(ImGuiCol_Button,
994 ImGui::PushStyleColor(ImGuiCol_Text, gui::GetOnSurfaceVariantVec4());
995 }
996
997 std::string button_text =
998 absl::StrFormat("%s %s", icon.c_str(), text.c_str());
999 if (ImGui::Button(button_text.c_str())) {
1000 if (enabled && callback) {
1001 callback();
1002 }
1003 }
1004
1005 if (!enabled) {
1006 ImGui::PopStyleColor(2);
1007 }
1008}
1009
1010// Layout and positioning helpers
1011void UICoordinator::CenterWindow(const std::string& window_name) {
1012 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(),
1013 ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
1014}
1015
1016void UICoordinator::PositionWindow(const std::string& window_name, float x,
1017 float y) {
1018 ImGui::SetNextWindowPos(ImVec2(x, y), ImGuiCond_Appearing);
1019}
1020
1021void UICoordinator::SetWindowSize(const std::string& window_name, float width,
1022 float height) {
1023 ImGui::SetNextWindowSize(ImVec2(width, height), ImGuiCond_FirstUseEver);
1024}
1025
1028 return;
1029
1030 using namespace ImGui;
1031 auto& theme = gui::ThemeManager::Get().GetCurrentTheme();
1032
1033 SetNextWindowPos(GetMainViewport()->GetCenter(), ImGuiCond_Appearing,
1034 ImVec2(0.5f, 0.5f));
1035 SetNextWindowSize(ImVec2(800, 600), ImGuiCond_FirstUseEver);
1036
1037 bool show_palette = true;
1038 if (Begin(absl::StrFormat("%s Command Palette", ICON_MD_SEARCH).c_str(),
1039 &show_palette, ImGuiWindowFlags_NoCollapse)) {
1040 // Search input with focus management
1041 SetNextItemWidth(-100);
1042 if (IsWindowAppearing()) {
1043 SetKeyboardFocusHere();
1045 }
1046
1047 bool input_changed = InputTextWithHint(
1048 "##cmd_query",
1049 absl::StrFormat("%s Search commands (fuzzy matching enabled)...",
1051 .c_str(),
1053
1054 SameLine();
1055 if (Button(absl::StrFormat("%s Clear", ICON_MD_CLEAR).c_str())) {
1056 command_palette_query_[0] = '\0';
1057 input_changed = true;
1059 }
1060
1061 Separator();
1062
1063 // Fuzzy filter commands with scoring
1064 std::vector<std::pair<int, std::pair<std::string, std::string>>>
1065 scored_commands;
1066 std::string query_lower = command_palette_query_;
1067 std::transform(query_lower.begin(), query_lower.end(), query_lower.begin(),
1068 ::tolower);
1069
1070 for (const auto& entry : shortcut_manager_.GetShortcuts()) {
1071 const auto& name = entry.first;
1072 const auto& shortcut = entry.second;
1073
1074 std::string name_lower = name;
1075 std::transform(name_lower.begin(), name_lower.end(), name_lower.begin(),
1076 ::tolower);
1077
1078 int score = 0;
1079 if (command_palette_query_[0] == '\0') {
1080 score = 1; // Show all when no query
1081 } else if (name_lower.find(query_lower) == 0) {
1082 score = 1000; // Starts with
1083 } else if (name_lower.find(query_lower) != std::string::npos) {
1084 score = 500; // Contains
1085 } else {
1086 // Fuzzy match - characters in order
1087 size_t text_idx = 0, query_idx = 0;
1088 while (text_idx < name_lower.length() &&
1089 query_idx < query_lower.length()) {
1090 if (name_lower[text_idx] == query_lower[query_idx]) {
1091 score += 10;
1092 query_idx++;
1093 }
1094 text_idx++;
1095 }
1096 if (query_idx != query_lower.length())
1097 score = 0;
1098 }
1099
1100 if (score > 0) {
1101 std::string shortcut_text =
1102 shortcut.keys.empty()
1103 ? ""
1104 : absl::StrFormat("(%s)", PrintShortcut(shortcut.keys).c_str());
1105 scored_commands.push_back({score, {name, shortcut_text}});
1106 }
1107 }
1108
1109 std::sort(scored_commands.begin(), scored_commands.end(),
1110 [](const auto& a, const auto& b) { return a.first > b.first; });
1111
1112 // Display results with categories
1113 if (BeginTabBar("CommandCategories")) {
1114 if (BeginTabItem(
1115 absl::StrFormat("%s All Commands", ICON_MD_LIST).c_str())) {
1117 "CommandPaletteTable", 3,
1118 ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg |
1119 ImGuiTableFlags_SizingStretchProp,
1120 ImVec2(0, -30))) {
1121 TableSetupColumn("Command", ImGuiTableColumnFlags_WidthStretch, 0.5f);
1122 TableSetupColumn("Shortcut", ImGuiTableColumnFlags_WidthStretch,
1123 0.3f);
1124 TableSetupColumn("Score", ImGuiTableColumnFlags_WidthStretch, 0.2f);
1125 TableHeadersRow();
1126
1127 for (size_t i = 0; i < scored_commands.size(); ++i) {
1128 const auto& [score, cmd_pair] = scored_commands[i];
1129 const auto& [command_name, shortcut_text] = cmd_pair;
1130
1131 TableNextRow();
1132 TableNextColumn();
1133
1134 PushID(static_cast<int>(i));
1135 bool is_selected =
1136 (static_cast<int>(i) == command_palette_selected_idx_);
1137 if (Selectable(command_name.c_str(), is_selected,
1138 ImGuiSelectableFlags_SpanAllColumns)) {
1140 const auto& shortcuts = shortcut_manager_.GetShortcuts();
1141 auto it = shortcuts.find(command_name);
1142 if (it != shortcuts.end() && it->second.callback) {
1143 it->second.callback();
1144 show_command_palette_ = false;
1145 }
1146 }
1147 PopID();
1148
1149 TableNextColumn();
1150 PushStyleColor(ImGuiCol_Text,
1151 gui::ConvertColorToImVec4(theme.text_secondary));
1152 Text("%s", shortcut_text.c_str());
1153 PopStyleColor();
1154
1155 TableNextColumn();
1156 if (score > 0) {
1157 PushStyleColor(ImGuiCol_Text,
1158 gui::ConvertColorToImVec4(theme.text_disabled));
1159 Text("%d", score);
1160 PopStyleColor();
1161 }
1162 }
1163
1165 }
1166 EndTabItem();
1167 }
1168
1169 if (BeginTabItem(absl::StrFormat("%s Recent", ICON_MD_HISTORY).c_str())) {
1170 Text("Recent commands coming soon...");
1171 EndTabItem();
1172 }
1173
1174 if (BeginTabItem(absl::StrFormat("%s Frequent", ICON_MD_STAR).c_str())) {
1175 Text("Frequent commands coming soon...");
1176 EndTabItem();
1177 }
1178
1179 EndTabBar();
1180 }
1181
1182 // Status bar with tips
1183 Separator();
1184 Text("%s %zu commands | Score: fuzzy match", ICON_MD_INFO,
1185 scored_commands.size());
1186 SameLine();
1187 PushStyleColor(ImGuiCol_Text,
1188 gui::ConvertColorToImVec4(theme.text_disabled));
1189 Text("| ↑↓=Navigate | Enter=Execute | Esc=Close");
1190 PopStyleColor();
1191 }
1192 End();
1193
1194 // Update visibility state
1195 if (!show_palette) {
1196 show_command_palette_ = false;
1197 }
1198}
1199
1202 return;
1203
1204 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(),
1205 ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
1206 ImGui::SetNextWindowSize(ImVec2(800, 600), ImGuiCond_FirstUseEver);
1207
1208 bool show_search = true;
1209 if (ImGui::Begin(
1210 absl::StrFormat("%s Global Search", ICON_MD_MANAGE_SEARCH).c_str(),
1211 &show_search, ImGuiWindowFlags_NoCollapse)) {
1212 // Enhanced search input with focus management
1213 ImGui::SetNextItemWidth(-100);
1214 if (ImGui::IsWindowAppearing()) {
1215 ImGui::SetKeyboardFocusHere();
1216 }
1217
1218 bool input_changed = ImGui::InputTextWithHint(
1219 "##global_query",
1220 absl::StrFormat("%s Search everything...", ICON_MD_SEARCH).c_str(),
1222
1223 ImGui::SameLine();
1224 if (ImGui::Button(absl::StrFormat("%s Clear", ICON_MD_CLEAR).c_str())) {
1225 global_search_query_[0] = '\0';
1226 input_changed = true;
1227 }
1228
1229 ImGui::Separator();
1230
1231 // Tabbed search results for better organization
1232 if (ImGui::BeginTabBar("SearchResultTabs")) {
1233 // Recent Files Tab
1234 if (ImGui::BeginTabItem(
1235 absl::StrFormat("%s Recent Files", ICON_MD_HISTORY).c_str())) {
1237 auto recent_files = manager.GetRecentFiles();
1238
1239 if (ImGui::BeginTable("RecentFilesTable", 3,
1240 ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg |
1241 ImGuiTableFlags_SizingStretchProp)) {
1242 ImGui::TableSetupColumn("File", ImGuiTableColumnFlags_WidthStretch,
1243 0.6f);
1244 ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed,
1245 80.0f);
1246 ImGui::TableSetupColumn("Action", ImGuiTableColumnFlags_WidthFixed,
1247 100.0f);
1248 ImGui::TableHeadersRow();
1249
1250 for (const auto& file : recent_files) {
1251 if (global_search_query_[0] != '\0' &&
1252 file.find(global_search_query_) == std::string::npos)
1253 continue;
1254
1255 ImGui::TableNextRow();
1256 ImGui::TableNextColumn();
1257 ImGui::Text("%s", util::GetFileName(file).c_str());
1258
1259 ImGui::TableNextColumn();
1260 std::string ext = util::GetFileExtension(file);
1261 if (ext == "sfc" || ext == "smc") {
1262 ImGui::TextColored(ImVec4(0.2f, 0.8f, 0.2f, 1.0f), "%s ROM",
1264 } else if (ext == "yaze") {
1265 ImGui::TextColored(ImVec4(0.2f, 0.6f, 0.8f, 1.0f), "%s Project",
1267 } else {
1268 ImGui::Text("%s File", ICON_MD_DESCRIPTION);
1269 }
1270
1271 ImGui::TableNextColumn();
1272 ImGui::PushID(file.c_str());
1273 if (ImGui::Button("Open")) {
1274 auto status = editor_manager_->OpenRomOrProject(file);
1275 if (!status.ok()) {
1277 absl::StrCat("Failed to open: ", status.message()),
1279 }
1281 }
1282 ImGui::PopID();
1283 }
1284
1285 ImGui::EndTable();
1286 }
1287 ImGui::EndTabItem();
1288 }
1289
1290 // Labels Tab (only if ROM is loaded)
1291 auto* current_rom = editor_manager_->GetCurrentRom();
1292 if (current_rom && current_rom->resource_label()) {
1293 if (ImGui::BeginTabItem(
1294 absl::StrFormat("%s Labels", ICON_MD_LABEL).c_str())) {
1295 auto& labels = current_rom->resource_label()->labels_;
1296
1297 if (ImGui::BeginTable("LabelsTable", 3,
1298 ImGuiTableFlags_ScrollY |
1299 ImGuiTableFlags_RowBg |
1300 ImGuiTableFlags_SizingStretchProp)) {
1301 ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed,
1302 100.0f);
1303 ImGui::TableSetupColumn("Label", ImGuiTableColumnFlags_WidthStretch,
1304 0.4f);
1305 ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthStretch,
1306 0.6f);
1307 ImGui::TableHeadersRow();
1308
1309 for (const auto& type_pair : labels) {
1310 for (const auto& kv : type_pair.second) {
1311 if (global_search_query_[0] != '\0' &&
1312 kv.first.find(global_search_query_) == std::string::npos &&
1313 kv.second.find(global_search_query_) == std::string::npos)
1314 continue;
1315
1316 ImGui::TableNextRow();
1317 ImGui::TableNextColumn();
1318 ImGui::Text("%s", type_pair.first.c_str());
1319
1320 ImGui::TableNextColumn();
1321 if (ImGui::Selectable(kv.first.c_str(), false,
1322 ImGuiSelectableFlags_SpanAllColumns)) {
1323 // Future: navigate to related editor/location
1324 }
1325
1326 ImGui::TableNextColumn();
1327 ImGui::TextDisabled("%s", kv.second.c_str());
1328 }
1329 }
1330
1331 ImGui::EndTable();
1332 }
1333 ImGui::EndTabItem();
1334 }
1335 }
1336
1337 // Sessions Tab
1339 if (ImGui::BeginTabItem(
1340 absl::StrFormat("%s Sessions", ICON_MD_TAB).c_str())) {
1341 ImGui::Text("Search and switch between active sessions:");
1342
1343 for (size_t i = 0; i < session_coordinator_.GetTotalSessionCount();
1344 ++i) {
1345 std::string session_info =
1347 if (session_info == "[CLOSED SESSION]")
1348 continue;
1349
1350 if (global_search_query_[0] != '\0' &&
1351 session_info.find(global_search_query_) == std::string::npos)
1352 continue;
1353
1354 bool is_current =
1356 if (is_current) {
1357 ImGui::PushStyleColor(ImGuiCol_Text,
1358 ImVec4(0.2f, 0.8f, 0.2f, 1.0f));
1359 }
1360
1361 if (ImGui::Selectable(absl::StrFormat("%s %s %s", ICON_MD_TAB,
1362 session_info.c_str(),
1363 is_current ? "(Current)" : "")
1364 .c_str())) {
1365 if (!is_current) {
1368 }
1369 }
1370
1371 if (is_current) {
1372 ImGui::PopStyleColor();
1373 }
1374 }
1375 ImGui::EndTabItem();
1376 }
1377 }
1378
1379 ImGui::EndTabBar();
1380 }
1381
1382 // Status bar
1383 ImGui::Separator();
1384 ImGui::Text("%s Global search across all YAZE data", ICON_MD_INFO);
1385 }
1386 ImGui::End();
1387
1388 // Update visibility state
1389 if (!show_search) {
1391 }
1392}
1393
1394// ============================================================================
1395// Startup Surface Management (Single Source of Truth)
1396// ============================================================================
1397
1400 current_startup_surface_ = surface;
1401
1402 // Log state transitions for debugging
1403 const char* surface_names[] = {"Welcome", "Dashboard", "Editor"};
1404 LOG_INFO("UICoordinator", "Startup surface: %s -> %s",
1405 surface_names[static_cast<int>(old_surface)],
1406 surface_names[static_cast<int>(surface)]);
1407
1408 // Update dependent visibility flags
1409 switch (surface) {
1411 show_welcome_screen_ = true;
1412 show_editor_selection_ = false; // Dashboard hidden
1413 // Activity Bar will be hidden (checked via ShouldShowActivityBar)
1414 break;
1416 show_welcome_screen_ = false;
1417 show_editor_selection_ = true; // Dashboard shown
1418 break;
1420 show_welcome_screen_ = false;
1421 show_editor_selection_ = false; // Dashboard hidden
1422 break;
1423 }
1424}
1425
1427 // Respect CLI overrides
1429 return false;
1430 }
1432 return true;
1433 }
1434
1435 // Default: show welcome only when in welcome state and not manually closed
1438}
1439
1441 // Respect CLI overrides
1443 return false;
1444 }
1446 return true;
1447 }
1448
1449 // Default: show dashboard only when in dashboard state
1451}
1452
1454 // Activity Bar hidden on cold start (welcome screen)
1455 // Only show after ROM is loaded
1457 return false;
1458 }
1459
1460 // Check if ROM is actually loaded
1461 if (editor_manager_) {
1462 auto* current_rom = editor_manager_->GetCurrentRom();
1463 if (!current_rom || !current_rom->is_loaded()) {
1464 return false;
1465 }
1466 }
1467
1468 return true;
1469}
1470
1471} // namespace editor
1472} // namespace yaze
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
Definition rom.h:24
auto short_name() const
Definition rom.h:143
bool is_loaded() const
Definition rom.h:128
The EditorManager controls the main editor window and manages the various editor classes.
void SaveWorkspacePreset(const std::string &name)
void SwitchToSession(size_t index)
WorkspaceManager * workspace_manager()
void LoadWorkspacePreset(const std::string &name)
absl::Status CreateNewProject(const std::string &template_name="Basic ROM Hack")
auto GetCurrentEditor() const -> Editor *
absl::Status LoadRom()
Load a ROM file into a new or existing session.
auto GetCurrentRom() const -> Rom *
absl::Status OpenRomOrProject(const std::string &filename)
RightPanelManager * right_panel_manager()
Manages editor types, categories, and lifecycle.
static std::string GetEditorCategory(EditorType type)
Central registry for all editor cards with session awareness and dependency injection.
bool ShowPanel(size_t session_id, const std::string &base_card_id)
bool IsPanelVisible(size_t session_id, const std::string &base_card_id) const
void SetSidebarVisible(bool visible)
bool HidePanel(size_t session_id, const std::string &base_card_id)
void HideAllPanelsInCategory(size_t session_id, const std::string &category)
void Show(const char *name)
void Hide(const char *name)
Handles all project file operations with ROM-first workflow.
float GetPanelWidth() const
Get the width of the panel when expanded.
bool IsPanelExpanded() const
Check if any panel is currently expanded.
bool DrawPanelToggleButtons()
Draw toggle buttons for the status cluster.
bool IsPanelActive(PanelType type) const
Check if a specific panel is active.
Handles all ROM file I/O operations.
High-level orchestrator for multi-session UI.
void * GetSession(size_t index) const
std::string GetSessionDisplayName(size_t index) const
bool IsSessionClosed(size_t index) 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_
UICoordinator(EditorManager *editor_manager, RomFileManager &rom_manager, ProjectManager &project_manager, EditorRegistry &editor_registry, PanelManager &card_registry, SessionCoordinator &session_coordinator, WindowDelegate &window_delegate, ToastManager &toast_manager, PopupManager &popup_manager, ShortcutManager &shortcut_manager)
void SetEmulatorVisible(bool visible)
void DrawNotificationBell(bool show_dirty, bool has_dirty_rom, bool show_session, bool has_multiple_sessions)
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)
void SetWelcomeScreenBehavior(StartupVisibility mode)
void SetDashboardBehavior(StartupVisibility mode)
std::unique_ptr< WelcomeScreen > welcome_screen_
static float GetMenuBarIconButtonWidth()
EditorRegistry & editor_registry_
void CenterWindow(const std::string &window_name)
Low-level window operations with minimal dependencies.
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)
const Theme & GetCurrentTheme() const
static ThemeManager & Get()
static RecentFilesManager & GetInstance()
Definition project.h:312
#define ICON_MD_NOTIFICATIONS
Definition icons.h:1335
#define ICON_MD_FOLDER_OPEN
Definition icons.h:813
#define ICON_MD_SETTINGS
Definition icons.h:1699
#define ICON_MD_APPS
Definition icons.h:168
#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_LABEL
Definition icons.h:1053
#define ICON_MD_VIDEOGAME_ASSET
Definition icons.h:2076
#define ICON_MD_EDIT
Definition icons.h:645
#define ICON_MD_LIST
Definition icons.h:1094
#define ICON_MD_MANAGE_SEARCH
Definition icons.h:1172
#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_DASHBOARD
Definition icons.h:517
#define ICON_MD_TAB
Definition icons.h:1930
#define ICON_MD_FOLDER
Definition icons.h:809
#define ICON_MD_VIEW_SIDEBAR
Definition icons.h:2095
#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:109
#define LOG_INFO(category, format,...)
Definition log.h:105
Definition input.cc:22
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:23
ImVec4 GetSurfaceContainerHighestVec4()
ImVec4 GetPrimaryVec4()
ImVec4 GetTextDisabledVec4()
ImVec4 GetTextSecondaryVec4()
ImVec4 GetSurfaceContainerHighVec4()
constexpr ImVec2 kDefaultModalSize
Definition input.h:21
ImVec4 GetOnSurfaceVariantVec4()
ImVec4 GetSurfaceContainerVec4()
SafeAreaInsets GetSafeAreaInsets()
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.