11#include "absl/strings/match.h"
12#include "absl/strings/str_format.h"
15#include <emscripten.h>
42#include "imgui/imgui.h"
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) {
81 var romInput = document.getElementById(
'rom-input');
93 absl::StrFormat(
"Failed to load ROM: %s", status.message()),
96#if !(defined(__APPLE__) && TARGET_OS_IOS == 1)
110 [
this](
const std::string& template_name,
const std::string& rom_path,
111 const std::string& project_name) -> absl::Status {
113 return absl::FailedPreconditionError(
114 "Editor manager is not available");
117 template_name, rom_path, project_name);
120 absl::StrFormat(
"Failed to create project: %s", status.message()),
125 project_name, template_name),
128 return absl::OkStatus();
131 welcome_screen_->SetOpenProjectCallback([
this](
const std::string& filepath) {
136 absl::StrFormat(
"Failed to open project: %s", status.message()),
206 if (!ImGui::GetCurrentContext()) {
210 const ImGuiViewport* viewport = ImGui::GetMainViewport();
215 ImDrawList* bg_draw_list =
216 ImGui::GetBackgroundDrawList(
const_cast<ImGuiViewport*
>(viewport));
219 auto current_theme = theme_manager.GetCurrentTheme();
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);
247#ifdef YAZE_BUILD_AGENT_UI
278 const ImGuiViewport* viewport = ImGui::GetMainViewport();
282 const float width = viewport->WorkSize.x;
283#if defined(__APPLE__) && TARGET_OS_IOS == 1
286 static bool compact_mode =
false;
287 constexpr float kEnterCompactWidth = 900.0f;
288 constexpr float kExitCompactWidth = 940.0f;
290 compact_mode ? (width < kExitCompactWidth) : (width < kEnterCompactWidth);
293 return width < 900.0f;
305 {{ImGuiCol_Button, ImVec4(0, 0, 0, 0)},
311 bool clicked = ImGui::SmallButton(icon);
313 if (tooltip && ImGui::IsItemHovered()) {
314 ImGui::SetTooltip(
"%s", tooltip);
322 const float frame_padding = ImGui::GetStyle().FramePadding.x;
325 return icon_width + frame_padding * 2.0f;
338 const float item_spacing = 6.0f;
339 const float padding = 8.0f;
341 auto CalcSmallButtonWidth = [](
const char* label) ->
float {
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;
349 const ImGuiViewport* viewport = ImGui::GetMainViewport();
350 const float true_viewport_right = viewport->WorkPos.x + viewport->WorkSize.x;
352 const bool has_panel_toggles =
354 float panel_buttons_width = 0.0f;
355 if (has_panel_toggles) {
361 float panel_region_width = panel_buttons_width;
364 panel_region_width +=
369 float panel_screen_x = true_viewport_right - panel_region_width;
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;
382 float panel_local_x = panel_screen_x - window_screen_x;
384 std::min(window_width - padding, panel_local_x - item_spacing);
388 current_rom && current_rom->is_loaded() && current_rom->dirty();
395 const float available_width = region_end - menu_items_end - padding;
404 has_multiple_sessions &&
405 (required_width + session_width + item_spacing) <= available_width;
407 required_width += session_width + item_spacing;
412 has_dirty_rom && (required_width + dirty_width) <= available_width;
414 required_width += dirty_width;
418 float start_pos = std::max(menu_items_end, region_end - required_width);
423 ImGui::SameLine(start_pos);
425 ImVec2(item_spacing, 0.0f));
432 if (ImGui::IsItemHovered()) {
433 ImGui::SetTooltip(tr(
"Unsaved changes: %s"),
434 current_rom->short_name().c_str());
447 has_multiple_sessions);
452 if (has_panel_toggles) {
453 float menu_bar_y = ImGui::GetCursorScreenPos().y;
454 ImGui::SetCursorScreenPos(ImVec2(panel_screen_x, menu_bar_y));
462 "Hide menu bar (Alt to restore)")) {
475 ImGuiWindowFlags flags =
476 ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize |
477 ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar |
478 ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_AlwaysAutoResize |
479 ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoSavedSettings;
481 ImGui::SetNextWindowPos(ImVec2(8, 8));
482 ImGui::SetNextWindowBgAlpha(0.7f);
484 if (ImGui::Begin(
"##MenuBarRestore",
nullptr, flags)) {
495 if (ImGui::IsItemHovered()) {
496 ImGui::SetTooltip(tr(
"Show menu bar (Alt)"));
502 if (ImGui::IsKeyPressed(ImGuiKey_LeftAlt) ||
503 ImGui::IsKeyPressed(ImGuiKey_RightAlt)) {
510 bool has_multiple_sessions) {
520 unread > 0 || is_active)) {
528 if (ImGui::IsItemHovered()) {
529 ImGui::BeginTooltip();
540 ImGui::TextDisabled(tr(
"Click to open Notifications panel"));
543 if (!show_dirty && has_dirty_rom) {
548 current_rom->short_name().c_str());
551 if (!show_session && has_multiple_sessions) {
552 if (!show_dirty && has_dirty_rom) {
570 ImVec2 button_min = ImGui::GetCursorScreenPos();
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)",
581 ImGui::OpenPopup(
"##SessionSwitcherPopup");
584 ImVec2 button_max = ImGui::GetItemRectMax();
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);
592 ImGui::SetNextWindowPos(ImVec2(popup_x, button_max.y + 2.0f),
593 ImGuiCond_Appearing);
596 if (ImGui::BeginPopup(
"##SessionSwitcherPopup")) {
609 Rom* rom = &session->rom;
610 ImGui::PushID(
static_cast<int>(i));
612 bool is_current = (rom == current_rom);
613 std::optional<gui::StyleColorGuard> current_guard;
624 if (ImGui::Selectable(label.c_str(), is_current)) {
664 if (initial_query !=
nullptr) {
688 auto emulator_windows =
690 for (
const auto& window : emulator_windows) {
691 if (window.visibility_flag && *window.visibility_flag) {
701 auto default_windows =
703 for (
const auto& window_id : default_windows) {
718 for (
const auto& window : windows) {
719 if (window.visibility_flag && *window.visibility_flag) {
729 auto default_windows =
731 for (
const auto& window_id : default_windows) {
760 "EditorManager is null - cannot check ROM state");
765 LOG_ERROR(
"UICoordinator",
"WelcomeScreen object is null - cannot render");
771 bool rom_is_loaded = current_rom && current_rom->
is_loaded();
835 ImGuiWindowFlags_AlwaysAutoResize);
836 static char preset_name[128] =
"";
837 ImGui::InputText(tr(
"Name"), preset_name, IM_ARRAYSIZE(preset_name));
839 if (strlen(preset_name) > 0) {
843 preset_name[0] =
'\0';
849 preset_name[0] =
'\0';
856 ImGuiWindowFlags_AlwaysAutoResize);
862 for (
const auto& name : workspace_manager->workspace_presets()) {
863 if (ImGui::Selectable(name.c_str())) {
869 if (workspace_manager->workspace_presets().empty())
870 ImGui::Text(tr(
"No presets found"));
926 const std::string& icon,
928 std::function<
void()> callback,
930 std::optional<gui::StyleColorGuard> disabled_guard;
932 disabled_guard.emplace(std::initializer_list<gui::StyleColorGuard::Entry>{
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) {
948 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(),
949 ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
954 ImGui::SetNextWindowPos(ImVec2(x, y), ImGuiCond_Appearing);
959 ImGui::SetNextWindowSize(ImVec2(width, height), ImGuiCond_FirstUseEver);
971 using namespace ImGui;
974 SetNextWindowPos(GetMainViewport()->GetCenter(), ImGuiCond_Appearing,
976 SetNextWindowSize(ImVec2(800, 600), ImGuiCond_FirstUseEver);
978 bool show_palette =
true;
979 if (Begin(absl::StrFormat(
"%s Command Palette",
ICON_MD_SEARCH).c_str(),
980 &show_palette, ImGuiWindowFlags_NoCollapse)) {
982 SetNextItemWidth(-100);
983 if (IsWindowAppearing()) {
984 SetKeyboardFocusHere();
988 bool input_changed = InputTextWithHint(
991 "%s Search… try drawer: window: layout: (or shortcut names)",
997 if (Button(absl::StrFormat(
"%s Clear",
ICON_MD_CLEAR).c_str())) {
999 input_changed =
true;
1006 struct ScoredCommand {
1009 std::string category;
1010 std::string shortcut;
1011 std::function<void()> callback;
1013 std::vector<ScoredCommand> scored_commands;
1016 std::transform(query_lower.begin(), query_lower.end(), query_lower.begin(),
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(),
1024 if (query_lower.empty())
1026 if (text_lower.find(query_lower) == 0)
1028 if (text_lower.find(query_lower) != std::string::npos)
1032 size_t text_idx = 0, query_idx = 0;
1034 while (text_idx < text_lower.length() &&
1035 query_idx < query_lower.length()) {
1036 if (text_lower[text_idx] == query_lower[query_idx]) {
1042 return (query_idx == query_lower.length()) ? score : 0;
1047 int score = score_text(name);
1049 std::string shortcut_text =
1050 shortcut.keys.empty()
1052 : absl::StrFormat(
"(%s)",
PrintShortcut(shortcut.keys).c_str());
1053 scored_commands.push_back(
1054 {score, name,
"Shortcuts", shortcut_text, shortcut.callback});
1060 int score = score_text(entry.name);
1062 score += score_text(entry.category) / 2;
1063 score += score_text(entry.description) / 4;
1066 scored_commands.push_back({score, entry.name, entry.category,
1067 entry.shortcut, entry.callback});
1072 std::sort(scored_commands.begin(), scored_commands.end(),
1073 [](
const auto& a,
const auto& b) { return a.score > b.score; });
1078 absl::StrFormat(
"%s All Commands",
ICON_MD_LIST).c_str())) {
1080 "CommandPaletteTable", 4,
1081 ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg |
1082 ImGuiTableFlags_SizingStretchProp,
1084 TableSetupColumn(
"Command", ImGuiTableColumnFlags_WidthStretch,
1086 TableSetupColumn(
"Category", ImGuiTableColumnFlags_WidthStretch,
1088 TableSetupColumn(
"Shortcut", ImGuiTableColumnFlags_WidthStretch,
1090 TableSetupColumn(
"Score", ImGuiTableColumnFlags_WidthStretch, 0.15f);
1093 for (
size_t i = 0; i < scored_commands.size(); ++i) {
1094 const auto& cmd = scored_commands[i];
1099 PushID(
static_cast<int>(i));
1102 if (Selectable(cmd.name.c_str(), is_selected,
1103 ImGuiSelectableFlags_SpanAllColumns)) {
1132 if (BeginTabItem(absl::StrFormat(
"%s Recent",
ICON_MD_HISTORY).c_str())) {
1134 if (recent.empty()) {
1135 Text(tr(
"No recent commands yet."));
1137 for (
const auto& entry : recent) {
1138 if (Selectable(entry.name.c_str())) {
1139 if (entry.callback) {
1150 if (BeginTabItem(absl::StrFormat(
"%s Frequent",
ICON_MD_STAR).c_str())) {
1152 if (frequent.empty()) {
1153 Text(tr(
"No frequently used commands yet."));
1155 for (
const auto& entry : frequent) {
1156 if (Selectable(absl::StrFormat(
"%s (%d uses)", entry.name,
1159 if (entry.callback) {
1175 Text(tr(
"%s %zu commands | Prefixes: drawer: window: layout:"),
1184 if (!show_palette) {
1188 if (config_dir.ok()) {
1189 std::filesystem::path history_file = *config_dir /
"command_history.json";
1199 using namespace ImGui;
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));
1210 SetNextWindowPos(viewport->GetCenter(), ImGuiCond_Appearing,
1211 ImVec2(0.5f, 0.3f));
1214 ImVec2(viewport->WorkSize.x * 0.95f, viewport->WorkSize.y * 0.70f),
1215 ImGuiCond_Appearing);
1217 SetNextWindowSize(ImVec2(600, 420), ImGuiCond_Appearing);
1220 const ImGuiWindowFlags finder_flags =
1221 ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove |
1222 ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoDocking |
1223 ImGuiWindowFlags_NoSavedSettings;
1228 if (IsWindowAppearing()) {
1229 SetKeyboardFocusHere();
1233 SetNextItemWidth(-1);
1234 bool input_changed = InputTextWithHint(
1238 if (input_changed) {
1245 struct WindowEntry {
1246 std::string card_id;
1247 std::string display_name;
1249 std::string category;
1254 std::vector<WindowEntry> entries;
1259 for (
const auto& card_id :
1267 if (!query.empty()) {
1276 entries.push_back({card_id, desc->display_name, desc->icon,
1277 desc->category, vis, pin, score});
1281 if (query.empty()) {
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);
1291 return lhs_t > rhs_t;
1292 return lhs.display_name < rhs.display_name;
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;
1307 if (IsKeyPressed(ImGuiKey_DownArrow) &&
1314 bool enter_pressed = IsKeyPressed(ImGuiKey_Enter);
1319 PushStyleVar(ImGuiStyleVar_ItemSpacing,
1320 ImVec2(GetStyle().ItemSpacing.x, 10.0f));
1324 BeginChild(
"##PanelFinderList");
1325 for (
int i = 0; i < static_cast<int>(entries.size()); ++i) {
1326 const auto& entry = entries[i];
1330 const char* vis_icon =
1334 std::optional<gui::StyleColorGuard> dim_guard;
1335 if (!entry.visible) {
1336 ImVec4 dimmed = GetStyleColorVec4(ImGuiCol_Text);
1338 dim_guard.emplace(ImGuiCol_Text, dimmed);
1342 absl::StrFormat(
"%s %s %s", vis_icon, entry.icon.c_str(),
1343 entry.display_name.c_str());
1347 is_touch ? std::max(GetTextLineHeightWithSpacing(), 44.0f) : 0.0f;
1349 PushID(entry.card_id.c_str());
1350 if (Selectable(label.c_str(), is_selected, ImGuiSelectableFlags_None,
1351 ImVec2(0, item_h))) {
1357 SameLine(GetContentRegionAvail().x - 80);
1358 TextDisabled(
"%s", entry.category.c_str());
1366 if (is_selected && enter_pressed) {
1373 if (is_selected && (IsKeyPressed(ImGuiKey_DownArrow) ||
1374 IsKeyPressed(ImGuiKey_UpArrow))) {
1387 if (!show || ImGui::IsKeyPressed(ImGuiKey_Escape)) {
1398 using namespace ImGui;
1399 const ImGuiViewport* viewport = GetMainViewport();
1400 SetNextWindowPos(viewport->GetCenter(), ImGuiCond_Appearing,
1401 ImVec2(0.5f, 0.5f));
1404 ImVec2(viewport->WorkSize.x * 0.95f, viewport->WorkSize.y * 0.80f),
1405 ImGuiCond_Appearing);
1407 SetNextWindowSize(ImVec2(720, 520), ImGuiCond_Appearing);
1411 if (Begin(absl::StrFormat(
"%s Keyboard Shortcuts",
ICON_MD_KEYBOARD).c_str(),
1412 &show, ImGuiWindowFlags_NoCollapse)) {
1413 if (IsWindowAppearing()) {
1414 SetKeyboardFocusHere();
1417 SetNextItemWidth(-180.0f);
1419 "##shortcuts_query",
1420 ICON_MD_SEARCH " Filter by name, keys, or group (File, Drawers, …)",
1433 std::transform(query.begin(), query.end(), query.begin(), ::tolower);
1435 std::map<std::string, std::vector<const Shortcut*>> grouped;
1436 static const char* kGroupOrder[] = {
"File",
"Edit",
"View",
"Drawers",
1437 "Windows",
"Tools",
"Layout",
"Editor",
1443 if (!query.empty()) {
1445 absl::AsciiStrToLower(absl::StrCat(name,
" ", keys,
" ", group));
1446 if (hay.find(query) == std::string::npos) {
1450 grouped[group].push_back(&shortcut);
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()) {
1459 if (!TreeNodeEx(group_name, ImGuiTreeNodeFlags_DefaultOpen)) {
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) {
1470 if (Selectable(sc->name.c_str(),
false,
1471 ImGuiSelectableFlags_SpanAllColumns)) {
1479 TextDisabled(
"%s", keys.empty() ?
"—" : keys.c_str());
1489 "%s Tip: Command Palette (Ctrl+Shift+P) also lists these actions. "
1490 "Try drawer: / window: / layout:",
1495 if (!show || IsKeyPressed(ImGuiKey_Escape)) {
1507 std::make_unique<PanelCommandsProvider>(&
window_manager_, session_id));
1509 std::make_unique<WorkflowCommandsProvider>(&
window_manager_, session_id));
1516 std::make_unique<DrawerCommandsProvider>(
1517 [drawers](
int drawer_type) {
1518 drawers->ToggleDrawer(
1521 [drawers]() { drawers->CycleToNextDrawer(); },
1522 [drawers]() { drawers->CycleToPreviousDrawer(); }));
1528 [
this](
const std::string& category) {
1538 std::make_unique<LayoutCommandsProvider>([
this](
const std::string&
id) {
1542 if (absl::StartsWith(
id,
"profile:")) {
1544 }
else if (
id ==
"session:capture") {
1546 }
else if (
id ==
"session:restore") {
1548 }
else if (
id ==
"session:clear") {
1558 std::make_unique<RecentFilesCommandsProvider>(
1559 [
this](
const std::string& filepath) {
1564 absl::StrFormat(
"Failed to open: %s", status.message()),
1572 std::make_unique<DungeonRoomCommandsProvider>(session_id));
1583 "Vanilla ROM Hack",
"ZSCustomOverworld v3",
"ZSCustomOverworld v2",
1584 "Randomizer Compatible"};
1585 welcome_callbacks.
remove = [
this](
const std::string& path) {
1591 absl::StrFormat(
"Removed %s from recents",
1592 std::filesystem::path(path).filename().string()),
1595 welcome_callbacks.
toggle_pin = [
this](
const std::string& path) {
1599 bool currently_pinned =
false;
1600 for (
const auto& entry : model.entries()) {
1601 if (entry.filepath == path) {
1602 currently_pinned = entry.pinned;
1606 model.SetPinned(path, !currently_pinned);
1626 [
this](
const std::string& template_name) {
1640 std::move(welcome_callbacks)));
1645 if (config_dir.ok()) {
1646 std::filesystem::path history_file = *config_dir /
"command_history.json";
1659 {.id =
"workflow.project.build",
1660 .group =
"Build & Run",
1661 .label =
"Build Project",
1662 .description =
"Run the active project's configured build command",
1665 .callback = [
this]() {
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 "
1678 .callback = [
this]() {
1685#ifdef YAZE_WITH_GRPC
1689 {.id =
"workflow.mesen.connect",
1690 .group =
"Live Debugging",
1691 .label =
"Connect Mesen2",
1693 "Auto-discover and connect to the active Mesen2 backend",
1696 .callback = [
this]() {
1704 {.id =
"workflow.mesen.step_over",
1705 .group =
"Live Debugging",
1706 .label =
"Mesen2 Step Over",
1707 .description =
"Execute one instruction without entering subroutines",
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",
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",
1726 .callback = [emu_backend]() {
1727 emu_backend->SetCollisionOverlay(
true);
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",
1736 .callback = [emu_backend]() {
1737 emu_backend->SetCollisionOverlay(
false);
1751 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(),
1752 ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
1753 ImGui::SetNextWindowSize(ImVec2(800, 600), ImGuiCond_FirstUseEver);
1755 bool show_search =
true;
1758 &show_search, ImGuiWindowFlags_NoCollapse)) {
1760 ImGui::SetNextItemWidth(-100);
1761 if (ImGui::IsWindowAppearing()) {
1762 ImGui::SetKeyboardFocusHere();
1765 bool input_changed = ImGui::InputTextWithHint(
1767 absl::StrFormat(
"%s Search everything...",
ICON_MD_SEARCH).c_str(),
1771 if (ImGui::Button(absl::StrFormat(
"%s Clear",
ICON_MD_CLEAR).c_str())) {
1773 input_changed =
true;
1781 if (ImGui::BeginTabItem(
1784 auto recent_files = manager.GetRecentFiles();
1786 if (ImGui::BeginTable(
"RecentFilesTable", 3,
1787 ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg |
1788 ImGuiTableFlags_SizingStretchProp)) {
1789 ImGui::TableSetupColumn(
"File", ImGuiTableColumnFlags_WidthStretch,
1791 ImGui::TableSetupColumn(
"Type", ImGuiTableColumnFlags_WidthFixed,
1793 ImGui::TableSetupColumn(
"Action", ImGuiTableColumnFlags_WidthFixed,
1795 ImGui::TableHeadersRow();
1797 for (
const auto& file : recent_files) {
1802 ImGui::TableNextRow();
1803 ImGui::TableNextColumn();
1806 ImGui::TableNextColumn();
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),
1818 ImGui::TableNextColumn();
1819 ImGui::PushID(file.c_str());
1820 if (ImGui::Button(tr(
"Open"))) {
1824 absl::StrCat(
"Failed to open: ", status.message()),
1834 ImGui::EndTabItem();
1839 if (current_rom && current_rom->resource_label()) {
1840 if (ImGui::BeginTabItem(
1844 if (ImGui::BeginTable(
"LabelsTable", 3,
1845 ImGuiTableFlags_ScrollY |
1846 ImGuiTableFlags_RowBg |
1847 ImGuiTableFlags_SizingStretchProp)) {
1848 ImGui::TableSetupColumn(
"Type", ImGuiTableColumnFlags_WidthFixed,
1850 ImGui::TableSetupColumn(
"Label", ImGuiTableColumnFlags_WidthStretch,
1852 ImGui::TableSetupColumn(
"Value", ImGuiTableColumnFlags_WidthStretch,
1854 ImGui::TableHeadersRow();
1856 for (
const auto& type_pair : labels) {
1857 for (
const auto& kv : type_pair.second) {
1863 ImGui::TableNextRow();
1864 ImGui::TableNextColumn();
1865 ImGui::Text(
"%s", type_pair.first.c_str());
1867 ImGui::TableNextColumn();
1868 if (ImGui::Selectable(kv.first.c_str(),
false,
1869 ImGuiSelectableFlags_SpanAllColumns)) {
1873 ImGui::TableNextColumn();
1874 ImGui::TextDisabled(
"%s", kv.second.c_str());
1880 ImGui::EndTabItem();
1886 if (ImGui::BeginTabItem(
1887 absl::StrFormat(
"%s Sessions",
ICON_MD_TAB).c_str())) {
1888 ImGui::Text(tr(
"Search and switch between active sessions:"));
1892 std::string session_info =
1894 if (session_info ==
"[CLOSED SESSION]")
1903 std::optional<gui::StyleColorGuard> current_guard;
1905 current_guard.emplace(ImGuiCol_Text,
1906 ImVec4(0.2f, 0.8f, 0.2f, 1.0f));
1909 if (ImGui::Selectable(absl::StrFormat(
"%s %s %s",
ICON_MD_TAB,
1910 session_info.c_str(),
1911 is_current ?
"(Current)" :
"")
1919 ImGui::EndTabItem();
1928 ImGui::Text(tr(
"%s Global search across all YAZE data"),
ICON_MD_INFO);
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)]);
2012 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).
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)
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.
void ClearTemporaryLayoutSnapshot()
project::YazeProject * GetCurrentProject()
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)
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 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
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)
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_
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()
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)
WindowDelegate & window_delegate_
bool DrawMenuBarIconButton(const char *icon, const char *tooltip, bool is_active=false)
bool ShouldShowDashboard() const
void ShowPopup(const std::string &popup_name)
void TogglePanelSidebar()
bool IsCompactLayout() const
StartupVisibility welcome_behavior_override_
int panel_finder_selected_idx_
char panel_finder_query_[256]
bool welcome_screen_manually_closed_
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)
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)
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.
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