yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
command_palette_widget.h
Go to the documentation of this file.
1#ifndef YAZE_APP_EDITOR_SYSTEM_COMMAND_PALETTE_WIDGET_H_
2#define YAZE_APP_EDITOR_SYSTEM_COMMAND_PALETTE_WIDGET_H_
3
4#include <algorithm>
5#include <string>
6#include <vector>
7
10#include "imgui/imgui.h"
11
12namespace yaze::editor {
13
14// Visual popup widget for the CommandPalette backend.
15// Invoke with Ctrl+P or programmatically via Toggle().
16// Renders a fuzzy-search input with categorized command results.
18 public:
19 explicit CommandPaletteWidget(CommandPalette* palette) : palette_(palette) {}
20
21 void SetPalette(CommandPalette* palette) { palette_ = palette; }
22
23 // Toggle visibility
24 void Toggle() { visible_ = !visible_; }
25 void Show() {
26 visible_ = true;
27 focus_input_ = true;
28 }
29 void Hide() { visible_ = false; }
30 bool IsVisible() const { return visible_; }
31
32 // Check keyboard shortcut (call every frame in the main loop)
34 if (ImGui::GetIO().KeyCtrl && ImGui::IsKeyPressed(ImGuiKey_P, false)) {
35 Toggle();
37 }
38 // Escape closes
39 if (visible_ && ImGui::IsKeyPressed(ImGuiKey_Escape, false)) {
40 Hide();
41 }
42 }
43
44 // Draw the palette popup (call every frame)
45 void Draw() {
46 if (!visible_ || !palette_)
47 return;
48
49 const ImGuiViewport* viewport = ImGui::GetMainViewport();
50 const float width = std::min(600.0f, viewport->Size.x * 0.6f);
51 const float x = viewport->Pos.x + (viewport->Size.x - width) * 0.5f;
52 const float y = viewport->Pos.y + viewport->Size.y * 0.15f;
53
54 ImGui::SetNextWindowPos(ImVec2(x, y), ImGuiCond_Always);
55 ImGui::SetNextWindowSize(ImVec2(width, 0), ImGuiCond_Always);
56 ImGui::SetNextWindowFocus();
57
58 constexpr ImGuiWindowFlags kFlags =
59 ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize |
60 ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings |
61 ImGuiWindowFlags_AlwaysAutoResize;
62
63 if (!ImGui::Begin("##CommandPalettePopup", &visible_, kFlags)) {
64 ImGui::End();
65 return;
66 }
67
68 // Search input
69 if (focus_input_) {
70 ImGui::SetKeyboardFocusHere();
71 focus_input_ = false;
72 }
73 ImGui::SetNextItemWidth(-1);
74 bool input_changed = ImGui::InputTextWithHint(
75 "##PaletteSearch", ICON_MD_SEARCH " Type to search commands...",
77
78 // Get results
79 std::string query(search_buffer_);
80 std::vector<CommandEntry> results;
81 if (query.empty()) {
82 // Show recent/frequent when no query
83 results = palette_->GetRecentCommands(15);
84 if (results.empty()) {
85 results = palette_->GetFrequentCommands(15);
86 }
87 if (results.empty()) {
88 // Show all if nothing yet
89 results = palette_->GetAllCommands();
90 if (results.size() > 20)
91 results.resize(20);
92 }
93 } else {
94 results = palette_->SearchCommands(query);
95 }
96
97 // Keyboard navigation
98 if (ImGui::IsKeyPressed(ImGuiKey_DownArrow)) {
100 std::min(selected_index_ + 1, static_cast<int>(results.size()) - 1);
101 }
102 if (ImGui::IsKeyPressed(ImGuiKey_UpArrow)) {
103 selected_index_ = std::max(selected_index_ - 1, 0);
104 }
105 if (input_changed) {
106 selected_index_ = 0; // Reset on new search
107 }
108
109 // Results list
110 ImGui::Separator();
111 const float max_height = 400.0f;
112 if (ImGui::BeginChild(
113 "##PaletteResults",
114 ImVec2(0, std::min(max_height,
115 static_cast<float>(results.size()) *
116 ImGui::GetTextLineHeightWithSpacing())),
117 false)) {
118 for (int i = 0; i < static_cast<int>(results.size()); ++i) {
119 const auto& cmd = results[i];
120 bool is_selected = (i == selected_index_);
121
122 ImGui::PushID(i);
123
124 if (ImGui::Selectable("##cmd", is_selected,
125 ImGuiSelectableFlags_SpanAvailWidth)) {
126 ExecuteCommand(cmd);
127 }
128 // Enter key executes selected
129 if (is_selected && ImGui::IsKeyPressed(ImGuiKey_Enter)) {
130 ExecuteCommand(cmd);
131 }
132
133 ImGui::SameLine(0, 0);
134
135 // Category badge
136 ImGui::TextDisabled("[%s]", cmd.category.c_str());
137 ImGui::SameLine();
138
139 // Command name
140 ImGui::TextUnformatted(cmd.name.c_str());
141
142 // Shortcut (right-aligned)
143 if (!cmd.shortcut.empty()) {
144 float shortcut_width = ImGui::CalcTextSize(cmd.shortcut.c_str()).x;
145 float avail = ImGui::GetContentRegionAvail().x;
146 if (avail > shortcut_width + 8) {
147 ImGui::SameLine(ImGui::GetWindowWidth() - shortcut_width -
148 ImGui::GetStyle().WindowPadding.x);
149 ImGui::TextDisabled("%s", cmd.shortcut.c_str());
150 }
151 }
152
153 ImGui::PopID();
154 }
155 }
156 ImGui::EndChild();
157
158 // Footer
159 ImGui::Separator();
160 ImGui::TextDisabled(ICON_MD_KEYBOARD " Enter to execute, Esc to close");
161
162 ImGui::End();
163 }
164
165 private:
166 void ExecuteCommand(const CommandEntry& cmd) {
167 if (cmd.callback) {
168 cmd.callback();
169 }
171 Hide();
172 search_buffer_[0] = '\0';
173 selected_index_ = 0;
174 }
175
177 bool visible_ = false;
178 bool focus_input_ = false;
179 char search_buffer_[256] = {};
181};
182
183} // namespace yaze::editor
184
185#endif // YAZE_APP_EDITOR_SYSTEM_COMMAND_PALETTE_WIDGET_H_
void ExecuteCommand(const CommandEntry &cmd)
void SetPalette(CommandPalette *palette)
CommandPaletteWidget(CommandPalette *palette)
std::vector< CommandEntry > SearchCommands(const std::string &query)
std::vector< CommandEntry > GetAllCommands() const
Get all registered commands.
void RecordUsage(const std::string &name)
std::vector< CommandEntry > GetRecentCommands(int limit=10)
std::vector< CommandEntry > GetFrequentCommands(int limit=10)
#define ICON_MD_SEARCH
Definition icons.h:1673
#define ICON_MD_KEYBOARD
Definition icons.h:1028
Editors are the view controllers for the application.
std::function< void()> callback