9#include "absl/strings/str_format.h"
25#include "imgui/imgui.h"
42 : editor_manager_(editor_manager),
43 rom_manager_(rom_manager),
44 project_manager_(project_manager),
45 editor_registry_(editor_registry),
46 card_registry_(card_registry),
47 session_coordinator_(session_coordinator),
48 window_delegate_(window_delegate),
49 toast_manager_(toast_manager),
50 popup_manager_(popup_manager),
51 shortcut_manager_(shortcut_manager) {
62 absl::StrFormat(
"Failed to load ROM: %s", status.message()),
66 show_welcome_screen_ = false;
67 welcome_screen_manually_closed_ = true;
77 absl::StrFormat(
"Failed to create project: %s", status.message()),
81 show_welcome_screen_ = false;
82 welcome_screen_manually_closed_ = true;
87 welcome_screen_->SetOpenProjectCallback([
this](
const std::string& filepath) {
92 absl::StrFormat(
"Failed to open project: %s", status.message()),
96 show_welcome_screen_ = false;
97 welcome_screen_manually_closed_ = true;
120 ImGui::SameLine((ImGui::GetWindowWidth() / 2) - 100);
121 if (current_rom && current_rom->is_loaded()) {
122 ImGui::SetNextItemWidth(ImGui::GetWindowWidth() / 6);
123 if (ImGui::BeginCombo(
"##ROMSelector", current_rom->short_name().c_str())) {
128 if (!session)
continue;
130 Rom* rom = &session->rom;
131 ImGui::PushID(
static_cast<int>(i));
132 bool selected = (rom == current_rom);
133 if (ImGui::Selectable(rom->
short_name().c_str(), selected)) {
142 ImGui::Text(
"Size: %.1f MB", current_rom->size() / 1048576.0f);
148 ImGui::Text(
"No ROM loaded");
158 float version_width = ImGui::CalcTextSize(version_text.c_str()).x;
163 std::string session_button_text = absl::StrFormat(
"%s %zu",
ICON_MD_TAB,
171 if (ImGui::SmallButton(session_button_text.c_str())) {
175 ImGui::PopStyleColor(3);
177 if (ImGui::IsItemHovered()) {
178 ImGui::SetTooltip(
"Switch Sessions (Ctrl+Tab)");
184 if (current_rom && current_rom->is_loaded()) {
186 std::string rom_title = current_rom->title();
187 if (current_rom->dirty()) {
189 if (ImGui::IsItemHovered()) {
190 ImGui::SetTooltip(
"Unsaved changes");
195 ImGui::PopStyleColor();
199 ImGui::PopStyleColor();
203 ImGui::SameLine(ImGui::GetWindowWidth() - version_width - 15.0f);
205 ImGui::Text(
"%s", version_text.c_str());
206 ImGui::PopStyleColor();
213 if (!active_editor)
return;
229 if (ImGui::SmallButton(absl::StrFormat(
"%s %s",
ICON_MD_LAYERS, category.c_str()).c_str())) {
230 ImGui::OpenPopup(
"##CardQuickAccess");
233 ImGui::PopStyleColor(2);
235 if (ImGui::IsItemHovered()) {
236 ImGui::SetTooltip(
"Quick access to %s cards", category.c_str());
240 if (ImGui::BeginPopup(
"##CardQuickAccess")) {
243 for (
const auto& card : cards) {
244 bool visible = card.visibility_flag ? *card.visibility_flag :
false;
245 if (ImGui::MenuItem(card.display_name.c_str(),
nullptr, visible)) {
298 LOG_ERROR(
"UICoordinator",
"EditorManager is null - cannot check ROM state");
303 LOG_ERROR(
"UICoordinator",
"WelcomeScreen object is null - cannot render");
309 bool rom_is_loaded = current_rom && current_rom->is_loaded();
353 ImGuiWindowFlags_AlwaysAutoResize);
354 static char preset_name[128] =
"";
355 ImGui::InputText(
"Name", preset_name, IM_ARRAYSIZE(preset_name));
357 if (strlen(preset_name) > 0) {
361 preset_name[0] =
'\0';
367 preset_name[0] =
'\0';
374 ImGuiWindowFlags_AlwaysAutoResize);
380 for (
const auto& name : workspace_manager->workspace_presets()) {
381 if (ImGui::Selectable(name.c_str())) {
387 if (workspace_manager->workspace_presets().empty())
388 ImGui::Text(
"No presets found");
422 if (!current_editor)
return;
427 LOG_INFO(
"UICoordinator",
"Hid all cards in category: %s", category.c_str());
453 const ImVec4& color, std::function<
void()> callback,
460 std::string button_text = absl::StrFormat(
"%s %s", icon.c_str(), text.c_str());
461 if (ImGui::Button(button_text.c_str())) {
462 if (enabled && callback) {
468 ImGui::PopStyleColor(2);
474 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
478 ImGui::SetNextWindowPos(ImVec2(x, y), ImGuiCond_Appearing);
482 ImGui::SetNextWindowSize(ImVec2(width, height), ImGuiCond_FirstUseEver);
518 using namespace ImGui;
521 SetNextWindowPos(GetMainViewport()->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
522 SetNextWindowSize(ImVec2(800, 600), ImGuiCond_FirstUseEver);
524 bool show_palette =
true;
525 if (Begin(absl::StrFormat(
"%s Command Palette",
ICON_MD_SEARCH).c_str(),
526 &show_palette, ImGuiWindowFlags_NoCollapse)) {
529 SetNextItemWidth(-100);
530 if (IsWindowAppearing()) {
531 SetKeyboardFocusHere();
535 bool input_changed = InputTextWithHint(
537 absl::StrFormat(
"%s Search commands (fuzzy matching enabled)...",
ICON_MD_SEARCH).c_str(),
541 if (Button(absl::StrFormat(
"%s Clear",
ICON_MD_CLEAR).c_str())) {
543 input_changed =
true;
550 std::vector<std::pair<int, std::pair<std::string, std::string>>> scored_commands;
552 std::transform(query_lower.begin(), query_lower.end(), query_lower.begin(), ::tolower);
555 const auto& name = entry.first;
556 const auto& shortcut = entry.second;
558 std::string name_lower = name;
559 std::transform(name_lower.begin(), name_lower.end(), name_lower.begin(), ::tolower);
564 }
else if (name_lower.find(query_lower) == 0) {
566 }
else if (name_lower.find(query_lower) != std::string::npos) {
570 size_t text_idx = 0, query_idx = 0;
571 while (text_idx < name_lower.length() && query_idx < query_lower.length()) {
572 if (name_lower[text_idx] == query_lower[query_idx]) {
578 if (query_idx != query_lower.length()) score = 0;
582 std::string shortcut_text = shortcut.keys.empty()
584 : absl::StrFormat(
"(%s)",
PrintShortcut(shortcut.keys).c_str());
585 scored_commands.push_back({score, {name, shortcut_text}});
589 std::sort(scored_commands.begin(), scored_commands.end(),
590 [](
const auto& a,
const auto& b) { return a.first > b.first; });
593 if (BeginTabBar(
"CommandCategories")) {
594 if (BeginTabItem(absl::StrFormat(
"%s All Commands",
ICON_MD_LIST).c_str())) {
596 ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingStretchProp,
599 TableSetupColumn(
"Command", ImGuiTableColumnFlags_WidthStretch, 0.5f);
600 TableSetupColumn(
"Shortcut", ImGuiTableColumnFlags_WidthStretch, 0.3f);
601 TableSetupColumn(
"Score", ImGuiTableColumnFlags_WidthStretch, 0.2f);
604 for (
size_t i = 0; i < scored_commands.size(); ++i) {
605 const auto& [score, cmd_pair] = scored_commands[i];
606 const auto& [command_name, shortcut_text] = cmd_pair;
611 PushID(
static_cast<int>(i));
613 if (Selectable(command_name.c_str(), is_selected,
614 ImGuiSelectableFlags_SpanAllColumns)) {
617 auto it = shortcuts.find(command_name);
618 if (it != shortcuts.end() && it->second.callback) {
619 it->second.callback();
627 Text(
"%s", shortcut_text.c_str());
643 if (BeginTabItem(absl::StrFormat(
"%s Recent",
ICON_MD_HISTORY).c_str())) {
644 Text(
"Recent commands coming soon...");
648 if (BeginTabItem(absl::StrFormat(
"%s Frequent",
ICON_MD_STAR).c_str())) {
649 Text(
"Frequent commands coming soon...");
658 Text(
"%s %zu commands | Score: fuzzy match",
ICON_MD_INFO, scored_commands.size());
661 Text(
"| ↑↓=Navigate | Enter=Execute | Esc=Close");
675 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(),
676 ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
677 ImGui::SetNextWindowSize(ImVec2(800, 600), ImGuiCond_FirstUseEver);
679 bool show_search =
true;
682 &show_search, ImGuiWindowFlags_NoCollapse)) {
685 ImGui::SetNextItemWidth(-100);
686 if (ImGui::IsWindowAppearing()) {
687 ImGui::SetKeyboardFocusHere();
690 bool input_changed = ImGui::InputTextWithHint(
692 absl::StrFormat(
"%s Search everything...",
ICON_MD_SEARCH).c_str(),
696 if (ImGui::Button(absl::StrFormat(
"%s Clear",
ICON_MD_CLEAR).c_str())) {
698 input_changed =
true;
704 if (ImGui::BeginTabBar(
"SearchResultTabs")) {
707 if (ImGui::BeginTabItem(
710 auto recent_files = manager.GetRecentFiles();
712 if (ImGui::BeginTable(
"RecentFilesTable", 3,
713 ImGuiTableFlags_ScrollY |
714 ImGuiTableFlags_RowBg |
715 ImGuiTableFlags_SizingStretchProp)) {
717 ImGui::TableSetupColumn(
"File", ImGuiTableColumnFlags_WidthStretch,
719 ImGui::TableSetupColumn(
"Type", ImGuiTableColumnFlags_WidthFixed,
721 ImGui::TableSetupColumn(
"Action", ImGuiTableColumnFlags_WidthFixed,
723 ImGui::TableHeadersRow();
725 for (
const auto& file : recent_files) {
729 ImGui::TableNextRow();
730 ImGui::TableNextColumn();
733 ImGui::TableNextColumn();
735 if (ext ==
"sfc" || ext ==
"smc") {
736 ImGui::TextColored(ImVec4(0.2f, 0.8f, 0.2f, 1.0f),
"%s ROM",
738 }
else if (ext ==
"yaze") {
739 ImGui::TextColored(ImVec4(0.2f, 0.6f, 0.8f, 1.0f),
"%s Project",
745 ImGui::TableNextColumn();
746 ImGui::PushID(file.c_str());
747 if (ImGui::Button(
"Open")) {
764 if (current_rom && current_rom->resource_label()) {
765 if (ImGui::BeginTabItem(
767 auto& labels = current_rom->resource_label()->labels_;
769 if (ImGui::BeginTable(
"LabelsTable", 3,
770 ImGuiTableFlags_ScrollY |
771 ImGuiTableFlags_RowBg |
772 ImGuiTableFlags_SizingStretchProp)) {
774 ImGui::TableSetupColumn(
"Type", ImGuiTableColumnFlags_WidthFixed,
776 ImGui::TableSetupColumn(
"Label",
777 ImGuiTableColumnFlags_WidthStretch, 0.4f);
778 ImGui::TableSetupColumn(
"Value",
779 ImGuiTableColumnFlags_WidthStretch, 0.6f);
780 ImGui::TableHeadersRow();
782 for (
const auto& type_pair : labels) {
783 for (
const auto& kv : type_pair.second) {
789 ImGui::TableNextRow();
790 ImGui::TableNextColumn();
791 ImGui::Text(
"%s", type_pair.first.c_str());
793 ImGui::TableNextColumn();
794 if (ImGui::Selectable(kv.first.c_str(),
false,
795 ImGuiSelectableFlags_SpanAllColumns)) {
799 ImGui::TableNextColumn();
800 ImGui::TextDisabled(
"%s", kv.second.c_str());
812 if (ImGui::BeginTabItem(
813 absl::StrFormat(
"%s Sessions",
ICON_MD_TAB).c_str())) {
814 ImGui::Text(
"Search and switch between active sessions:");
818 if (session_info ==
"[CLOSED SESSION]")
827 ImGui::PushStyleColor(ImGuiCol_Text,
828 ImVec4(0.2f, 0.8f, 0.2f, 1.0f));
831 if (ImGui::Selectable(absl::StrFormat(
"%s %s %s",
ICON_MD_TAB,
832 session_info.c_str(),
833 is_current ?
"(Current)" :
"")
842 ImGui::PopStyleColor();
854 ImGui::Text(
"%s Global search across all YAZE data",
ICON_MD_INFO);
The Rom class is used to load, save, and modify Rom data.
Central registry for all editor cards with session awareness and dependency injection.
bool HideCard(size_t session_id, const std::string &base_card_id)
Hide a card programmatically.
std::vector< CardInfo > GetCardsInCategory(size_t session_id, const std::string &category) const
Get cards in a specific category for a session.
void HideAllCardsInCategory(size_t session_id, const std::string &category)
Hide all cards in a category for a session.
bool ShowCard(size_t session_id, const std::string &base_card_id)
Show a card programmatically.
The EditorManager controls the main editor window and manages the various editor classes.
void SaveWorkspacePreset(const std::string &name)
void SwitchToSession(size_t index)
WorkspaceManager * workspace_manager()
void LoadWorkspacePreset(const std::string &name)
absl::Status CreateNewProject(const std::string &template_name="Basic ROM Hack")
void RefreshWorkspacePresets()
auto GetCurrentEditor() const -> Editor *
absl::Status LoadRom()
Load a ROM file into a new or existing session.
size_t GetCurrentSessionId() const
auto GetCurrentRom() const -> Rom *
absl::Status OpenRomOrProject(const std::string &filename)
Manages editor types, categories, and lifecycle.
static bool IsCardBasedEditor(EditorType type)
static std::string GetEditorCategory(EditorType type)
Handles all project file operations.
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
size_t GetActiveSessionCount() const
void HideSessionSwitcher()
std::string GetSessionDisplayName(size_t index) const
void ShowSessionSwitcher()
size_t GetTotalSessionCount() const
void ToggleSessionSwitcher()
bool IsSessionClosed(size_t index) const
bool HasMultipleSessions() const
auto GetShortcuts() const
void Show(const std::string &message, ToastType type=ToastType::kInfo, float ttl_seconds=3.0f)
ShortcutManager & shortcut_manager_
void DrawMaterialButton(const std::string &text, const std::string &icon, const ImVec4 &color, std::function< void()> callback, bool enabled=true)
void HideCurrentEditorCards()
void DrawSessionIndicator()
bool show_save_workspace_preset_
void SetSessionSwitcherVisible(bool visible)
void SetGlobalSearchVisible(bool visible)
void HidePopup(const std::string &popup_name)
SessionCoordinator & session_coordinator_
std::string GetColorForEditor(EditorType type) const
char global_search_query_[256]
void ShowSessionSwitcher()
ToastManager & toast_manager_
void DrawWorkspacePresetDialogs()
char command_palette_query_[256]
EditorCardRegistry & card_registry_
UICoordinator(EditorManager *editor_manager, RomFileManager &rom_manager, ProjectManager &project_manager, EditorRegistry &editor_registry, EditorCardRegistry &card_registry, SessionCoordinator &session_coordinator, WindowDelegate &window_delegate, ToastManager &toast_manager, PopupManager &popup_manager, ShortcutManager &shortcut_manager)
bool show_command_palette_
std::string GetIconForEditor(EditorType type) const
WindowDelegate & window_delegate_
void ShowPopup(const std::string &popup_name)
bool welcome_screen_manually_closed_
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_welcome_screen_
EditorManager * editor_manager_
bool IsSessionSwitcherVisible() const
void ApplyEditorTheme(EditorType type)
void DrawContextSensitiveCardControl()
std::unique_ptr< WelcomeScreen > welcome_screen_
bool show_load_workspace_preset_
int command_palette_selected_idx_
void DrawCommandPalette()
EditorRegistry & editor_registry_
void CenterWindow(const std::string &window_name)
Low-level window operations with minimal dependencies.
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 ThemeManager & Get()
const EnhancedTheme & GetCurrentTheme() const
static RecentFilesManager & GetInstance()
#define ICON_MD_INSERT_DRIVE_FILE
#define ICON_MD_DATA_ARRAY
#define ICON_MD_PLAY_ARROW
#define ICON_MD_VIDEOGAME_ASSET
#define ICON_MD_CHAT_BUBBLE
#define ICON_MD_MUSIC_NOTE
#define ICON_MD_MANAGE_SEARCH
#define ICON_MD_DESCRIPTION
#define LOG_ERROR(category, format,...)
#define LOG_INFO(category, format,...)
std::string PrintShortcut(const std::vector< ImGuiKey > &keys)
ImVec4 ConvertColorToImVec4(const Color &color)
ImVec4 GetSurfaceContainerHighestVec4()
ImVec4 GetPrimaryActiveVec4()
ImVec4 GetTextDisabledVec4()
ImVec4 GetTextSecondaryVec4()
ImVec4 GetSurfaceContainerHighVec4()
constexpr ImVec2 kDefaultModalSize
ImVec4 GetPrimaryHoverVec4()
ImVec4 GetOnSurfaceVariantVec4()
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.
Main namespace for the application.
Represents a single session, containing a ROM and its associated editors.