refactor(editor): streamline command palette and project management UI
- Removed the save as menu and new project menu from EditorManager, delegating their functionality to PopupManager for better separation of concerns. - Introduced a command palette in UICoordinator, enhancing user experience with fuzzy search capabilities for commands. - Updated UI components to improve clarity and maintainability, ensuring a more efficient workflow for users. Benefits: - Enhances the organization of UI elements, leading to a more intuitive user experience. - Improves code maintainability by consolidating UI management responsibilities within dedicated components.
This commit is contained in:
@@ -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<std::pair<int, std::pair<std::string, std::string>>>
|
||||
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<int>(i));
|
||||
bool is_selected = (static_cast<int>(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_,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
#include "app/editor/ui/ui_coordinator.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#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<std::pair<int, std::pair<std::string, std::string>>> 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<int>(i));
|
||||
bool is_selected = (static_cast<int>(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
|
||||
|
||||
@@ -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<WelcomeScreen> welcome_screen_;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user