12#include "absl/strings/match.h"
13#include "absl/strings/str_format.h"
16#include <emscripten.h>
44#include "imgui/imgui.h"
58 : editor_manager_(editor_manager),
59 rom_manager_(rom_manager),
60 project_manager_(project_manager),
61 editor_registry_(editor_registry),
62 window_manager_(window_manager),
63 session_coordinator_(session_coordinator),
64 window_delegate_(window_delegate),
65 toast_manager_(toast_manager),
66 popup_manager_(popup_manager),
67 shortcut_manager_(shortcut_manager) {
83 var romInput = document.getElementById(
'rom-input');
95 absl::StrFormat(
"Failed to load ROM: %s", status.message()),
98#if !(defined(__APPLE__) && TARGET_OS_IOS == 1)
112 [
this](
const std::string& template_name,
const std::string& rom_path,
113 const std::string& project_name) -> absl::Status {
115 return absl::FailedPreconditionError(
116 "Editor manager is not available");
119 template_name, rom_path, project_name);
122 absl::StrFormat(
"Failed to create project: %s", status.message()),
127 project_name, template_name),
130 return absl::OkStatus();
133 welcome_screen_->SetOpenProjectCallback([
this](
const std::string& filepath) {
138 absl::StrFormat(
"Failed to open project: %s", status.message()),
216 if (!ImGui::GetCurrentContext()) {
220 const ImGuiViewport* viewport = ImGui::GetMainViewport();
225 ImDrawList* bg_draw_list =
226 ImGui::GetBackgroundDrawList(
const_cast<ImGuiViewport*
>(viewport));
229 auto current_theme = theme_manager.GetCurrentTheme();
233 ImVec2 grid_pos = viewport->WorkPos;
234 ImVec2 grid_size = viewport->WorkSize;
235 bg_renderer.RenderDockingBackground(bg_draw_list, grid_pos, grid_size,
236 current_theme.primary);
257#ifdef YAZE_BUILD_AGENT_UI
288 const ImGuiViewport* viewport = ImGui::GetMainViewport();
292 const float width = viewport->WorkSize.x;
293#if defined(__APPLE__) && TARGET_OS_IOS == 1
296 static bool compact_mode =
false;
297 constexpr float kEnterCompactWidth = 900.0f;
298 constexpr float kExitCompactWidth = 940.0f;
300 compact_mode ? (width < kExitCompactWidth) : (width < kEnterCompactWidth);
303 return width < 900.0f;
315 {{ImGuiCol_Button, ImVec4(0, 0, 0, 0)},
321 bool clicked = ImGui::SmallButton(icon);
323 if (tooltip && ImGui::IsItemHovered()) {
324 ImGui::SetTooltip(
"%s", tooltip);
332 const float frame_padding = ImGui::GetStyle().FramePadding.x;
335 return icon_width + frame_padding * 2.0f;
348 const float item_spacing = 6.0f;
349 const float padding = 8.0f;
351 auto CalcSmallButtonWidth = [](
const char* label) ->
float {
353 const float frame_padding = ImGui::GetStyle().FramePadding.x;
354 const float text_w = ImGui::CalcTextSize(label).x;
355 return text_w + frame_padding * 2.0f;
359 const ImGuiViewport* viewport = ImGui::GetMainViewport();
360 const float true_viewport_right = viewport->WorkPos.x + viewport->WorkSize.x;
362 const bool has_panel_toggles =
364 float panel_buttons_width = 0.0f;
365 if (has_panel_toggles) {
371 float panel_region_width = panel_buttons_width;
374 panel_region_width +=
379 float panel_screen_x = true_viewport_right - panel_region_width;
387 const float window_width = ImGui::GetWindowWidth();
388 const float window_screen_x = ImGui::GetWindowPos().x;
389 const float menu_items_end = ImGui::GetCursorPosX() + 16.0f;
392 float panel_local_x = panel_screen_x - window_screen_x;
394 std::min(window_width - padding, panel_local_x - item_spacing);
398 current_rom && current_rom->is_loaded() && current_rom->dirty();
405 const float available_width = region_end - menu_items_end - padding;
414 has_multiple_sessions &&
415 (required_width + session_width + item_spacing) <= available_width;
417 required_width += session_width + item_spacing;
422 has_dirty_rom && (required_width + dirty_width) <= available_width;
424 required_width += dirty_width;
428 float start_pos = std::max(menu_items_end, region_end - required_width);
433 ImGui::SameLine(start_pos);
435 ImVec2(item_spacing, 0.0f));
442 if (ImGui::IsItemHovered()) {
443 ImGui::SetTooltip(tr(
"Unsaved changes: %s"),
444 current_rom->short_name().c_str());
457 has_multiple_sessions);
462 if (has_panel_toggles) {
463 float menu_bar_y = ImGui::GetCursorScreenPos().y;
464 ImGui::SetCursorScreenPos(ImVec2(panel_screen_x, menu_bar_y));
472 "Hide menu bar (Alt to restore)")) {
485 ImGuiWindowFlags flags =
486 ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize |
487 ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar |
488 ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_AlwaysAutoResize |
489 ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoSavedSettings;
491 ImGui::SetNextWindowPos(ImVec2(8, 8));
492 ImGui::SetNextWindowBgAlpha(0.7f);
494 if (ImGui::Begin(
"##MenuBarRestore",
nullptr, flags)) {
505 if (ImGui::IsItemHovered()) {
506 ImGui::SetTooltip(tr(
"Show menu bar (Alt)"));
512 if (ImGui::IsKeyPressed(ImGuiKey_LeftAlt) ||
513 ImGui::IsKeyPressed(ImGuiKey_RightAlt)) {
520 bool has_multiple_sessions) {
530 unread > 0 || is_active)) {
538 if (ImGui::IsItemHovered()) {
539 ImGui::BeginTooltip();
550 ImGui::TextDisabled(tr(
"Click to open Notifications panel"));
553 if (!show_dirty && has_dirty_rom) {
558 current_rom->short_name().c_str());
561 if (!show_session && has_multiple_sessions) {
562 if (!show_dirty && has_dirty_rom) {
580 ImVec2 button_min = ImGui::GetCursorScreenPos();
582 std::string tooltip =
583 current_rom && current_rom->is_loaded()
584 ? absl::StrFormat(
"%s\n%zu sessions open (Ctrl+Tab)",
585 current_rom->short_name().c_str(),
587 : absl::StrFormat(
"No ROM loaded\n%zu sessions open (Ctrl+Tab)",
591 ImGui::OpenPopup(
"##SessionSwitcherPopup");
594 ImVec2 button_max = ImGui::GetItemRectMax();
597 const float popup_width = 250.0f;
598 const float screen_width = ImGui::GetIO().DisplaySize.x;
599 const float popup_x =
600 std::min(button_min.x, screen_width - popup_width - 10.0f);
602 ImGui::SetNextWindowPos(ImVec2(popup_x, button_max.y + 2.0f),
603 ImGuiCond_Appearing);
606 if (ImGui::BeginPopup(
"##SessionSwitcherPopup")) {
619 Rom* rom = &session->rom;
620 ImGui::PushID(
static_cast<int>(i));
622 bool is_current = (rom == current_rom);
623 std::optional<gui::StyleColorGuard> current_guard;
634 if (ImGui::Selectable(label.c_str(), is_current)) {
674 if (initial_query !=
nullptr) {
698 auto emulator_windows =
700 for (
const auto& window : emulator_windows) {
701 if (window.visibility_flag && *window.visibility_flag) {
711 auto default_windows =
713 for (
const auto& window_id : default_windows) {
728 for (
const auto& window : windows) {
729 if (window.visibility_flag && *window.visibility_flag) {
739 auto default_windows =
741 for (
const auto& window_id : default_windows) {
770 "EditorManager is null - cannot check ROM state");
775 LOG_ERROR(
"UICoordinator",
"WelcomeScreen object is null - cannot render");
781 bool rom_is_loaded = current_rom && current_rom->
is_loaded();
845 ImGuiWindowFlags_AlwaysAutoResize);
846 static char preset_name[128] =
"";
847 ImGui::InputText(tr(
"Name"), preset_name, IM_ARRAYSIZE(preset_name));
849 if (strlen(preset_name) > 0) {
853 preset_name[0] =
'\0';
859 preset_name[0] =
'\0';
866 ImGuiWindowFlags_AlwaysAutoResize);
872 for (
const auto& name : workspace_manager->workspace_presets()) {
873 if (ImGui::Selectable(name.c_str())) {
879 if (workspace_manager->workspace_presets().empty())
880 ImGui::Text(tr(
"No presets found"));
936 const std::string& icon,
938 std::function<
void()> callback,
940 std::optional<gui::StyleColorGuard> disabled_guard;
942 disabled_guard.emplace(std::initializer_list<gui::StyleColorGuard::Entry>{
947 std::string button_text =
948 absl::StrFormat(
"%s %s", icon.c_str(), text.c_str());
949 if (ImGui::Button(button_text.c_str())) {
950 if (enabled && callback) {
958 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(),
959 ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
964 ImGui::SetNextWindowPos(ImVec2(x, y), ImGuiCond_Appearing);
969 ImGui::SetNextWindowSize(ImVec2(width, height), ImGuiCond_FirstUseEver);
974 if (config_dir.ok()) {
975 std::filesystem::path history_file = *config_dir /
"command_history.json";
981 std::function<
void()> callback) {
1002 using namespace ImGui;
1005 SetNextWindowPos(GetMainViewport()->GetCenter(), ImGuiCond_Appearing,
1006 ImVec2(0.5f, 0.5f));
1007 SetNextWindowSize(ImVec2(800, 600), ImGuiCond_FirstUseEver);
1009 bool show_palette =
true;
1010 bool close_requested =
false;
1012 std::string pending_name;
1013 std::function<void()> pending_callback;
1015 if (Begin(absl::StrFormat(
"%s Command Palette",
ICON_MD_SEARCH).c_str(),
1016 &show_palette, ImGuiWindowFlags_NoCollapse)) {
1017 const bool appearing = IsWindowAppearing();
1025 SetNextItemWidth(-100);
1027 SetKeyboardFocusHere();
1030 bool input_changed = InputTextWithHint(
1032 absl::StrFormat(
"%s Search… try room 4A map 1B msg #12 drawer: "
1040 ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_CallbackHistory,
1041 [](ImGuiInputTextCallbackData*) {
return 0; });
1044 if (Button(absl::StrFormat(
"%s Clear",
ICON_MD_CLEAR).c_str())) {
1046 input_changed =
true;
1048 if (input_changed) {
1057 std::optional<CommandEntry> goto_row;
1068 const int goto_offset = goto_row ? 1 : 0;
1070 if (goto_row && i == 0)
1076 const int row_count =
1078 const bool palette_focused =
1079 IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows);
1080 if (palette_focused && row_count > 0) {
1081 constexpr int kPageStep = 10;
1083 if (IsKeyPressed(ImGuiKey_DownArrow))
1085 if (IsKeyPressed(ImGuiKey_UpArrow))
1087 if (IsKeyPressed(ImGuiKey_PageDown))
1089 if (IsKeyPressed(ImGuiKey_PageUp))
1091 if (IsKeyPressed(ImGuiKey_Home,
false))
1093 if (IsKeyPressed(ImGuiKey_End,
false))
1094 idx = row_count - 1;
1095 idx = std::clamp(idx, 0, row_count - 1);
1105 if (palette_focused && (IsKeyPressed(ImGuiKey_Enter,
false) ||
1106 IsKeyPressed(ImGuiKey_KeypadEnter,
false))) {
1107 if (row_count > 0) {
1110 pending_name = selected.
name;
1111 pending_callback = selected.
callback;
1113 if (!selected.
note.empty())
1120 if (palette_focused && IsKeyPressed(ImGuiKey_Escape,
false)) {
1121 close_requested =
true;
1127 absl::StrFormat(
"%s All Commands",
ICON_MD_LIST).c_str())) {
1129 "CommandPaletteTable", 3,
1130 ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg |
1131 ImGuiTableFlags_SizingStretchProp,
1133 TableSetupScrollFreeze(0, 1);
1134 TableSetupColumn(
"Command", ImGuiTableColumnFlags_WidthStretch,
1136 TableSetupColumn(
"Category", ImGuiTableColumnFlags_WidthStretch,
1138 TableSetupColumn(
"Shortcut", ImGuiTableColumnFlags_WidthStretch,
1142 for (
int i = 0; i < row_count; ++i) {
1150 const ImGuiSelectableFlags flags =
1151 ImGuiSelectableFlags_SpanAllColumns |
1152 (cmd.
enabled ? ImGuiSelectableFlags_None
1153 : ImGuiSelectableFlags_Disabled);
1154 if (Selectable(cmd.
name.c_str(), is_selected, flags)) {
1157 pending_name = cmd.
name;
1166 if (IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
1168 : cmd.
note.c_str());
1192 if (BeginTabItem(absl::StrFormat(
"%s Recent",
ICON_MD_HISTORY).c_str())) {
1194 if (recent.empty()) {
1195 Text(tr(
"No recent commands yet."));
1197 for (
const auto& entry : recent) {
1198 if (Selectable(entry.name.c_str()) && entry.callback) {
1199 pending_name = entry.name;
1200 pending_callback = entry.callback;
1207 if (BeginTabItem(absl::StrFormat(
"%s Frequent",
ICON_MD_STAR).c_str())) {
1209 if (frequent.empty()) {
1210 Text(tr(
"No frequently used commands yet."));
1212 for (
const auto& entry : frequent) {
1213 if (Selectable(absl::StrFormat(
"%s (%d uses)", entry.name,
1217 pending_name = entry.name;
1218 pending_callback = entry.callback;
1230 Text(tr(
"%s %d commands | Go to: room/map/msg <hex|#dec> | drawer: "
1239 if (!show_palette || close_requested) {
1242 if (pending_callback) {
1251 using namespace ImGui;
1255 const ImGuiViewport* viewport = GetMainViewport();
1256 ImDrawList* bg_list = GetBackgroundDrawList();
1257 bg_list->AddRectFilled(viewport->WorkPos,
1258 ImVec2(viewport->WorkPos.x + viewport->WorkSize.x,
1259 viewport->WorkPos.y + viewport->WorkSize.y),
1260 IM_COL32(0, 0, 0, 100));
1262 SetNextWindowPos(viewport->GetCenter(), ImGuiCond_Appearing,
1263 ImVec2(0.5f, 0.3f));
1266 ImVec2(viewport->WorkSize.x * 0.95f, viewport->WorkSize.y * 0.70f),
1267 ImGuiCond_Appearing);
1269 SetNextWindowSize(ImVec2(600, 420), ImGuiCond_Appearing);
1272 const ImGuiWindowFlags finder_flags =
1273 ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove |
1274 ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoDocking |
1275 ImGuiWindowFlags_NoSavedSettings;
1280 if (IsWindowAppearing()) {
1281 SetKeyboardFocusHere();
1285 SetNextItemWidth(-1);
1286 bool input_changed = InputTextWithHint(
1290 if (input_changed) {
1297 struct WindowEntry {
1298 std::string card_id;
1299 std::string display_name;
1301 std::string category;
1306 std::vector<WindowEntry> entries;
1311 for (
const auto& card_id :
1319 if (!query.empty()) {
1328 entries.push_back({card_id, desc->display_name, desc->icon,
1329 desc->category, vis, pin, score});
1333 if (query.empty()) {
1336 entries.begin(), entries.end(),
1337 [
this](
const WindowEntry& lhs,
const WindowEntry& rhs) {
1338 if (lhs.pinned != rhs.pinned)
1339 return lhs.pinned > rhs.pinned;
1340 uint64_t lhs_t = window_manager_.GetWindowMRUTime(lhs.card_id);
1341 uint64_t rhs_t = window_manager_.GetWindowMRUTime(rhs.card_id);
1343 return lhs_t > rhs_t;
1344 return lhs.display_name < rhs.display_name;
1348 std::sort(entries.begin(), entries.end(),
1349 [](
const WindowEntry& lhs,
const WindowEntry& rhs) {
1350 if (lhs.score != rhs.score)
1351 return lhs.score > rhs.score;
1352 if (lhs.pinned != rhs.pinned)
1353 return lhs.pinned > rhs.pinned;
1354 return lhs.display_name < rhs.display_name;
1359 if (IsKeyPressed(ImGuiKey_DownArrow) &&
1366 bool enter_pressed = IsKeyPressed(ImGuiKey_Enter);
1371 PushStyleVar(ImGuiStyleVar_ItemSpacing,
1372 ImVec2(GetStyle().ItemSpacing.x, 10.0f));
1376 BeginChild(
"##PanelFinderList");
1377 for (
int i = 0; i < static_cast<int>(entries.size()); ++i) {
1378 const auto& entry = entries[i];
1382 const char* vis_icon =
1386 std::optional<gui::StyleColorGuard> dim_guard;
1387 if (!entry.visible) {
1388 ImVec4 dimmed = GetStyleColorVec4(ImGuiCol_Text);
1390 dim_guard.emplace(ImGuiCol_Text, dimmed);
1394 absl::StrFormat(
"%s %s %s", vis_icon, entry.icon.c_str(),
1395 entry.display_name.c_str());
1399 is_touch ? std::max(GetTextLineHeightWithSpacing(), 44.0f) : 0.0f;
1401 PushID(entry.card_id.c_str());
1402 if (Selectable(label.c_str(), is_selected, ImGuiSelectableFlags_None,
1403 ImVec2(0, item_h))) {
1409 SameLine(GetContentRegionAvail().x - 80);
1410 TextDisabled(
"%s", entry.category.c_str());
1418 if (is_selected && enter_pressed) {
1425 if (is_selected && (IsKeyPressed(ImGuiKey_DownArrow) ||
1426 IsKeyPressed(ImGuiKey_UpArrow))) {
1439 if (!show || ImGui::IsKeyPressed(ImGuiKey_Escape)) {
1450 using namespace ImGui;
1451 const ImGuiViewport* viewport = GetMainViewport();
1452 SetNextWindowPos(viewport->GetCenter(), ImGuiCond_Appearing,
1453 ImVec2(0.5f, 0.5f));
1456 ImVec2(viewport->WorkSize.x * 0.95f, viewport->WorkSize.y * 0.80f),
1457 ImGuiCond_Appearing);
1459 SetNextWindowSize(ImVec2(720, 520), ImGuiCond_Appearing);
1463 if (Begin(absl::StrFormat(
"%s Keyboard Shortcuts",
ICON_MD_KEYBOARD).c_str(),
1464 &show, ImGuiWindowFlags_NoCollapse)) {
1465 if (IsWindowAppearing()) {
1466 SetKeyboardFocusHere();
1469 SetNextItemWidth(-180.0f);
1471 "##shortcuts_query",
1472 ICON_MD_SEARCH " Filter by name, keys, or group (File, Drawers, …)",
1485 std::transform(query.begin(), query.end(), query.begin(), ::tolower);
1487 std::map<std::string, std::vector<const Shortcut*>> grouped;
1488 static const char* kGroupOrder[] = {
"File",
"Edit",
"View",
"Drawers",
1489 "Windows",
"Tools",
"Layout",
"Editor",
1495 if (!query.empty()) {
1497 absl::AsciiStrToLower(absl::StrCat(name,
" ", keys,
" ", group));
1498 if (hay.find(query) == std::string::npos) {
1502 grouped[group].push_back(&shortcut);
1505 BeginChild(
"##ShortcutsBrowserList");
1506 for (
const char* group_name : kGroupOrder) {
1507 auto it = grouped.find(group_name);
1508 if (it == grouped.end() || it->second.empty()) {
1511 if (!TreeNodeEx(group_name, ImGuiTreeNodeFlags_DefaultOpen)) {
1515 absl::StrFormat(
"##sc_%s", group_name).c_str(), 2,
1516 ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingStretchProp)) {
1517 TableSetupColumn(
"Action", ImGuiTableColumnFlags_WidthStretch, 0.65f);
1518 TableSetupColumn(
"Keys", ImGuiTableColumnFlags_WidthStretch, 0.35f);
1519 for (
const Shortcut* sc : it->second) {
1522 if (Selectable(sc->name.c_str(),
false,
1523 ImGuiSelectableFlags_SpanAllColumns)) {
1531 TextDisabled(
"%s", keys.empty() ?
"—" : keys.c_str());
1541 "%s Tip: Command Palette (Ctrl+Shift+P) also lists these actions. "
1542 "Try drawer: / window: / layout:",
1547 if (!show || IsKeyPressed(ImGuiKey_Escape)) {
1559 std::make_unique<PanelCommandsProvider>(&
window_manager_, session_id));
1561 std::make_unique<WorkflowCommandsProvider>(&
window_manager_, session_id));
1568 std::make_unique<DrawerCommandsProvider>(
1569 [drawers](
int drawer_type) {
1570 drawers->ToggleDrawer(
1573 [drawers]() { drawers->CycleToNextDrawer(); },
1574 [drawers]() { drawers->CycleToPreviousDrawer(); }));
1580 [
this](
const std::string& category) {
1590 std::make_unique<LayoutCommandsProvider>([
this](
const std::string&
id) {
1594 if (absl::StartsWith(
id,
"profile:")) {
1596 }
else if (
id ==
"session:capture") {
1598 }
else if (
id ==
"session:restore") {
1600 }
else if (
id ==
"session:clear") {
1610 std::make_unique<RecentFilesCommandsProvider>(
1611 [
this](
const std::string& filepath) {
1616 absl::StrFormat(
"Failed to open: %s", status.message()),
1625 std::make_unique<DungeonRoomCommandsProvider>(session_id));
1627 std::make_unique<OverworldMapCommandsProvider>(session_id));
1643 "Vanilla ROM Hack",
"ZSCustomOverworld v3",
"ZSCustomOverworld v2",
1644 "Randomizer Compatible"};
1645 welcome_callbacks.
remove = [
this](
const std::string& path) {
1651 absl::StrFormat(
"Removed %s from recents",
1652 std::filesystem::path(path).filename().string()),
1655 welcome_callbacks.
toggle_pin = [
this](
const std::string& path) {
1659 bool currently_pinned =
false;
1660 for (
const auto& entry : model.entries()) {
1661 if (entry.filepath == path) {
1662 currently_pinned = entry.pinned;
1666 model.SetPinned(path, !currently_pinned);
1686 [
this](
const std::string& template_name) {
1700 std::move(welcome_callbacks)));
1707 if (config_dir.ok()) {
1708 std::filesystem::path history_file = *config_dir /
"command_history.json";
1723 {.id =
"workflow.project.build",
1724 .group =
"Build & Run",
1725 .label =
"Build Project",
1726 .description =
"Run the active project's configured build command",
1729 .callback = [
this]() {
1735 {.id =
"workflow.project.run",
1736 .group =
"Build & Run",
1737 .label =
"Run Project Output",
1738 .description =
"Open the active project's configured run target in a "
1742 .callback = [
this]() {
1749#ifdef YAZE_WITH_GRPC
1753 {.id =
"workflow.mesen.connect",
1754 .group =
"Live Debugging",
1755 .label =
"Connect Mesen2",
1757 "Auto-discover and connect to the active Mesen2 backend",
1760 .callback = [
this]() {
1768 {.id =
"workflow.mesen.step_over",
1769 .group =
"Live Debugging",
1770 .label =
"Mesen2 Step Over",
1771 .description =
"Execute one instruction without entering subroutines",
1774 .callback = [emu_backend]() { emu_backend->StepOver(); }});
1776 {.id =
"workflow.mesen.step_out",
1777 .group =
"Live Debugging",
1778 .label =
"Mesen2 Step Out",
1779 .description =
"Run until return from the current subroutine",
1780 .shortcut =
"Shift+F11",
1782 .callback = [emu_backend]() { emu_backend->StepOut(); }});
1784 {.id =
"workflow.mesen.overlay_on",
1785 .group =
"Live Debugging",
1786 .label =
"Enable Collision Overlay",
1787 .description =
"Show collision overlays in the active Mesen2 backend",
1790 .callback = [emu_backend]() {
1791 emu_backend->SetCollisionOverlay(
true);
1794 {.id =
"workflow.mesen.overlay_off",
1795 .group =
"Live Debugging",
1796 .label =
"Disable Collision Overlay",
1797 .description =
"Hide collision overlays in the active Mesen2 backend",
1800 .callback = [emu_backend]() {
1801 emu_backend->SetCollisionOverlay(
false);
1815 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(),
1816 ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
1817 ImGui::SetNextWindowSize(ImVec2(800, 600), ImGuiCond_FirstUseEver);
1819 bool show_search =
true;
1822 &show_search, ImGuiWindowFlags_NoCollapse)) {
1824 ImGui::SetNextItemWidth(-100);
1825 if (ImGui::IsWindowAppearing()) {
1826 ImGui::SetKeyboardFocusHere();
1829 bool input_changed = ImGui::InputTextWithHint(
1831 absl::StrFormat(
"%s Search everything...",
ICON_MD_SEARCH).c_str(),
1835 if (ImGui::Button(absl::StrFormat(
"%s Clear",
ICON_MD_CLEAR).c_str())) {
1837 input_changed =
true;
1845 if (ImGui::BeginTabItem(
1848 auto recent_files = manager.GetRecentFiles();
1850 if (ImGui::BeginTable(
"RecentFilesTable", 3,
1851 ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg |
1852 ImGuiTableFlags_SizingStretchProp)) {
1853 ImGui::TableSetupColumn(
"File", ImGuiTableColumnFlags_WidthStretch,
1855 ImGui::TableSetupColumn(
"Type", ImGuiTableColumnFlags_WidthFixed,
1857 ImGui::TableSetupColumn(
"Action", ImGuiTableColumnFlags_WidthFixed,
1859 ImGui::TableHeadersRow();
1861 for (
const auto& file : recent_files) {
1866 ImGui::TableNextRow();
1867 ImGui::TableNextColumn();
1870 ImGui::TableNextColumn();
1872 if (ext ==
"sfc" || ext ==
"smc") {
1873 ImGui::TextColored(ImVec4(0.2f, 0.8f, 0.2f, 1.0f), tr(
"%s ROM"),
1875 }
else if (ext ==
"yaze") {
1876 ImGui::TextColored(ImVec4(0.2f, 0.6f, 0.8f, 1.0f),
1882 ImGui::TableNextColumn();
1883 ImGui::PushID(file.c_str());
1884 if (ImGui::Button(tr(
"Open"))) {
1888 absl::StrCat(
"Failed to open: ", status.message()),
1898 ImGui::EndTabItem();
1903 if (current_rom && current_rom->resource_label()) {
1904 if (ImGui::BeginTabItem(
1908 if (ImGui::BeginTable(
"LabelsTable", 3,
1909 ImGuiTableFlags_ScrollY |
1910 ImGuiTableFlags_RowBg |
1911 ImGuiTableFlags_SizingStretchProp)) {
1912 ImGui::TableSetupColumn(
"Type", ImGuiTableColumnFlags_WidthFixed,
1914 ImGui::TableSetupColumn(
"Label", ImGuiTableColumnFlags_WidthStretch,
1916 ImGui::TableSetupColumn(
"Value", ImGuiTableColumnFlags_WidthStretch,
1918 ImGui::TableHeadersRow();
1920 for (
const auto& type_pair : labels) {
1921 for (
const auto& kv : type_pair.second) {
1927 ImGui::TableNextRow();
1928 ImGui::TableNextColumn();
1929 ImGui::Text(
"%s", type_pair.first.c_str());
1931 ImGui::TableNextColumn();
1932 if (ImGui::Selectable(kv.first.c_str(),
false,
1933 ImGuiSelectableFlags_SpanAllColumns)) {
1937 ImGui::TableNextColumn();
1938 ImGui::TextDisabled(
"%s", kv.second.c_str());
1944 ImGui::EndTabItem();
1950 if (ImGui::BeginTabItem(
1951 absl::StrFormat(
"%s Sessions",
ICON_MD_TAB).c_str())) {
1952 ImGui::Text(tr(
"Search and switch between active sessions:"));
1956 std::string session_info =
1958 if (session_info ==
"[CLOSED SESSION]")
1967 std::optional<gui::StyleColorGuard> current_guard;
1969 current_guard.emplace(ImGuiCol_Text,
1970 ImVec4(0.2f, 0.8f, 0.2f, 1.0f));
1973 if (ImGui::Selectable(absl::StrFormat(
"%s %s %s",
ICON_MD_TAB,
1974 session_info.c_str(),
1975 is_current ?
"(Current)" :
"")
1983 ImGui::EndTabItem();
1992 ImGui::Text(tr(
"%s Global search across all YAZE data"),
ICON_MD_INFO);
2018 const char* surface_names[] = {
"Welcome",
"Dashboard",
"Editor"};
2019 LOG_INFO(
"UICoordinator",
"Startup surface: %s -> %s",
2020 surface_names[
static_cast<int>(old_surface)],
2021 surface_names[
static_cast<int>(surface)]);
2091 if (!current_rom || !current_rom->is_loaded()) {
static Application & Instance()
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
project::ResourceLabelManager * resource_label()
void SaveHistory(const std::string &filepath)
Save command usage history to disk.
void Clear()
Clear all commands (and forget every registered provider). Usage history is kept so refreshing provid...
void LoadHistory(const std::string &filepath)
Load command usage history from disk.
void RegisterProvider(std::unique_ptr< CommandProvider > provider)
std::vector< CommandEntry > GetRecentCommands(int limit=10)
bool RecordUsage(const std::string &name)
std::vector< CommandMatch > Search(const std::string &query, int64_t now_ms) const
std::vector< CommandEntry > GetFrequentCommands(int limit=10)
static int FuzzyScore(const std::string &text, const std::string &query)
Legacy name for ScoreText (kept for the Window Finder).
uint64_t generation() const
The EditorManager controls the main editor window and manages the various editor classes.
void SaveWorkspacePreset(const std::string &name)
float GetRightLayoutOffset() const
void SwitchToEditor(EditorType editor_type, bool force_visible=false, bool from_dialog=false) override
void QueueBuildCurrentProject()
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 RunCurrentProject()
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 RefreshWorkspacePresets()
void ApplyLayoutPreset(const std::string &preset_name)
void RestoreTemporaryLayoutSnapshot(bool clear_after_restore=false)
UserSettings & user_settings()
bool ApplyLayoutProfile(const std::string &profile_id)
absl::Status LoadRom()
Load a ROM file into a new or existing session.
FeatureHintManager * feature_hint_manager()
void ClearTemporaryLayoutSnapshot()
project::YazeProject * GetCurrentProject()
bool HasOpenEditor() const
absl::Status OpenRomOrProject(const std::string &filename)
void CaptureTemporaryLayoutSnapshot()
float GetLeftLayoutOffset() const
Manages editor types, categories, and lifecycle.
static EditorType GetEditorTypeFromCategory(const std::string &category)
void Dismiss(std::string_view id)
Mark dismissed without showing (e.g. user opened the guide manually).
static std::vector< std::string > GetDefaultWindows(EditorType type)
void Open(const std::string &initial_template="")
void SetCreateCallback(CreateCallback cb)
Handles all project file operations with ROM-first workflow.
bool DrawSidebarToggleButton()
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
static float GetSidebarToggleWidth()
Handles all ROM file I/O operations.
High-level orchestrator for multi-session UI.
void * GetSession(size_t index) const
bool IsSessionSwitcherVisible() const
size_t GetActiveSessionIndex() const
Compact zero-based UI position in sessions_.
size_t GetActiveSessionCount() const
void HideSessionSwitcher()
std::string GetSessionDisplayName(size_t index) const
void ShowSessionSwitcher()
size_t GetTotalSessionCount() const
bool IsSessionClosed(size_t index) const
size_t GetActiveSessionId() const
Stable workspace identity that is never reused while this coordinator lives.
bool HasMultipleSessions() const
const std::unordered_map< std::string, Shortcut > & GetShortcuts() const
size_t GetUnreadCount() const
void Show(const std::string &message, ToastType type=ToastType::kInfo, float ttl_seconds=3.0f, std::string action_label={}, std::function< void()> on_action=nullptr)
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)
bool show_save_workspace_preset_
bool command_history_loaded_
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)
char global_search_query_[256]
void SetWelcomeScreenManuallyClosed(bool closed)
bool IsEmulatorVisible() const
bool show_proposal_drawer_
void ShowSessionSwitcher()
ToastManager & toast_manager_
void DrawWorkspacePresetDialogs()
void SaveCommandPaletteHistory()
bool command_palette_refocus_input_
bool IsAsmEditorVisible() const
void SetEmulatorVisible(bool visible)
void ShowCommandPalette(const char *initial_query=nullptr)
char command_palette_query_[256]
bool ShouldShowActivityBar() const
void DrawNotificationBell(bool show_dirty, bool has_dirty_rom, bool show_session, bool has_multiple_sessions)
bool show_command_palette_
bool IsPanelSidebarVisible() const
void SetAsmEditorVisible(bool visible)
void SetWelcomeScreenVisible(bool visible)
bool command_palette_scroll_to_selected_
WindowDelegate & window_delegate_
bool DrawMenuBarIconButton(const char *icon, const char *tooltip, bool is_active=false)
bool ShouldShowDashboard() const
std::string command_palette_cached_query_
void ShowPopup(const std::string &popup_name)
void TogglePanelSidebar()
bool IsCompactLayout() const
std::vector< CommandMatch > command_palette_cached_matches_
StartupVisibility welcome_behavior_override_
int panel_finder_selected_idx_
char panel_finder_query_[256]
bool welcome_screen_manually_closed_
uint64_t command_palette_cached_generation_
void ExecutePaletteCommand(const std::string &name, std::function< void()> callback)
char shortcuts_browser_query_[256]
StartupVisibility dashboard_behavior_override_
void RefreshWorkflowActions()
StartupSurface current_startup_surface_
bool command_palette_initialized_
PopupManager & popup_manager_
void DrawWindowManagementUI()
void PositionWindow(const std::string &window_name, float x, float y)
void SetWindowSize(const std::string &window_name, float width, float height)
void ShowDisplaySettings()
bool show_shortcuts_browser_
NewProjectDialog new_project_dialog_
void SetWelcomeScreenBehavior(StartupVisibility mode)
bool show_editor_selection_
bool show_welcome_screen_
WorkspaceWindowManager & window_manager_
EditorManager * editor_manager_
void DrawShortcutsBrowser()
void SetDashboardBehavior(StartupVisibility mode)
bool IsSessionSwitcherVisible() const
void SetCommandPaletteVisible(bool visible)
CommandPalette command_palette_
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)
bool show_load_workspace_preset_
void DrawMenuBarRestoreButton()
int command_palette_selected_idx_
bool ShouldShowWelcome() const
void DrawCommandPalette()
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)
size_t GetActiveSessionId() const
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
void ToggleSidebarVisibility()
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
bool IsSidebarVisible() 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)
static bool IsTouchDevice()
RAII guard for ImGui style colors.
RAII guard for ImGui style vars.
const Theme & GetCurrentTheme() const
static ThemeManager & Get()
static RecentFilesManager & GetInstance()
#define ICON_MD_NOTIFICATIONS
#define ICON_MD_FULLSCREEN_EXIT
#define ICON_MD_EXPAND_LESS
#define ICON_MD_VIDEOGAME_ASSET
#define ICON_MD_VISIBILITY
#define ICON_MD_MANAGE_SEARCH
#define ICON_MD_VISIBILITY_OFF
#define ICON_MD_DESCRIPTION
#define ICON_MD_DASHBOARD
#define ICON_MD_FIBER_MANUAL_RECORD
#define LOG_ERROR(category, format,...)
#define LOG_INFO(category, format,...)
void Register(const ActionDef &action)
constexpr const char * kZScreamWelcome
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, …).
CommandEntry BuildGotoEntry(const GotoQuery &query, size_t session_id)
std::string PrintShortcut(const std::vector< ImGuiKey > &keys)
std::optional< GotoQuery > ParseGotoQuery(std::string_view query)
ImVec4 ConvertColorToImVec4(const Color &color)
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.
ImVec4 GetTextSecondaryVec4()
void ColoredTextF(const ImVec4 &color, const char *fmt,...)
ImVec4 GetSurfaceContainerHighVec4()
constexpr ImVec2 kDefaultModalSize
ImVec4 GetOnSurfaceVariantVec4()
ImVec4 GetSurfaceContainerVec4()
std::string GetFileName(const std::string &filename)
Gets the filename from a full path.
std::string GetFileExtension(const std::string &filename)
Gets the file extension from a filename.
StartupVisibility
Tri-state toggle used for startup UI visibility controls.
std::function< void()> callback
Represents a single session, containing a ROM and its associated editors.
std::function< void()> clear_recents
std::function< void(const std::string &) toggle_pin)
std::vector< std::string > template_names
std::function< void()> undo_remove
std::function< void(const std::string &) remove)
std::function< void()> dismiss_welcome
std::function< void()> show_welcome
std::function< void(const std::string &) create_from_template)
const RecentProjectsModel * model
std::unordered_map< std::string, std::unordered_map< std::string, std::string > > labels_
bool project_opened() const