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)
113 [
this](
const std::string& template_name) {
118 [
this](
const std::string& template_name,
const std::string& rom_path,
119 const std::string& project_name) -> absl::Status {
121 return absl::FailedPreconditionError(
122 "Editor manager is not available");
125 template_name, rom_path, project_name);
128 absl::StrFormat(
"Failed to create project: %s", status.message()),
133 project_name, template_name),
136 return absl::OkStatus();
139 welcome_screen_->SetOpenProjectCallback([
this](
const std::string& filepath) {
144 absl::StrFormat(
"Failed to open project: %s", status.message()),
155#ifdef YAZE_BUILD_AGENT_UI
168 absl::StrFormat(
"Failed to open project: %s", status.message()),
188 if (!project || project->filepath.empty()) {
250 if (!ImGui::GetCurrentContext()) {
254 const ImGuiViewport* viewport = ImGui::GetMainViewport();
259 ImDrawList* bg_draw_list =
260 ImGui::GetBackgroundDrawList(
const_cast<ImGuiViewport*
>(viewport));
263 auto current_theme = theme_manager.GetCurrentTheme();
267 ImVec2 grid_pos = viewport->WorkPos;
268 ImVec2 grid_size = viewport->WorkSize;
269 bg_renderer.RenderDockingBackground(bg_draw_list, grid_pos, grid_size,
270 current_theme.primary);
290#ifdef YAZE_BUILD_AGENT_UI
321 const ImGuiViewport* viewport = ImGui::GetMainViewport();
325 const float width = viewport->WorkSize.x;
326#if defined(__APPLE__) && TARGET_OS_IOS == 1
329 static bool compact_mode =
false;
330 constexpr float kEnterCompactWidth = 900.0f;
331 constexpr float kExitCompactWidth = 940.0f;
333 compact_mode ? (width < kExitCompactWidth) : (width < kEnterCompactWidth);
336 return width < 900.0f;
348 {{ImGuiCol_Button, ImVec4(0, 0, 0, 0)},
354 bool clicked = ImGui::SmallButton(icon);
356 if (tooltip && ImGui::IsItemHovered()) {
357 ImGui::SetTooltip(
"%s", tooltip);
365 const float frame_padding = ImGui::GetStyle().FramePadding.x;
368 return icon_width + frame_padding * 2.0f;
380 const std::string full_version =
383 const float item_spacing = 6.0f;
384 const float padding = 8.0f;
386 auto CalcSmallButtonWidth = [](
const char* label) ->
float {
388 const float frame_padding = ImGui::GetStyle().FramePadding.x;
389 const float text_w = ImGui::CalcTextSize(label).x;
390 return text_w + frame_padding * 2.0f;
394 const ImGuiViewport* viewport = ImGui::GetMainViewport();
395 const float true_viewport_right = viewport->WorkPos.x + viewport->WorkSize.x;
399 const bool has_panel_toggles =
401 float panel_buttons_width = 0.0f;
402 if (has_panel_toggles) {
403 const char* kIcons[] = {
410 constexpr size_t kIconCount =
sizeof(kIcons) /
sizeof(kIcons[0]);
412 for (
size_t i = 0; i < kIconCount; ++i) {
413 panel_buttons_width += CalcSmallButtonWidth(kIcons[i]);
414 if (i + 1 < kIconCount) {
415 panel_buttons_width += item_spacing;
422 float panel_region_width = panel_buttons_width;
425 panel_region_width +=
430 float panel_screen_x = true_viewport_right - panel_region_width;
438 const float window_width = ImGui::GetWindowWidth();
439 const float window_screen_x = ImGui::GetWindowPos().x;
440 const float menu_items_end = ImGui::GetCursorPosX() + 16.0f;
443 float panel_local_x = panel_screen_x - window_screen_x;
445 std::min(window_width - padding, panel_local_x - item_spacing);
449 current_rom && current_rom->is_loaded() && current_rom->dirty();
452 float version_width = ImGui::CalcTextSize(full_version.c_str()).x;
457 const float available_width = region_end - menu_items_end - padding;
467 (required_width + version_width + item_spacing) <= available_width;
469 required_width += version_width + item_spacing;
474 has_multiple_sessions &&
475 (required_width + session_width + item_spacing) <= available_width;
477 required_width += session_width + item_spacing;
482 has_dirty_rom && (required_width + dirty_width) <= available_width;
484 required_width += dirty_width;
488 float start_pos = std::max(menu_items_end, region_end - required_width);
493 ImGui::SameLine(start_pos);
495 ImVec2(item_spacing, 0.0f));
508 if (ImGui::IsItemHovered()) {
509 ImGui::SetTooltip(tr(
"Unsaved changes: %s"),
510 current_rom->short_name().c_str());
523 has_multiple_sessions);
528 if (has_panel_toggles) {
530 float menu_bar_y = ImGui::GetCursorScreenPos().y;
533 ImGui::SetCursorScreenPos(ImVec2(panel_screen_x, menu_bar_y));
543 "Hide menu bar (Alt to restore)")) {
556 ImGuiWindowFlags flags =
557 ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize |
558 ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar |
559 ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_AlwaysAutoResize |
560 ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoSavedSettings;
562 ImGui::SetNextWindowPos(ImVec2(8, 8));
563 ImGui::SetNextWindowBgAlpha(0.7f);
565 if (ImGui::Begin(
"##MenuBarRestore",
nullptr, flags)) {
576 if (ImGui::IsItemHovered()) {
577 ImGui::SetTooltip(tr(
"Show menu bar (Alt)"));
583 if (ImGui::IsKeyPressed(ImGuiKey_LeftAlt) ||
584 ImGui::IsKeyPressed(ImGuiKey_RightAlt)) {
591 bool has_multiple_sessions) {
602 ImVec4 bell_text_color = (unread > 0 || is_active)
606 {{ImGuiCol_Text, bell_text_color},
607 {ImGuiCol_Button, ImVec4(0, 0, 0, 0)},
620 if (ImGui::IsItemHovered()) {
621 ImGui::BeginTooltip();
632 ImGui::TextDisabled(tr(
"Click to open Notifications panel"));
635 if (!show_dirty && has_dirty_rom) {
640 current_rom->short_name().c_str());
643 if (!show_session && has_multiple_sessions) {
644 if (!show_dirty && has_dirty_rom) {
663 {{ImGuiCol_Button, ImVec4(0, 0, 0, 0)},
669 ImVec2 button_min = ImGui::GetCursorScreenPos();
672 ImGui::OpenPopup(
"##SessionSwitcherPopup");
675 ImVec2 button_max = ImGui::GetItemRectMax();
677 if (ImGui::IsItemHovered()) {
678 std::string tooltip = current_rom && current_rom->is_loaded()
679 ? current_rom->short_name()
681 ImGui::SetTooltip(tr(
"%s\n%zu sessions open (Ctrl+Tab)"), tooltip.c_str(),
686 const float popup_width = 250.0f;
687 const float screen_width = ImGui::GetIO().DisplaySize.x;
688 const float popup_x =
689 std::min(button_min.x, screen_width - popup_width - 10.0f);
691 ImGui::SetNextWindowPos(ImVec2(popup_x, button_max.y + 2.0f),
692 ImGuiCond_Appearing);
695 if (ImGui::BeginPopup(
"##SessionSwitcherPopup")) {
708 Rom* rom = &session->rom;
709 ImGui::PushID(
static_cast<int>(i));
711 bool is_current = (rom == current_rom);
712 std::optional<gui::StyleColorGuard> current_guard;
723 if (ImGui::Selectable(label.c_str(), is_current)) {
777 auto emulator_windows =
779 for (
const auto& window : emulator_windows) {
780 if (window.visibility_flag && *window.visibility_flag) {
790 auto default_windows =
792 for (
const auto& window_id : default_windows) {
807 for (
const auto& window : windows) {
808 if (window.visibility_flag && *window.visibility_flag) {
818 auto default_windows =
820 for (
const auto& window_id : default_windows) {
849 "EditorManager is null - cannot check ROM state");
854 LOG_ERROR(
"UICoordinator",
"WelcomeScreen object is null - cannot render");
860 bool rom_is_loaded = current_rom && current_rom->
is_loaded();
925 ImGuiWindowFlags_AlwaysAutoResize);
926 static char preset_name[128] =
"";
927 ImGui::InputText(tr(
"Name"), preset_name, IM_ARRAYSIZE(preset_name));
929 if (strlen(preset_name) > 0) {
933 preset_name[0] =
'\0';
939 preset_name[0] =
'\0';
946 ImGuiWindowFlags_AlwaysAutoResize);
952 for (
const auto& name : workspace_manager->workspace_presets()) {
953 if (ImGui::Selectable(name.c_str())) {
959 if (workspace_manager->workspace_presets().empty())
960 ImGui::Text(tr(
"No presets found"));
1016 const std::string& icon,
1017 const ImVec4& color,
1018 std::function<
void()> callback,
1020 std::optional<gui::StyleColorGuard> disabled_guard;
1022 disabled_guard.emplace(std::initializer_list<gui::StyleColorGuard::Entry>{
1027 std::string button_text =
1028 absl::StrFormat(
"%s %s", icon.c_str(), text.c_str());
1029 if (ImGui::Button(button_text.c_str())) {
1030 if (enabled && callback) {
1038 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(),
1039 ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
1044 ImGui::SetNextWindowPos(ImVec2(x, y), ImGuiCond_Appearing);
1049 ImGui::SetNextWindowSize(ImVec2(width, height), ImGuiCond_FirstUseEver);
1061 using namespace ImGui;
1064 SetNextWindowPos(GetMainViewport()->GetCenter(), ImGuiCond_Appearing,
1065 ImVec2(0.5f, 0.5f));
1066 SetNextWindowSize(ImVec2(800, 600), ImGuiCond_FirstUseEver);
1068 bool show_palette =
true;
1069 if (Begin(absl::StrFormat(
"%s Command Palette",
ICON_MD_SEARCH).c_str(),
1070 &show_palette, ImGuiWindowFlags_NoCollapse)) {
1072 SetNextItemWidth(-100);
1073 if (IsWindowAppearing()) {
1074 SetKeyboardFocusHere();
1078 bool input_changed = InputTextWithHint(
1080 absl::StrFormat(
"%s Search commands (fuzzy matching enabled)...",
1086 if (Button(absl::StrFormat(
"%s Clear",
ICON_MD_CLEAR).c_str())) {
1088 input_changed =
true;
1095 struct ScoredCommand {
1098 std::string category;
1099 std::string shortcut;
1100 std::function<void()> callback;
1102 std::vector<ScoredCommand> scored_commands;
1105 std::transform(query_lower.begin(), query_lower.end(), query_lower.begin(),
1108 auto score_text = [&query_lower](
const std::string& text) ->
int {
1109 std::string text_lower = text;
1110 std::transform(text_lower.begin(), text_lower.end(), text_lower.begin(),
1113 if (query_lower.empty())
1115 if (text_lower.find(query_lower) == 0)
1117 if (text_lower.find(query_lower) != std::string::npos)
1121 size_t text_idx = 0, query_idx = 0;
1123 while (text_idx < text_lower.length() &&
1124 query_idx < query_lower.length()) {
1125 if (text_lower[text_idx] == query_lower[query_idx]) {
1131 return (query_idx == query_lower.length()) ? score : 0;
1136 int score = score_text(name);
1138 std::string shortcut_text =
1139 shortcut.keys.empty()
1141 : absl::StrFormat(
"(%s)",
PrintShortcut(shortcut.keys).c_str());
1142 scored_commands.push_back(
1143 {score, name,
"Shortcuts", shortcut_text, shortcut.callback});
1149 int score = score_text(entry.name);
1151 score += score_text(entry.category) / 2;
1152 score += score_text(entry.description) / 4;
1155 scored_commands.push_back({score, entry.name, entry.category,
1156 entry.shortcut, entry.callback});
1161 std::sort(scored_commands.begin(), scored_commands.end(),
1162 [](
const auto& a,
const auto& b) { return a.score > b.score; });
1167 absl::StrFormat(
"%s All Commands",
ICON_MD_LIST).c_str())) {
1169 "CommandPaletteTable", 4,
1170 ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg |
1171 ImGuiTableFlags_SizingStretchProp,
1173 TableSetupColumn(
"Command", ImGuiTableColumnFlags_WidthStretch,
1175 TableSetupColumn(
"Category", ImGuiTableColumnFlags_WidthStretch,
1177 TableSetupColumn(
"Shortcut", ImGuiTableColumnFlags_WidthStretch,
1179 TableSetupColumn(
"Score", ImGuiTableColumnFlags_WidthStretch, 0.15f);
1182 for (
size_t i = 0; i < scored_commands.size(); ++i) {
1183 const auto& cmd = scored_commands[i];
1188 PushID(
static_cast<int>(i));
1191 if (Selectable(cmd.name.c_str(), is_selected,
1192 ImGuiSelectableFlags_SpanAllColumns)) {
1221 if (BeginTabItem(absl::StrFormat(
"%s Recent",
ICON_MD_HISTORY).c_str())) {
1223 if (recent.empty()) {
1224 Text(tr(
"No recent commands yet."));
1226 for (
const auto& entry : recent) {
1227 if (Selectable(entry.name.c_str())) {
1228 if (entry.callback) {
1239 if (BeginTabItem(absl::StrFormat(
"%s Frequent",
ICON_MD_STAR).c_str())) {
1241 if (frequent.empty()) {
1242 Text(tr(
"No frequently used commands yet."));
1244 for (
const auto& entry : frequent) {
1245 if (Selectable(absl::StrFormat(
"%s (%d uses)", entry.name,
1248 if (entry.callback) {
1264 Text(tr(
"%s %zu commands | Score: fuzzy match"),
ICON_MD_INFO,
1265 scored_commands.size());
1273 if (!show_palette) {
1277 if (config_dir.ok()) {
1278 std::filesystem::path history_file = *config_dir /
"command_history.json";
1288 using namespace ImGui;
1292 const ImGuiViewport* viewport = GetMainViewport();
1293 ImDrawList* bg_list = GetBackgroundDrawList();
1294 bg_list->AddRectFilled(viewport->WorkPos,
1295 ImVec2(viewport->WorkPos.x + viewport->WorkSize.x,
1296 viewport->WorkPos.y + viewport->WorkSize.y),
1297 IM_COL32(0, 0, 0, 100));
1299 SetNextWindowPos(viewport->GetCenter(), ImGuiCond_Appearing,
1300 ImVec2(0.5f, 0.3f));
1303 ImVec2(viewport->WorkSize.x * 0.95f, viewport->WorkSize.y * 0.70f),
1304 ImGuiCond_Appearing);
1306 SetNextWindowSize(ImVec2(600, 420), ImGuiCond_Appearing);
1309 const ImGuiWindowFlags finder_flags =
1310 ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove |
1311 ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoDocking |
1312 ImGuiWindowFlags_NoSavedSettings;
1317 if (IsWindowAppearing()) {
1318 SetKeyboardFocusHere();
1322 SetNextItemWidth(-1);
1323 bool input_changed = InputTextWithHint(
1327 if (input_changed) {
1334 struct WindowEntry {
1335 std::string card_id;
1336 std::string display_name;
1338 std::string category;
1343 std::vector<WindowEntry> entries;
1348 for (
const auto& card_id :
1356 if (!query.empty()) {
1365 entries.push_back({card_id, desc->display_name, desc->icon,
1366 desc->category, vis, pin, score});
1370 if (query.empty()) {
1373 entries.begin(), entries.end(),
1374 [
this](
const WindowEntry& lhs,
const WindowEntry& rhs) {
1375 if (lhs.pinned != rhs.pinned)
1376 return lhs.pinned > rhs.pinned;
1377 uint64_t lhs_t = window_manager_.GetWindowMRUTime(lhs.card_id);
1378 uint64_t rhs_t = window_manager_.GetWindowMRUTime(rhs.card_id);
1380 return lhs_t > rhs_t;
1381 return lhs.display_name < rhs.display_name;
1385 std::sort(entries.begin(), entries.end(),
1386 [](
const WindowEntry& lhs,
const WindowEntry& rhs) {
1387 if (lhs.score != rhs.score)
1388 return lhs.score > rhs.score;
1389 if (lhs.pinned != rhs.pinned)
1390 return lhs.pinned > rhs.pinned;
1391 return lhs.display_name < rhs.display_name;
1396 if (IsKeyPressed(ImGuiKey_DownArrow) &&
1403 bool enter_pressed = IsKeyPressed(ImGuiKey_Enter);
1408 PushStyleVar(ImGuiStyleVar_ItemSpacing,
1409 ImVec2(GetStyle().ItemSpacing.x, 10.0f));
1413 BeginChild(
"##PanelFinderList");
1414 for (
int i = 0; i < static_cast<int>(entries.size()); ++i) {
1415 const auto& entry = entries[i];
1419 const char* vis_icon =
1423 std::optional<gui::StyleColorGuard> dim_guard;
1424 if (!entry.visible) {
1425 ImVec4 dimmed = GetStyleColorVec4(ImGuiCol_Text);
1427 dim_guard.emplace(ImGuiCol_Text, dimmed);
1431 absl::StrFormat(
"%s %s %s", vis_icon, entry.icon.c_str(),
1432 entry.display_name.c_str());
1436 is_touch ? std::max(GetTextLineHeightWithSpacing(), 44.0f) : 0.0f;
1438 PushID(entry.card_id.c_str());
1439 if (Selectable(label.c_str(), is_selected, ImGuiSelectableFlags_None,
1440 ImVec2(0, item_h))) {
1446 SameLine(GetContentRegionAvail().x - 80);
1447 TextDisabled(
"%s", entry.category.c_str());
1455 if (is_selected && enter_pressed) {
1462 if (is_selected && (IsKeyPressed(ImGuiKey_DownArrow) ||
1463 IsKeyPressed(ImGuiKey_UpArrow))) {
1476 if (!show || ImGui::IsKeyPressed(ImGuiKey_Escape)) {
1488 std::make_unique<PanelCommandsProvider>(&
window_manager_, session_id));
1490 std::make_unique<WorkflowCommandsProvider>(&
window_manager_, session_id));
1498 [
this](
const std::string& category) {
1507 "Switch to essential cards only",
"", [
this]() {
1515 "Switch to debug and development focused layout",
"", [
this]() {
1523 "Switch to visual and overworld focused layout",
"", [
this]() {
1531 "Switch to comprehensive dungeon editing layout",
"", [
this]() {
1539 "Switch to music and sound editing layout",
"", [
this]() {
1547 std::make_unique<RecentFilesCommandsProvider>(
1548 [
this](
const std::string& filepath) {
1553 absl::StrFormat(
"Failed to open: %s", status.message()),
1561 std::make_unique<DungeonRoomCommandsProvider>(session_id));
1572 "Vanilla ROM Hack",
"ZSCustomOverworld v3",
"ZSCustomOverworld v2",
1573 "Randomizer Compatible"};
1574 welcome_callbacks.
remove = [
this](
const std::string& path) {
1580 absl::StrFormat(
"Removed %s from recents",
1581 std::filesystem::path(path).filename().string()),
1584 welcome_callbacks.
toggle_pin = [
this](
const std::string& path) {
1588 bool currently_pinned =
false;
1589 for (
const auto& entry : model.entries()) {
1590 if (entry.filepath == path) {
1591 currently_pinned = entry.pinned;
1595 model.SetPinned(path, !currently_pinned);
1615 [
this](
const std::string& template_name) {
1629 std::move(welcome_callbacks)));
1634 if (config_dir.ok()) {
1635 std::filesystem::path history_file = *config_dir /
"command_history.json";
1648 {.id =
"workflow.project.build",
1649 .group =
"Build & Run",
1650 .label =
"Build Project",
1651 .description =
"Run the active project's configured build command",
1654 .callback = [
this]() {
1660 {.id =
"workflow.project.run",
1661 .group =
"Build & Run",
1662 .label =
"Run Project Output",
1663 .description =
"Open the active project's configured run target in a "
1667 .callback = [
this]() {
1674#ifdef YAZE_WITH_GRPC
1678 {.id =
"workflow.mesen.connect",
1679 .group =
"Live Debugging",
1680 .label =
"Connect Mesen2",
1682 "Auto-discover and connect to the active Mesen2 backend",
1685 .callback = [
this]() {
1693 {.id =
"workflow.mesen.step_over",
1694 .group =
"Live Debugging",
1695 .label =
"Mesen2 Step Over",
1696 .description =
"Execute one instruction without entering subroutines",
1699 .callback = [emu_backend]() { emu_backend->StepOver(); }});
1701 {.id =
"workflow.mesen.step_out",
1702 .group =
"Live Debugging",
1703 .label =
"Mesen2 Step Out",
1704 .description =
"Run until return from the current subroutine",
1705 .shortcut =
"Shift+F11",
1707 .callback = [emu_backend]() { emu_backend->StepOut(); }});
1709 {.id =
"workflow.mesen.overlay_on",
1710 .group =
"Live Debugging",
1711 .label =
"Enable Collision Overlay",
1712 .description =
"Show collision overlays in the active Mesen2 backend",
1715 .callback = [emu_backend]() {
1716 emu_backend->SetCollisionOverlay(
true);
1719 {.id =
"workflow.mesen.overlay_off",
1720 .group =
"Live Debugging",
1721 .label =
"Disable Collision Overlay",
1722 .description =
"Hide collision overlays in the active Mesen2 backend",
1725 .callback = [emu_backend]() {
1726 emu_backend->SetCollisionOverlay(
false);
1740 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(),
1741 ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
1742 ImGui::SetNextWindowSize(ImVec2(800, 600), ImGuiCond_FirstUseEver);
1744 bool show_search =
true;
1747 &show_search, ImGuiWindowFlags_NoCollapse)) {
1749 ImGui::SetNextItemWidth(-100);
1750 if (ImGui::IsWindowAppearing()) {
1751 ImGui::SetKeyboardFocusHere();
1754 bool input_changed = ImGui::InputTextWithHint(
1756 absl::StrFormat(
"%s Search everything...",
ICON_MD_SEARCH).c_str(),
1760 if (ImGui::Button(absl::StrFormat(
"%s Clear",
ICON_MD_CLEAR).c_str())) {
1762 input_changed =
true;
1770 if (ImGui::BeginTabItem(
1773 auto recent_files = manager.GetRecentFiles();
1775 if (ImGui::BeginTable(
"RecentFilesTable", 3,
1776 ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg |
1777 ImGuiTableFlags_SizingStretchProp)) {
1778 ImGui::TableSetupColumn(
"File", ImGuiTableColumnFlags_WidthStretch,
1780 ImGui::TableSetupColumn(
"Type", ImGuiTableColumnFlags_WidthFixed,
1782 ImGui::TableSetupColumn(
"Action", ImGuiTableColumnFlags_WidthFixed,
1784 ImGui::TableHeadersRow();
1786 for (
const auto& file : recent_files) {
1791 ImGui::TableNextRow();
1792 ImGui::TableNextColumn();
1795 ImGui::TableNextColumn();
1797 if (ext ==
"sfc" || ext ==
"smc") {
1798 ImGui::TextColored(ImVec4(0.2f, 0.8f, 0.2f, 1.0f), tr(
"%s ROM"),
1800 }
else if (ext ==
"yaze") {
1801 ImGui::TextColored(ImVec4(0.2f, 0.6f, 0.8f, 1.0f),
1807 ImGui::TableNextColumn();
1808 ImGui::PushID(file.c_str());
1809 if (ImGui::Button(tr(
"Open"))) {
1813 absl::StrCat(
"Failed to open: ", status.message()),
1823 ImGui::EndTabItem();
1828 if (current_rom && current_rom->resource_label()) {
1829 if (ImGui::BeginTabItem(
1833 if (ImGui::BeginTable(
"LabelsTable", 3,
1834 ImGuiTableFlags_ScrollY |
1835 ImGuiTableFlags_RowBg |
1836 ImGuiTableFlags_SizingStretchProp)) {
1837 ImGui::TableSetupColumn(
"Type", ImGuiTableColumnFlags_WidthFixed,
1839 ImGui::TableSetupColumn(
"Label", ImGuiTableColumnFlags_WidthStretch,
1841 ImGui::TableSetupColumn(
"Value", ImGuiTableColumnFlags_WidthStretch,
1843 ImGui::TableHeadersRow();
1845 for (
const auto& type_pair : labels) {
1846 for (
const auto& kv : type_pair.second) {
1852 ImGui::TableNextRow();
1853 ImGui::TableNextColumn();
1854 ImGui::Text(
"%s", type_pair.first.c_str());
1856 ImGui::TableNextColumn();
1857 if (ImGui::Selectable(kv.first.c_str(),
false,
1858 ImGuiSelectableFlags_SpanAllColumns)) {
1862 ImGui::TableNextColumn();
1863 ImGui::TextDisabled(
"%s", kv.second.c_str());
1869 ImGui::EndTabItem();
1875 if (ImGui::BeginTabItem(
1876 absl::StrFormat(
"%s Sessions",
ICON_MD_TAB).c_str())) {
1877 ImGui::Text(tr(
"Search and switch between active sessions:"));
1881 std::string session_info =
1883 if (session_info ==
"[CLOSED SESSION]")
1892 std::optional<gui::StyleColorGuard> current_guard;
1894 current_guard.emplace(ImGuiCol_Text,
1895 ImVec4(0.2f, 0.8f, 0.2f, 1.0f));
1898 if (ImGui::Selectable(absl::StrFormat(
"%s %s %s",
ICON_MD_TAB,
1899 session_info.c_str(),
1900 is_current ?
"(Current)" :
"")
1908 ImGui::EndTabItem();
1917 ImGui::Text(tr(
"%s Global search across all YAZE data"),
ICON_MD_INFO);
1936 const char* surface_names[] = {
"Welcome",
"Dashboard",
"Editor"};
1937 LOG_INFO(
"UICoordinator",
"Startup surface: %s -> %s",
1938 surface_names[
static_cast<int>(old_surface)],
1939 surface_names[
static_cast<int>(surface)]);
2001 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 AddCommand(const std::string &name, const std::string &category, const std::string &description, const std::string &shortcut, std::function< void()> callback)
void LoadHistory(const std::string &filepath)
Load command usage history from disk.
std::vector< CommandEntry > GetAllCommands() const
Get all registered commands.
void RecordUsage(const std::string &name)
void RegisterProvider(std::unique_ptr< CommandProvider > provider)
std::vector< CommandEntry > GetRecentCommands(int limit=10)
std::vector< CommandEntry > GetFrequentCommands(int limit=10)
static int FuzzyScore(const std::string &text, const std::string &query)
The EditorManager controls the main editor window and manages the various editor classes.
void SaveWorkspacePreset(const std::string &name)
float GetRightLayoutOffset() const
void SwitchToEditor(EditorType editor_type, bool force_visible=false, bool from_dialog=false) override
void QueueBuildCurrentProject()
void SwitchToSession(size_t index)
absl::Status OpenProject()
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)
UserSettings & user_settings()
absl::Status LoadRom()
Load a ROM file into a new or existing session.
void ShowProjectFileEditor()
project::YazeProject * GetCurrentProject()
absl::Status OpenRomOrProject(const std::string &filename)
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 HasActiveProject() const
bool DrawDrawerToggleButtons()
Draw drawer toggle buttons for the status cluster.
bool IsDrawerActive(DrawerType type) const
Check if a specific drawer is active.
bool IsDrawerExpanded() const
Check if any drawer is currently expanded (or animating closed)
float GetDrawerWidth() const
Get the width of the drawer when expanded.
Handles all ROM file I/O operations.
High-level orchestrator for multi-session UI.
void * GetSession(size_t index) const
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)
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
ProjectManager & project_manager_
StartupVisibility welcome_behavior_override_
int panel_finder_selected_idx_
char panel_finder_query_[256]
bool welcome_screen_manually_closed_
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()
NewProjectDialog new_project_dialog_
void SetWelcomeScreenBehavior(StartupVisibility mode)
bool show_editor_selection_
bool show_welcome_screen_
WorkspaceWindowManager & window_manager_
EditorManager * editor_manager_
void SetDashboardBehavior(StartupVisibility mode)
bool IsSessionSwitcherVisible() const
void SetCommandPaletteVisible(bool visible)
void ShowCommandPalette()
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_FOLDER_SPECIAL
#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_HELP_OUTLINE
#define ICON_MD_DASHBOARD
#define ICON_MD_FIBER_MANUAL_RECORD
#define ICON_MD_SMART_TOY
#define LOG_ERROR(category, format,...)
#define LOG_INFO(category, format,...)
void Register(const ActionDef &action)
StartupSurface
Represents the current startup surface state.
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 GetTextDisabledVec4()
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.
static constexpr const char * kLayout
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