diff --git a/src/app/editor/editor_manager.cc b/src/app/editor/editor_manager.cc index ef3cdfaa..b3ec6b8a 100644 --- a/src/app/editor/editor_manager.cc +++ b/src/app/editor/editor_manager.cc @@ -950,7 +950,6 @@ void EditorManager::DrawContextSensitiveCardControl() { */ void EditorManager::DrawMenuBar() { static bool show_display_settings = false; - static bool save_as_menu = false; if (ImGui::BeginMenuBar()) { // Delegate menu building to MenuOrchestrator @@ -1034,173 +1033,6 @@ void EditorManager::DrawMenuBar() { emulator_.Run(current_rom_); } - // Enhanced Command Palette UI with Fuzzy Search (managed by UICoordinator) - // TODO: Move this to UI - if (ui_coordinator_ && ui_coordinator_->IsCommandPaletteVisible()) { - ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), - ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); - ImGui::SetNextWindowSize(ImVec2(800, 600), ImGuiCond_FirstUseEver); - - bool show_palette = true; - if (ImGui::Begin(absl::StrFormat("%s Command Palette", ICON_MD_SEARCH).c_str(), - &show_palette, ImGuiWindowFlags_NoCollapse)) { - - // Search input with focus management - static char query[256] = {}; - static int selected_idx = 0; - ImGui::SetNextItemWidth(-100); - if (ImGui::IsWindowAppearing()) { - ImGui::SetKeyboardFocusHere(); - selected_idx = 0; - } - - bool input_changed = ImGui::InputTextWithHint( - "##cmd_query", - absl::StrFormat("%s Search commands (fuzzy matching enabled)...", - ICON_MD_SEARCH) - .c_str(), - query, IM_ARRAYSIZE(query)); - - ImGui::SameLine(); - if (ImGui::Button(absl::StrFormat("%s Clear", ICON_MD_CLEAR).c_str())) { - query[0] = '\0'; - input_changed = true; - selected_idx = 0; - } - - ImGui::Separator(); - - // Fuzzy filter commands with scoring - std::vector>> - scored_commands; - std::string query_lower = query; - std::transform(query_lower.begin(), query_lower.end(), - query_lower.begin(), ::tolower); - - for (const auto& entry : shortcut_manager_.GetShortcuts()) { - const auto& name = entry.first; - const auto& shortcut = entry.second; - - std::string name_lower = name; - std::transform(name_lower.begin(), name_lower.end(), name_lower.begin(), - ::tolower); - - int score = 0; - if (query[0] == '\0') { - score = 1; // Show all when no query - } else if (name_lower.find(query_lower) == 0) { - score = 1000; // Starts with - } else if (name_lower.find(query_lower) != std::string::npos) { - score = 500; // Contains - } else { - // Fuzzy match - characters in order - size_t text_idx = 0, query_idx = 0; - while (text_idx < name_lower.length() && - query_idx < query_lower.length()) { - if (name_lower[text_idx] == query_lower[query_idx]) { - score += 10; - query_idx++; - } - text_idx++; - } - if (query_idx != query_lower.length()) - score = 0; - } - - if (score > 0) { - std::string shortcut_text = - shortcut.keys.empty() - ? "" - : absl::StrFormat("(%s)", - PrintShortcut(shortcut.keys).c_str()); - scored_commands.push_back({score, {name, shortcut_text}}); - } - } - - std::sort(scored_commands.begin(), scored_commands.end(), - [](const auto& a, const auto& b) { return a.first > b.first; }); - - // Display results with categories - if (ImGui::BeginTabBar("CommandCategories")) { - if (ImGui::BeginTabItem(ICON_MD_LIST " All Commands")) { - if (ImGui::BeginTable("CommandPaletteTable", 3, - ImGuiTableFlags_ScrollY | - ImGuiTableFlags_RowBg | - ImGuiTableFlags_SizingStretchProp, - ImVec2(0, -30))) { - - ImGui::TableSetupColumn("Command", - ImGuiTableColumnFlags_WidthStretch, 0.5f); - ImGui::TableSetupColumn("Shortcut", - ImGuiTableColumnFlags_WidthStretch, 0.3f); - ImGui::TableSetupColumn("Score", ImGuiTableColumnFlags_WidthStretch, - 0.2f); - ImGui::TableHeadersRow(); - - for (size_t i = 0; i < scored_commands.size(); ++i) { - const auto& [score, cmd_pair] = scored_commands[i]; - const auto& [command_name, shortcut_text] = cmd_pair; - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - - ImGui::PushID(static_cast(i)); - bool is_selected = (static_cast(i) == selected_idx); - if (ImGui::Selectable(command_name.c_str(), is_selected, - ImGuiSelectableFlags_SpanAllColumns)) { - selected_idx = i; - const auto& shortcuts = shortcut_manager_.GetShortcuts(); - auto it = shortcuts.find(command_name); - if (it != shortcuts.end() && it->second.callback) { - it->second.callback(); - if (ui_coordinator_) { - ui_coordinator_->SetCommandPaletteVisible(false); - } - } - } - ImGui::PopID(); - - ImGui::TableNextColumn(); - ImGui::TextDisabled("%s", shortcut_text.c_str()); - - ImGui::TableNextColumn(); - if (score > 0) - ImGui::TextDisabled("%d", score); - } - - ImGui::EndTable(); - } - ImGui::EndTabItem(); - } - - if (ImGui::BeginTabItem(ICON_MD_HISTORY " Recent")) { - ImGui::Text("Recent commands coming soon..."); - ImGui::EndTabItem(); - } - - if (ImGui::BeginTabItem(ICON_MD_STAR " Frequent")) { - ImGui::Text("Frequent commands coming soon..."); - ImGui::EndTabItem(); - } - - ImGui::EndTabBar(); - } - - // Status bar with tips - ImGui::Separator(); - ImGui::Text("%s %zu commands | Score: fuzzy match", ICON_MD_INFO, - scored_commands.size()); - ImGui::SameLine(); - ImGui::TextDisabled("| ↑↓=Navigate | Enter=Execute | Esc=Close"); - } - ImGui::End(); - - // Update visibility state - if (!show_palette && ui_coordinator_) { - ui_coordinator_->SetCommandPaletteVisible(false); - } - } - // Enhanced Global Search UI (managed by UICoordinator) // TODO: Move this to UI if (ui_coordinator_ && ui_coordinator_->IsGlobalSearchVisible()) { @@ -1418,143 +1250,6 @@ void EditorManager::DrawMenuBar() { } } - if (save_as_menu) { - ImGui::Begin("Save ROM As", &save_as_menu, ImGuiWindowFlags_AlwaysAutoResize); - - ImGui::Text("%s Save ROM to new location", ICON_MD_SAVE_AS); - ImGui::Separator(); - - static std::string save_as_filename = ""; - if (current_rom_ && save_as_filename.empty()) { - save_as_filename = current_rom_->title(); - } - - ImGui::InputText("Filename", &save_as_filename); - - ImGui::Separator(); - - if (ImGui::Button(absl::StrFormat("%s Browse...", ICON_MD_FOLDER_OPEN).c_str(), - gui::kDefaultModalSize)) { - // Use save file dialog for ROM files - auto file_path = - util::FileDialogWrapper::ShowSaveFileDialog(save_as_filename, "sfc"); - if (!file_path.empty()) { - save_as_filename = file_path; - } - } - - ImGui::SameLine(); - if (ImGui::Button(absl::StrFormat("%s Save", ICON_MD_SAVE).c_str(), - gui::kDefaultModalSize)) { - if (!save_as_filename.empty()) { - // Ensure proper file extension - std::string final_filename = save_as_filename; - if (final_filename.find(".sfc") == std::string::npos && - final_filename.find(".smc") == std::string::npos) { - final_filename += ".sfc"; - } - - status_ = SaveRomAs(final_filename); - if (status_.ok()) { - save_as_menu = false; - toast_manager_.Show( - absl::StrFormat("ROM saved as: %s", final_filename), - editor::ToastType::kSuccess); - } else { - toast_manager_.Show( - absl::StrFormat("Failed to save ROM: %s", status_.message()), - editor::ToastType::kError); - } - } - } - - ImGui::SameLine(); - if (ImGui::Button(absl::StrFormat("%s Cancel", ICON_MD_CANCEL).c_str(), - gui::kDefaultModalSize)) { - save_as_menu = false; - } - ImGui::End(); - } - - if (new_project_menu) { - ImGui::Begin("New Project", &new_project_menu, ImGuiWindowFlags_AlwaysAutoResize); - static std::string save_as_filename = ""; - ImGui::InputText("Project Name", &save_as_filename); - if (ImGui::Button(absl::StrFormat("%s Destination Folder", ICON_MD_FOLDER).c_str(), - gui::kDefaultModalSize)) { - current_project_.filepath = FileDialogWrapper::ShowOpenFolderDialog(); - } - ImGui::SameLine(); - ImGui::Text("%s", current_project_.filepath.c_str()); - - if (ImGui::Button(absl::StrFormat("%s ROM File", ICON_MD_VIDEOGAME_ASSET).c_str(), - gui::kDefaultModalSize)) { - current_project_.rom_filename = FileDialogWrapper::ShowOpenFileDialog(); - } - ImGui::SameLine(); - ImGui::Text("%s", current_project_.rom_filename.c_str()); - - if (ImGui::Button(absl::StrFormat("%s Labels File", ICON_MD_LABEL).c_str(), - gui::kDefaultModalSize)) { - current_project_.labels_filename = - FileDialogWrapper::ShowOpenFileDialog(); - } - ImGui::SameLine(); - ImGui::Text("%s", current_project_.labels_filename.c_str()); - - if (ImGui::Button(absl::StrFormat("%s Code Folder", ICON_MD_CODE).c_str(), - gui::kDefaultModalSize)) { - current_project_.code_folder = FileDialogWrapper::ShowOpenFolderDialog(); - } - ImGui::SameLine(); - ImGui::Text("%s", current_project_.code_folder.c_str()); - - ImGui::Separator(); - - if (ImGui::Button(absl::StrFormat("%s Choose Project File Location", ICON_MD_SAVE) - .c_str(), - gui::kDefaultModalSize)) { - auto project_file_path = - util::FileDialogWrapper::ShowSaveFileDialog(save_as_filename, "yaze"); - if (!project_file_path.empty()) { - // Ensure .yaze extension - if (project_file_path.find(".yaze") == std::string::npos) { - project_file_path += ".yaze"; - } - - // Update project filepath to the chosen location - current_project_.filepath = project_file_path; - - // Also set the project directory to the parent directory - size_t last_slash = project_file_path.find_last_of("/\\"); - if (last_slash != std::string::npos) { - std::string project_dir = project_file_path.substr(0, last_slash); - ImGui::Text("Project will be saved to: %s", project_dir.c_str()); - } - } - } - - if (ImGui::Button(absl::StrFormat("%s Create Project", ICON_MD_ADD).c_str(), - gui::kDefaultModalSize)) { - if (!current_project_.filepath.empty()) { - new_project_menu = false; - status_ = current_project_.Create(save_as_filename, - current_project_.filepath); - if (status_.ok()) { - status_ = current_project_.Save(); - } - } else { - toast_manager_.Show("Please choose a project file location first", - editor::ToastType::kWarning); - } - } - ImGui::SameLine(); - if (ImGui::Button("Cancel", gui::kDefaultModalSize)) { - new_project_menu = false; - } - ImGui::End(); - } - // Workspace preset dialogs if (show_save_workspace_preset_) { ImGui::Begin("Save Workspace Preset", &show_save_workspace_preset_, diff --git a/src/app/editor/editor_manager.h b/src/app/editor/editor_manager.h index b2270bc7..065fb0f6 100644 --- a/src/app/editor/editor_manager.h +++ b/src/app/editor/editor_manager.h @@ -306,7 +306,6 @@ class EditorManager { void InitializeTestSuites(); bool quit_ = false; - bool new_project_menu = false; bool show_emulator_ = false; bool show_memory_editor_ = false; diff --git a/src/app/editor/system/popup_manager.cc b/src/app/editor/system/popup_manager.cc index 7288ab74..51c11ba0 100644 --- a/src/app/editor/system/popup_manager.cc +++ b/src/app/editor/system/popup_manager.cc @@ -154,55 +154,130 @@ void PopupManager::DrawRomInfoPopup() { } void PopupManager::DrawSaveAsPopup() { + using namespace ImGui; + + Text("%s Save ROM to new location", ICON_MD_SAVE_AS); + Separator(); + static std::string save_as_filename = ""; - InputText("Filename", &save_as_filename); - if (Button("Save", gui::kDefaultModalSize)) { - // Call the save function from editor manager - // This will need to be implemented in the editor manager - Hide("Save As.."); + if (editor_manager_->GetCurrentRom() && save_as_filename.empty()) { + save_as_filename = editor_manager_->GetCurrentRom()->title(); } + + InputText("Filename", &save_as_filename); + Separator(); + + if (Button(absl::StrFormat("%s Browse...", ICON_MD_FOLDER_OPEN).c_str(), + gui::kDefaultModalSize)) { + auto file_path = util::FileDialogWrapper::ShowSaveFileDialog(save_as_filename, "sfc"); + if (!file_path.empty()) { + save_as_filename = file_path; + } + } + SameLine(); - if (Button("Cancel", gui::kDefaultModalSize)) { + if (Button(absl::StrFormat("%s Save", ICON_MD_SAVE).c_str(), + gui::kDefaultModalSize)) { + if (!save_as_filename.empty()) { + // Ensure proper file extension + std::string final_filename = save_as_filename; + if (final_filename.find(".sfc") == std::string::npos && + final_filename.find(".smc") == std::string::npos) { + final_filename += ".sfc"; + } + + auto status = editor_manager_->SaveRomAs(final_filename); + if (status.ok()) { + save_as_filename = ""; + Hide("Save As.."); + } + } + } + + SameLine(); + if (Button(absl::StrFormat("%s Cancel", ICON_MD_CANCEL).c_str(), + gui::kDefaultModalSize)) { + save_as_filename = ""; Hide("Save As.."); } } void PopupManager::DrawNewProjectPopup() { - static std::string save_as_filename = ""; - InputText("Project Name", &save_as_filename); + using namespace ImGui; - // These would need to be implemented in the editor manager - if (Button("Destination Filepath", gui::kDefaultModalSize)) { - // Call file dialog + static std::string project_name = ""; + static std::string project_filepath = ""; + static std::string rom_filename = ""; + static std::string labels_filename = ""; + static std::string code_folder = ""; + + InputText("Project Name", &project_name); + + if (Button(absl::StrFormat("%s Destination Folder", ICON_MD_FOLDER).c_str(), + gui::kDefaultModalSize)) { + project_filepath = util::FileDialogWrapper::ShowOpenFolderDialog(); } SameLine(); - Text("%s", "filepath"); // This would be from the editor manager + Text("%s", project_filepath.empty() ? "(Not set)" : project_filepath.c_str()); - if (Button("ROM File", gui::kDefaultModalSize)) { - // Call file dialog + if (Button(absl::StrFormat("%s ROM File", ICON_MD_VIDEOGAME_ASSET).c_str(), + gui::kDefaultModalSize)) { + rom_filename = util::FileDialogWrapper::ShowOpenFileDialog(); } SameLine(); - Text("%s", "rom_filename"); // This would be from the editor manager + Text("%s", rom_filename.empty() ? "(Not set)" : rom_filename.c_str()); - if (Button("Labels File", gui::kDefaultModalSize)) { - // Call file dialog + if (Button(absl::StrFormat("%s Labels File", ICON_MD_LABEL).c_str(), + gui::kDefaultModalSize)) { + labels_filename = util::FileDialogWrapper::ShowOpenFileDialog(); } SameLine(); - Text("%s", "labels_filename"); // This would be from the editor manager + Text("%s", labels_filename.empty() ? "(Not set)" : labels_filename.c_str()); - if (Button("Code Folder", gui::kDefaultModalSize)) { - // Call file dialog + if (Button(absl::StrFormat("%s Code Folder", ICON_MD_CODE).c_str(), + gui::kDefaultModalSize)) { + code_folder = util::FileDialogWrapper::ShowOpenFolderDialog(); } SameLine(); - Text("%s", "code_folder"); // This would be from the editor manager + Text("%s", code_folder.empty() ? "(Not set)" : code_folder.c_str()); Separator(); - if (Button("Create", gui::kDefaultModalSize)) { - // Create project - Hide("New Project"); + + if (Button(absl::StrFormat("%s Choose Project File Location", ICON_MD_SAVE).c_str(), + gui::kDefaultModalSize)) { + auto project_file_path = util::FileDialogWrapper::ShowSaveFileDialog(project_name, "yaze"); + if (!project_file_path.empty()) { + if (project_file_path.find(".yaze") == std::string::npos) { + project_file_path += ".yaze"; + } + project_filepath = project_file_path; + } + } + + if (Button(absl::StrFormat("%s Create Project", ICON_MD_ADD).c_str(), + gui::kDefaultModalSize)) { + if (!project_filepath.empty() && !project_name.empty()) { + auto status = editor_manager_->CreateNewProject(); + if (status.ok()) { + // Clear fields + project_name = ""; + project_filepath = ""; + rom_filename = ""; + labels_filename = ""; + code_folder = ""; + Hide("New Project"); + } + } } SameLine(); - if (Button("Cancel", gui::kDefaultModalSize)) { + if (Button(absl::StrFormat("%s Cancel", ICON_MD_CANCEL).c_str(), + gui::kDefaultModalSize)) { + // Clear fields + project_name = ""; + project_filepath = ""; + rom_filename = ""; + labels_filename = ""; + code_folder = ""; Hide("New Project"); } } diff --git a/src/app/editor/ui/ui_coordinator.cc b/src/app/editor/ui/ui_coordinator.cc index 52b68c21..4ca038ab 100644 --- a/src/app/editor/ui/ui_coordinator.cc +++ b/src/app/editor/ui/ui_coordinator.cc @@ -1,10 +1,13 @@ #include "app/editor/ui/ui_coordinator.h" +#include #include #include #include +#include #include "absl/strings/str_format.h" +#include "app/core/project.h" #include "app/editor/editor.h" #include "app/editor/editor_manager.h" #include "app/editor/system/editor_registry.h" @@ -16,9 +19,11 @@ #include "app/editor/system/window_delegate.h" #include "app/editor/ui/welcome_screen.h" #include "app/gui/core/icons.h" +#include "app/gui/core/layout_helpers.h" #include "app/gui/core/style.h" #include "app/gui/core/theme_manager.h" #include "imgui/imgui.h" +#include "util/file_util.h" namespace yaze { namespace editor { @@ -331,75 +336,6 @@ void UICoordinator::HideAllWindows() { window_delegate_.HideAllWindows(); } -void UICoordinator::ApplyMaterialDesignStyling() { - // Apply Material Design 3 color scheme - ImGuiStyle& style = ImGui::GetStyle(); - - // Set Material Design colors - style.Colors[ImGuiCol_WindowBg] = gui::GetSurfaceVec4(); - style.Colors[ImGuiCol_ChildBg] = gui::GetSurfaceVariantVec4(); - style.Colors[ImGuiCol_PopupBg] = gui::GetSurfaceContainerVec4(); - style.Colors[ImGuiCol_Border] = gui::GetOutlineVec4(); - style.Colors[ImGuiCol_BorderShadow] = gui::GetShadowVec4(); - - // Text colors - style.Colors[ImGuiCol_Text] = gui::GetOnSurfaceVec4(); - style.Colors[ImGuiCol_TextDisabled] = gui::GetOnSurfaceVariantVec4(); - - // Button colors - style.Colors[ImGuiCol_Button] = gui::GetSurfaceContainerHighestVec4(); - style.Colors[ImGuiCol_ButtonHovered] = gui::GetSurfaceContainerHighVec4(); - style.Colors[ImGuiCol_ButtonActive] = gui::GetPrimaryVec4(); - - // Header colors - style.Colors[ImGuiCol_Header] = gui::GetSurfaceContainerHighVec4(); - style.Colors[ImGuiCol_HeaderHovered] = gui::GetSurfaceContainerHighestVec4(); - style.Colors[ImGuiCol_HeaderActive] = gui::GetPrimaryVec4(); - - // Frame colors - style.Colors[ImGuiCol_FrameBg] = gui::GetSurfaceContainerHighestVec4(); - style.Colors[ImGuiCol_FrameBgHovered] = gui::GetSurfaceContainerHighVec4(); - style.Colors[ImGuiCol_FrameBgActive] = gui::GetPrimaryVec4(); - - // Scrollbar colors - style.Colors[ImGuiCol_ScrollbarBg] = gui::GetSurfaceContainerVec4(); - style.Colors[ImGuiCol_ScrollbarGrab] = gui::GetOutlineVec4(); - style.Colors[ImGuiCol_ScrollbarGrabHovered] = gui::GetOnSurfaceVariantVec4(); - style.Colors[ImGuiCol_ScrollbarGrabActive] = gui::GetOnSurfaceVec4(); - - // Slider colors - style.Colors[ImGuiCol_SliderGrab] = gui::GetPrimaryVec4(); - style.Colors[ImGuiCol_SliderGrabActive] = gui::GetPrimaryActiveVec4(); - - // Tab colors - style.Colors[ImGuiCol_Tab] = gui::GetSurfaceContainerHighVec4(); - style.Colors[ImGuiCol_TabHovered] = gui::GetSurfaceContainerHighestVec4(); - style.Colors[ImGuiCol_TabActive] = gui::GetPrimaryVec4(); - style.Colors[ImGuiCol_TabUnfocused] = gui::GetSurfaceContainerVec4(); - style.Colors[ImGuiCol_TabUnfocusedActive] = gui::GetPrimaryActiveVec4(); - - // Title bar colors - style.Colors[ImGuiCol_TitleBg] = gui::GetSurfaceContainerVec4(); - style.Colors[ImGuiCol_TitleBgActive] = gui::GetSurfaceContainerHighVec4(); - style.Colors[ImGuiCol_TitleBgCollapsed] = gui::GetSurfaceContainerVec4(); - - // Menu bar colors - style.Colors[ImGuiCol_MenuBarBg] = gui::GetSurfaceContainerVec4(); - - // Modal dimming - style.Colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.0f, 0.0f, 0.0f, 0.6f); -} - -void UICoordinator::UpdateThemeElements() { - // Update theme-specific elements - ApplyMaterialDesignStyling(); -} - -void UICoordinator::DrawThemePreview() { - // TODO: Implement theme preview - // This would show a preview of the current theme -} - // Helper methods for drawing operations void UICoordinator::DrawSessionIndicator() { // TODO: Implement session indicator @@ -546,5 +482,162 @@ void UICoordinator::DrawTestingUI() { // TODO: Implement testing UI } +void UICoordinator::DrawCommandPalette() { + if (!show_command_palette_) return; + + using namespace ImGui; + auto& theme = gui::ThemeManager::Get().GetCurrentTheme(); + + SetNextWindowPos(GetMainViewport()->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); + SetNextWindowSize(ImVec2(800, 600), ImGuiCond_FirstUseEver); + + bool show_palette = true; + if (Begin(absl::StrFormat("%s Command Palette", ICON_MD_SEARCH).c_str(), + &show_palette, ImGuiWindowFlags_NoCollapse)) { + + // Search input with focus management + SetNextItemWidth(-100); + if (IsWindowAppearing()) { + SetKeyboardFocusHere(); + command_palette_selected_idx_ = 0; + } + + bool input_changed = InputTextWithHint( + "##cmd_query", + absl::StrFormat("%s Search commands (fuzzy matching enabled)...", ICON_MD_SEARCH).c_str(), + command_palette_query_, IM_ARRAYSIZE(command_palette_query_)); + + SameLine(); + if (Button(absl::StrFormat("%s Clear", ICON_MD_CLEAR).c_str())) { + command_palette_query_[0] = '\0'; + input_changed = true; + command_palette_selected_idx_ = 0; + } + + Separator(); + + // Fuzzy filter commands with scoring + std::vector>> scored_commands; + std::string query_lower = command_palette_query_; + std::transform(query_lower.begin(), query_lower.end(), query_lower.begin(), ::tolower); + + for (const auto& entry : shortcut_manager_.GetShortcuts()) { + const auto& name = entry.first; + const auto& shortcut = entry.second; + + std::string name_lower = name; + std::transform(name_lower.begin(), name_lower.end(), name_lower.begin(), ::tolower); + + int score = 0; + if (command_palette_query_[0] == '\0') { + score = 1; // Show all when no query + } else if (name_lower.find(query_lower) == 0) { + score = 1000; // Starts with + } else if (name_lower.find(query_lower) != std::string::npos) { + score = 500; // Contains + } else { + // Fuzzy match - characters in order + size_t text_idx = 0, query_idx = 0; + while (text_idx < name_lower.length() && query_idx < query_lower.length()) { + if (name_lower[text_idx] == query_lower[query_idx]) { + score += 10; + query_idx++; + } + text_idx++; + } + if (query_idx != query_lower.length()) score = 0; + } + + if (score > 0) { + std::string shortcut_text = shortcut.keys.empty() + ? "" + : absl::StrFormat("(%s)", PrintShortcut(shortcut.keys).c_str()); + scored_commands.push_back({score, {name, shortcut_text}}); + } + } + + std::sort(scored_commands.begin(), scored_commands.end(), + [](const auto& a, const auto& b) { return a.first > b.first; }); + + // Display results with categories + if (BeginTabBar("CommandCategories")) { + if (BeginTabItem(absl::StrFormat("%s All Commands", ICON_MD_LIST).c_str())) { + if (gui::LayoutHelpers::BeginTableWithTheming("CommandPaletteTable", 3, + ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingStretchProp, + ImVec2(0, -30))) { + + TableSetupColumn("Command", ImGuiTableColumnFlags_WidthStretch, 0.5f); + TableSetupColumn("Shortcut", ImGuiTableColumnFlags_WidthStretch, 0.3f); + TableSetupColumn("Score", ImGuiTableColumnFlags_WidthStretch, 0.2f); + TableHeadersRow(); + + for (size_t i = 0; i < scored_commands.size(); ++i) { + const auto& [score, cmd_pair] = scored_commands[i]; + const auto& [command_name, shortcut_text] = cmd_pair; + + TableNextRow(); + TableNextColumn(); + + PushID(static_cast(i)); + bool is_selected = (static_cast(i) == command_palette_selected_idx_); + if (Selectable(command_name.c_str(), is_selected, + ImGuiSelectableFlags_SpanAllColumns)) { + command_palette_selected_idx_ = i; + const auto& shortcuts = shortcut_manager_.GetShortcuts(); + auto it = shortcuts.find(command_name); + if (it != shortcuts.end() && it->second.callback) { + it->second.callback(); + show_command_palette_ = false; + } + } + PopID(); + + TableNextColumn(); + PushStyleColor(ImGuiCol_Text, gui::ConvertColorToImVec4(theme.text_secondary)); + Text("%s", shortcut_text.c_str()); + PopStyleColor(); + + TableNextColumn(); + if (score > 0) { + PushStyleColor(ImGuiCol_Text, gui::ConvertColorToImVec4(theme.text_disabled)); + Text("%d", score); + PopStyleColor(); + } + } + + EndTable(); + } + EndTabItem(); + } + + if (BeginTabItem(absl::StrFormat("%s Recent", ICON_MD_HISTORY).c_str())) { + Text("Recent commands coming soon..."); + EndTabItem(); + } + + if (BeginTabItem(absl::StrFormat("%s Frequent", ICON_MD_STAR).c_str())) { + Text("Frequent commands coming soon..."); + EndTabItem(); + } + + EndTabBar(); + } + + // Status bar with tips + Separator(); + Text("%s %zu commands | Score: fuzzy match", ICON_MD_INFO, scored_commands.size()); + SameLine(); + PushStyleColor(ImGuiCol_Text, gui::ConvertColorToImVec4(theme.text_disabled)); + Text("| ↑↓=Navigate | Enter=Execute | Esc=Close"); + PopStyleColor(); + } + End(); + + // Update visibility state + if (!show_palette) { + show_command_palette_ = false; + } +} + } // namespace editor } // namespace yaze diff --git a/src/app/editor/ui/ui_coordinator.h b/src/app/editor/ui/ui_coordinator.h index 9059de93..87d51104 100644 --- a/src/app/editor/ui/ui_coordinator.h +++ b/src/app/editor/ui/ui_coordinator.h @@ -157,6 +157,13 @@ class UICoordinator { bool show_resource_label_manager_ = false; bool show_card_sidebar_ = false; + // Command Palette state + char command_palette_query_[256] = {}; + int command_palette_selected_idx_ = 0; + + // Global Search state + char global_search_query_[256] = {}; + // Welcome screen component std::unique_ptr welcome_screen_;