yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
keyboard_shortcuts.cc
Go to the documentation of this file.
1// SPDX-License-Identifier: MIT
2// Implementation of keyboard shortcut overlay system for yaze
3
5
6#include <algorithm>
7#include <cstring>
8
12#include "imgui/imgui.h"
13
14namespace yaze {
15namespace gui {
16
17namespace {
18
19// Key name lookup table (local to this file for Shortcut::GetDisplayString)
20// Note: gui::GetKeyName from platform_keys.h is the canonical version
21constexpr struct {
22 ImGuiKey key;
23 const char* name;
24} kLocalKeyNames[] = {
25 {ImGuiKey_Tab, "Tab"},
26 {ImGuiKey_LeftArrow, "Left"},
27 {ImGuiKey_RightArrow, "Right"},
28 {ImGuiKey_UpArrow, "Up"},
29 {ImGuiKey_DownArrow, "Down"},
30 {ImGuiKey_PageUp, "PageUp"},
31 {ImGuiKey_PageDown, "PageDown"},
32 {ImGuiKey_Home, "Home"},
33 {ImGuiKey_End, "End"},
34 {ImGuiKey_Insert, "Insert"},
35 {ImGuiKey_Delete, "Delete"},
36 {ImGuiKey_Backspace, "Backspace"},
37 {ImGuiKey_Space, "Space"},
38 {ImGuiKey_Enter, "Enter"},
39 {ImGuiKey_Escape, "Escape"},
40 {ImGuiKey_A, "A"},
41 {ImGuiKey_B, "B"},
42 {ImGuiKey_C, "C"},
43 {ImGuiKey_D, "D"},
44 {ImGuiKey_E, "E"},
45 {ImGuiKey_F, "F"},
46 {ImGuiKey_G, "G"},
47 {ImGuiKey_H, "H"},
48 {ImGuiKey_I, "I"},
49 {ImGuiKey_J, "J"},
50 {ImGuiKey_K, "K"},
51 {ImGuiKey_L, "L"},
52 {ImGuiKey_M, "M"},
53 {ImGuiKey_N, "N"},
54 {ImGuiKey_O, "O"},
55 {ImGuiKey_P, "P"},
56 {ImGuiKey_Q, "Q"},
57 {ImGuiKey_R, "R"},
58 {ImGuiKey_S, "S"},
59 {ImGuiKey_T, "T"},
60 {ImGuiKey_U, "U"},
61 {ImGuiKey_V, "V"},
62 {ImGuiKey_W, "W"},
63 {ImGuiKey_X, "X"},
64 {ImGuiKey_Y, "Y"},
65 {ImGuiKey_Z, "Z"},
66 {ImGuiKey_0, "0"},
67 {ImGuiKey_1, "1"},
68 {ImGuiKey_2, "2"},
69 {ImGuiKey_3, "3"},
70 {ImGuiKey_4, "4"},
71 {ImGuiKey_5, "5"},
72 {ImGuiKey_6, "6"},
73 {ImGuiKey_7, "7"},
74 {ImGuiKey_8, "8"},
75 {ImGuiKey_9, "9"},
76 {ImGuiKey_F1, "F1"},
77 {ImGuiKey_F2, "F2"},
78 {ImGuiKey_F3, "F3"},
79 {ImGuiKey_F4, "F4"},
80 {ImGuiKey_F5, "F5"},
81 {ImGuiKey_F6, "F6"},
82 {ImGuiKey_F7, "F7"},
83 {ImGuiKey_F8, "F8"},
84 {ImGuiKey_F9, "F9"},
85 {ImGuiKey_F10, "F10"},
86 {ImGuiKey_F11, "F11"},
87 {ImGuiKey_F12, "F12"},
88 {ImGuiKey_Minus, "-"},
89 {ImGuiKey_Equal, "="},
90 {ImGuiKey_LeftBracket, "["},
91 {ImGuiKey_RightBracket, "]"},
92 {ImGuiKey_Backslash, "\\"},
93 {ImGuiKey_Semicolon, ";"},
94 {ImGuiKey_Apostrophe, "'"},
95 {ImGuiKey_Comma, ","},
96 {ImGuiKey_Period, "."},
97 {ImGuiKey_Slash, "/"},
98 {ImGuiKey_GraveAccent, "`"},
99};
100
101const char* GetLocalKeyName(ImGuiKey key) {
102 for (const auto& entry : kLocalKeyNames) {
103 if (entry.key == key) {
104 return entry.name;
105 }
106 }
107 return "?";
108}
109
110} // namespace
111
112std::string Shortcut::GetDisplayString() const {
113 std::string result;
114
115 // Use runtime platform detection for correct modifier names
116 // This handles native macOS, WASM on Mac browsers, and Windows/Linux
117 if (requires_ctrl) {
118 result += GetCtrlDisplayName();
119 result += "+";
120 }
121 if (requires_alt) {
122 result += GetAltDisplayName();
123 result += "+";
124 }
125 if (requires_shift) {
126 result += "Shift+";
127 }
128
129 // Use platform_keys.h GetKeyName for consistent key name formatting
130 result += gui::GetKeyName(key);
131 return result;
132}
133
134bool Shortcut::Matches(ImGuiKey pressed_key, bool ctrl, bool shift,
135 bool alt) const {
136 if (!enabled) return false;
137 if (pressed_key != key) return false;
138 if (ctrl != requires_ctrl) return false;
139 if (shift != requires_shift) return false;
140 if (alt != requires_alt) return false;
141 return true;
142}
143
145 static KeyboardShortcuts instance;
146 return instance;
147}
148
150 shortcuts_[shortcut.id] = shortcut;
151}
152
153void KeyboardShortcuts::RegisterShortcut(const std::string& id,
154 const std::string& description,
155 ImGuiKey key, bool ctrl, bool shift,
156 bool alt, const std::string& category,
157 ShortcutContext context,
158 std::function<void()> action) {
159 Shortcut shortcut;
160 shortcut.id = id;
161 shortcut.description = description;
162 shortcut.key = key;
163 shortcut.requires_ctrl = ctrl;
164 shortcut.requires_shift = shift;
165 shortcut.requires_alt = alt;
166 shortcut.category = category;
167 shortcut.context = context;
168 shortcut.action = action;
169 shortcut.enabled = true;
170 RegisterShortcut(shortcut);
171}
172
173void KeyboardShortcuts::UnregisterShortcut(const std::string& id) {
174 shortcuts_.erase(id);
175}
176
177void KeyboardShortcuts::SetShortcutEnabled(const std::string& id,
178 bool enabled) {
179 auto it = shortcuts_.find(id);
180 if (it != shortcuts_.end()) {
181 it->second.enabled = enabled;
182 }
183}
184
186 // Check if any ImGui input widget is active (text fields, etc.)
187 // This prevents shortcuts from triggering while the user is typing
188 return ImGui::GetIO().WantTextInput;
189}
190
192 // Don't process shortcuts when text input is active
193 if (IsTextInputActive()) {
195 return;
196 }
197
198 const auto& io = ImGui::GetIO();
199 bool ctrl = io.KeyCtrl;
200 bool shift = io.KeyShift;
201 bool alt = io.KeyAlt;
202
203 // Handle '?' key to toggle overlay (Shift + /)
204 // Note: '?' is typically Shift+Slash on US keyboards
205 if (ImGui::IsKeyPressed(ImGuiKey_Slash) && shift && !ctrl && !alt) {
209 }
210 } else if (!ImGui::IsKeyPressed(ImGuiKey_Slash)) {
212 }
213
214 // Close overlay with Escape
215 if (show_overlay_ && ImGui::IsKeyPressed(ImGuiKey_Escape)) {
216 HideOverlay();
217 return;
218 }
219
220 // Don't process other shortcuts while overlay is shown
221 if (show_overlay_) {
222 return;
223 }
224
225 // Check all registered shortcuts
226 for (const auto& [id, shortcut] : shortcuts_) {
227 if (!shortcut.enabled) continue;
228 if (!IsShortcutActiveInContext(shortcut)) continue;
229
230 // Check if this shortcut's key is pressed
231 if (ImGui::IsKeyPressed(shortcut.key, false)) {
232 if (shortcut.Matches(shortcut.key, ctrl, shift, alt)) {
233 if (shortcut.action) {
234 shortcut.action();
235 }
236 }
237 }
238 }
239}
240
243 if (show_overlay_) {
244 // Clear search filter when opening
245 search_filter_[0] = '\0';
246 }
247}
248
250 if (!show_overlay_) return;
251
252 const auto& theme = ThemeManager::Get().GetCurrentTheme();
253
254 // Semi-transparent fullscreen background
255 ImGuiIO& io = ImGui::GetIO();
256 ImGui::SetNextWindowPos(ImVec2(0, 0));
257 ImGui::SetNextWindowSize(io.DisplaySize);
258 ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.0f, 0.0f, 0.0f, 0.7f));
259 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
260 ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
261
262 ImGuiWindowFlags overlay_flags = ImGuiWindowFlags_NoTitleBar |
263 ImGuiWindowFlags_NoResize |
264 ImGuiWindowFlags_NoMove |
265 ImGuiWindowFlags_NoScrollbar |
266 ImGuiWindowFlags_NoSavedSettings |
267 ImGuiWindowFlags_NoBringToFrontOnFocus;
268
269 if (ImGui::Begin("##ShortcutOverlayBg", nullptr, overlay_flags)) {
270 // Close on click outside modal
271 if (ImGui::IsWindowHovered() && ImGui::IsMouseClicked(0)) {
272 ImVec2 mouse = ImGui::GetMousePos();
273 ImVec2 modal_pos =
274 ImVec2((io.DisplaySize.x - 600) * 0.5f, (io.DisplaySize.y - 500) * 0.5f);
275 ImVec2 modal_size = ImVec2(600, 500);
276
277 if (mouse.x < modal_pos.x || mouse.x > modal_pos.x + modal_size.x ||
278 mouse.y < modal_pos.y || mouse.y > modal_pos.y + modal_size.y) {
279 HideOverlay();
280 }
281 }
282 }
283 ImGui::End();
284 ImGui::PopStyleVar(2);
285 ImGui::PopStyleColor();
286
287 // Draw the centered modal window
289}
290
292 const auto& theme = ThemeManager::Get().GetCurrentTheme();
293 ImGuiIO& io = ImGui::GetIO();
294
295 // Calculate centered position
296 float modal_width = 600.0f;
297 float modal_height = 500.0f;
298 ImVec2 modal_pos((io.DisplaySize.x - modal_width) * 0.5f,
299 (io.DisplaySize.y - modal_height) * 0.5f);
300
301 ImGui::SetNextWindowPos(modal_pos);
302 ImGui::SetNextWindowSize(ImVec2(modal_width, modal_height));
303
304 // Style the modal window
305 ImGui::PushStyleColor(ImGuiCol_WindowBg, ConvertColorToImVec4(theme.popup_bg));
306 ImGui::PushStyleColor(ImGuiCol_Border, ConvertColorToImVec4(theme.border));
307 ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 8.0f);
308 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(20, 16));
309 ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f);
310
311 ImGuiWindowFlags modal_flags = ImGuiWindowFlags_NoTitleBar |
312 ImGuiWindowFlags_NoResize |
313 ImGuiWindowFlags_NoMove |
314 ImGuiWindowFlags_NoCollapse |
315 ImGuiWindowFlags_NoSavedSettings;
316
317 if (ImGui::Begin("##ShortcutOverlay", nullptr, modal_flags)) {
318 // Header
319 ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[0]); // Use default font
320 ImGui::TextColored(ConvertColorToImVec4(theme.accent), "Keyboard Shortcuts");
321 ImGui::PopFont();
322
323 ImGui::SameLine(modal_width - 60);
324 if (ImGui::SmallButton("X##CloseOverlay")) {
325 HideOverlay();
326 }
327 if (ImGui::IsItemHovered()) {
328 ImGui::SetTooltip("Close (Escape)");
329 }
330
331 ImGui::Separator();
332 ImGui::Spacing();
333
334 // Search filter
335 ImGui::SetNextItemWidth(modal_width - 40);
336 ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 4.0f);
337 ImGui::InputTextWithHint("##ShortcutSearch", "Search shortcuts...",
339 ImGui::PopStyleVar();
340
341 // Context indicator
342 ImGui::SameLine();
343 ImGui::TextDisabled("(%s)", ShortcutContextToString(current_context_));
344
345 ImGui::Spacing();
346 ImGui::Separator();
347 ImGui::Spacing();
348
349 // Scrollable content area
350 ImGui::BeginChild("##ShortcutList", ImVec2(0, -30), false,
351 ImGuiWindowFlags_AlwaysVerticalScrollbar);
352
353 // Collect shortcuts by category
354 std::map<std::string, std::vector<const Shortcut*>> shortcuts_by_category;
355 std::string filter_lower;
356 for (char c : std::string(search_filter_)) {
357 filter_lower += static_cast<char>(std::tolower(c));
358 }
359
360 for (const auto& [id, shortcut] : shortcuts_) {
361 // Apply search filter
362 if (!filter_lower.empty()) {
363 std::string desc_lower;
364 for (char c : shortcut.description) {
365 desc_lower += static_cast<char>(std::tolower(c));
366 }
367 std::string cat_lower;
368 for (char c : shortcut.category) {
369 cat_lower += static_cast<char>(std::tolower(c));
370 }
371 std::string key_lower;
372 for (char c : shortcut.GetDisplayString()) {
373 key_lower += static_cast<char>(std::tolower(c));
374 }
375
376 if (desc_lower.find(filter_lower) == std::string::npos &&
377 cat_lower.find(filter_lower) == std::string::npos &&
378 key_lower.find(filter_lower) == std::string::npos) {
379 continue;
380 }
381 }
382
383 shortcuts_by_category[shortcut.category].push_back(&shortcut);
384 }
385
386 // Display shortcuts by category in order
387 for (const auto& category : category_order_) {
388 auto it = shortcuts_by_category.find(category);
389 if (it != shortcuts_by_category.end() && !it->second.empty()) {
390 DrawCategorySection(category, it->second);
391 }
392 }
393
394 // Display any categories not in the order list
395 for (const auto& [category, shortcuts] : shortcuts_by_category) {
396 bool in_order = false;
397 for (const auto& ordered : category_order_) {
398 if (ordered == category) {
399 in_order = true;
400 break;
401 }
402 }
403 if (!in_order && !shortcuts.empty()) {
404 DrawCategorySection(category, shortcuts);
405 }
406 }
407
408 ImGui::EndChild();
409
410 // Footer
411 ImGui::Separator();
412 ImGui::TextDisabled("Press ? to toggle | Escape to close");
413 }
414 ImGui::End();
415
416 ImGui::PopStyleVar(3);
417 ImGui::PopStyleColor(2);
418}
419
421 const std::string& category,
422 const std::vector<const Shortcut*>& shortcuts) {
423 const auto& theme = ThemeManager::Get().GetCurrentTheme();
424 auto header_bg = ConvertColorToImVec4(theme.header);
425
426 // Category header with collapsible behavior
427 ImGui::PushStyleColor(ImGuiCol_Header, header_bg);
428 ImGui::PushStyleColor(ImGuiCol_HeaderHovered,
429 ImVec4(header_bg.x + 0.05f,
430 header_bg.y + 0.05f,
431 header_bg.z + 0.05f, 1.0f));
432
433 bool is_open = ImGui::CollapsingHeader(
434 category.c_str(), ImGuiTreeNodeFlags_DefaultOpen);
435
436 ImGui::PopStyleColor(2);
437
438 if (is_open) {
439 ImGui::Indent(10.0f);
440
441 // Table for shortcuts
442 if (ImGui::BeginTable("##ShortcutTable", 3,
443 ImGuiTableFlags_SizingStretchProp |
444 ImGuiTableFlags_RowBg)) {
445 ImGui::TableSetupColumn("Shortcut", ImGuiTableColumnFlags_WidthFixed, 120.0f);
446 ImGui::TableSetupColumn("Description", ImGuiTableColumnFlags_WidthStretch);
447 ImGui::TableSetupColumn("Context", ImGuiTableColumnFlags_WidthFixed, 80.0f);
448
449 for (const auto* shortcut : shortcuts) {
450 DrawShortcutRow(*shortcut);
451 }
452
453 ImGui::EndTable();
454 }
455
456 ImGui::Unindent(10.0f);
457 ImGui::Spacing();
458 }
459}
460
462 const auto& theme = ThemeManager::Get().GetCurrentTheme();
463
464 ImGui::TableNextRow();
465
466 // Shortcut key combination
467 ImGui::TableNextColumn();
468 bool is_active = IsShortcutActiveInContext(shortcut);
469
470 // Draw keyboard shortcut badge
471 ImGui::PushStyleColor(ImGuiCol_Button,
472 is_active ? ImVec4(0.2f, 0.3f, 0.4f, 0.8f)
473 : ImVec4(0.15f, 0.15f, 0.15f, 0.6f));
474 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
475 is_active ? ImVec4(0.25f, 0.35f, 0.45f, 0.9f)
476 : ImVec4(0.2f, 0.2f, 0.2f, 0.7f));
477 ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 4.0f);
478 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6, 2));
479
480 std::string display = shortcut.GetDisplayString();
481 ImGui::SmallButton(display.c_str());
482
483 ImGui::PopStyleVar(2);
484 ImGui::PopStyleColor(2);
485
486 // Description
487 ImGui::TableNextColumn();
488 ImVec4 text_color = is_active ? ConvertColorToImVec4(theme.text_primary)
489 : ConvertColorToImVec4(theme.text_secondary);
490 ImGui::TextColored(text_color, "%s", shortcut.description.c_str());
491
492 // Context indicator
493 ImGui::TableNextColumn();
494 if (shortcut.context != ShortcutContext::kGlobal) {
495 ImGui::TextDisabled("%s", ShortcutContextToString(shortcut.context));
496 }
497}
498
500 const Shortcut& shortcut) const {
501 if (shortcut.context == ShortcutContext::kGlobal) {
502 return true;
503 }
504 return shortcut.context == current_context_;
505}
506
507std::vector<const Shortcut*> KeyboardShortcuts::GetShortcutsInCategory(
508 const std::string& category) const {
509 std::vector<const Shortcut*> result;
510 for (const auto& [id, shortcut] : shortcuts_) {
511 if (shortcut.category == category) {
512 result.push_back(&shortcut);
513 }
514 }
515 return result;
516}
517
518std::vector<const Shortcut*> KeyboardShortcuts::GetContextShortcuts() const {
519 std::vector<const Shortcut*> result;
520 for (const auto& [id, shortcut] : shortcuts_) {
521 if (IsShortcutActiveInContext(shortcut)) {
522 result.push_back(&shortcut);
523 }
524 }
525 return result;
526}
527
528std::vector<std::string> KeyboardShortcuts::GetCategories() const {
529 std::vector<std::string> result;
530 for (const auto& [id, shortcut] : shortcuts_) {
531 bool found = false;
532 for (const auto& cat : result) {
533 if (cat == shortcut.category) {
534 found = true;
535 break;
536 }
537 }
538 if (!found) {
539 result.push_back(shortcut.category);
540 }
541 }
542 return result;
543}
544
546 std::function<void()> open_callback,
547 std::function<void()> save_callback,
548 std::function<void()> save_as_callback,
549 std::function<void()> close_callback,
550 std::function<void()> undo_callback,
551 std::function<void()> redo_callback,
552 std::function<void()> copy_callback,
553 std::function<void()> paste_callback,
554 std::function<void()> cut_callback,
555 std::function<void()> find_callback) {
556
557 // === File Shortcuts ===
558 if (open_callback) {
559 RegisterShortcut("file.open", "Open ROM/Project", ImGuiKey_O,
560 true, false, false, "File", ShortcutContext::kGlobal,
561 open_callback);
562 }
563
564 if (save_callback) {
565 RegisterShortcut("file.save", "Save", ImGuiKey_S,
566 true, false, false, "File", ShortcutContext::kGlobal,
567 save_callback);
568 }
569
570 if (save_as_callback) {
571 RegisterShortcut("file.save_as", "Save As...", ImGuiKey_S,
572 true, true, false, "File", ShortcutContext::kGlobal,
573 save_as_callback);
574 }
575
576 if (close_callback) {
577 RegisterShortcut("file.close", "Close", ImGuiKey_W,
578 true, false, false, "File", ShortcutContext::kGlobal,
579 close_callback);
580 }
581
582 // === Edit Shortcuts ===
583 if (undo_callback) {
584 RegisterShortcut("edit.undo", "Undo", ImGuiKey_Z,
585 true, false, false, "Edit", ShortcutContext::kGlobal,
586 undo_callback);
587 }
588
589 if (redo_callback) {
590 RegisterShortcut("edit.redo", "Redo", ImGuiKey_Y,
591 true, false, false, "Edit", ShortcutContext::kGlobal,
592 redo_callback);
593
594 // Also register Ctrl+Shift+Z for redo (common alternative)
595 RegisterShortcut("edit.redo_alt", "Redo", ImGuiKey_Z,
596 true, true, false, "Edit", ShortcutContext::kGlobal,
597 redo_callback);
598 }
599
600 if (copy_callback) {
601 RegisterShortcut("edit.copy", "Copy", ImGuiKey_C,
602 true, false, false, "Edit", ShortcutContext::kGlobal,
603 copy_callback);
604 }
605
606 if (paste_callback) {
607 RegisterShortcut("edit.paste", "Paste", ImGuiKey_V,
608 true, false, false, "Edit", ShortcutContext::kGlobal,
609 paste_callback);
610 }
611
612 if (cut_callback) {
613 RegisterShortcut("edit.cut", "Cut", ImGuiKey_X,
614 true, false, false, "Edit", ShortcutContext::kGlobal,
615 cut_callback);
616 }
617
618 if (find_callback) {
619 RegisterShortcut("edit.find", "Find", ImGuiKey_F,
620 true, false, false, "Edit", ShortcutContext::kGlobal,
621 find_callback);
622 }
623
624 // === View Shortcuts ===
625 RegisterShortcut("view.fullscreen", "Toggle Fullscreen", ImGuiKey_F11,
626 false, false, false, "View", ShortcutContext::kGlobal,
627 nullptr); // Placeholder - implement in EditorManager
628
629 RegisterShortcut("view.grid", "Toggle Grid", ImGuiKey_G,
630 true, false, false, "View", ShortcutContext::kGlobal,
631 nullptr); // Placeholder
632
633 RegisterShortcut("view.zoom_in", "Zoom In", ImGuiKey_Equal,
634 true, false, false, "View", ShortcutContext::kGlobal,
635 nullptr); // Placeholder
636
637 RegisterShortcut("view.zoom_out", "Zoom Out", ImGuiKey_Minus,
638 true, false, false, "View", ShortcutContext::kGlobal,
639 nullptr); // Placeholder
640
641 RegisterShortcut("view.zoom_reset", "Reset Zoom", ImGuiKey_0,
642 true, false, false, "View", ShortcutContext::kGlobal,
643 nullptr); // Placeholder
644
645 // === Navigation Shortcuts ===
646 RegisterShortcut("nav.first", "Go to First", ImGuiKey_Home,
647 false, false, false, "Navigation", ShortcutContext::kGlobal,
648 nullptr); // Placeholder
649
650 RegisterShortcut("nav.last", "Go to Last", ImGuiKey_End,
651 false, false, false, "Navigation", ShortcutContext::kGlobal,
652 nullptr); // Placeholder
653
654 RegisterShortcut("nav.prev", "Previous", ImGuiKey_PageUp,
655 false, false, false, "Navigation", ShortcutContext::kGlobal,
656 nullptr); // Placeholder
657
658 RegisterShortcut("nav.next", "Next", ImGuiKey_PageDown,
659 false, false, false, "Navigation", ShortcutContext::kGlobal,
660 nullptr); // Placeholder
661
662 // === Emulator Shortcuts ===
663 RegisterShortcut("emu.play_pause", "Play/Pause Emulator", ImGuiKey_Space,
664 false, false, false, "Editor", ShortcutContext::kEmulator,
665 nullptr); // Placeholder
666
667 RegisterShortcut("emu.reset", "Reset Emulator", ImGuiKey_R,
668 true, false, false, "Editor", ShortcutContext::kEmulator,
669 nullptr); // Placeholder
670
671 RegisterShortcut("emu.step", "Step Frame", ImGuiKey_F,
672 false, false, false, "Editor", ShortcutContext::kEmulator,
673 nullptr); // Placeholder
674
675 // === Overworld Editor Shortcuts ===
676 RegisterShortcut("ow.select_all", "Select All Tiles", ImGuiKey_A,
677 true, false, false, "Editor", ShortcutContext::kOverworld,
678 nullptr); // Placeholder
679
680 RegisterShortcut("ow.deselect", "Deselect", ImGuiKey_D,
681 true, false, false, "Editor", ShortcutContext::kOverworld,
682 nullptr); // Placeholder
683
684 // === Dungeon Editor Shortcuts ===
685 RegisterShortcut("dg.select_all", "Select All Objects", ImGuiKey_A,
686 true, false, false, "Editor", ShortcutContext::kDungeon,
687 nullptr); // Placeholder
688
689 RegisterShortcut("dg.delete", "Delete Selected", ImGuiKey_Delete,
690 false, false, false, "Editor", ShortcutContext::kDungeon,
691 nullptr); // Placeholder
692
693 RegisterShortcut("dg.duplicate", "Duplicate Selected", ImGuiKey_D,
694 true, false, false, "Editor", ShortcutContext::kDungeon,
695 nullptr); // Placeholder
696}
697
699 switch (context) {
701 return "Global";
703 return "Overworld";
705 return "Dungeon";
707 return "Graphics";
709 return "Palette";
711 return "Sprite";
713 return "Music";
715 return "Message";
717 return "Emulator";
719 return "Code";
720 default:
721 return "Unknown";
722 }
723}
724
725ShortcutContext EditorNameToContext(const std::string& editor_name) {
726 if (editor_name == "Overworld") return ShortcutContext::kOverworld;
727 if (editor_name == "Dungeon") return ShortcutContext::kDungeon;
728 if (editor_name == "Graphics") return ShortcutContext::kGraphics;
729 if (editor_name == "Palette") return ShortcutContext::kPalette;
730 if (editor_name == "Sprite") return ShortcutContext::kSprite;
731 if (editor_name == "Music") return ShortcutContext::kMusic;
732 if (editor_name == "Message") return ShortcutContext::kMessage;
733 if (editor_name == "Emulator") return ShortcutContext::kEmulator;
734 if (editor_name == "Assembly" || editor_name == "Code")
737}
738
739} // namespace gui
740} // namespace yaze
Manages keyboard shortcuts and provides an overlay UI.
void SetShortcutEnabled(const std::string &id, bool enabled)
void UnregisterShortcut(const std::string &id)
std::vector< std::string > category_order_
std::vector< std::string > GetCategories() const
std::map< std::string, Shortcut > shortcuts_
void DrawShortcutRow(const Shortcut &shortcut)
std::vector< const Shortcut * > GetContextShortcuts() const
std::vector< const Shortcut * > GetShortcutsInCategory(const std::string &category) const
static KeyboardShortcuts & Get()
void DrawCategorySection(const std::string &category, const std::vector< const Shortcut * > &shortcuts)
void RegisterShortcut(const Shortcut &shortcut)
void RegisterDefaultShortcuts(std::function< void()> open_callback, std::function< void()> save_callback, std::function< void()> save_as_callback, std::function< void()> close_callback, std::function< void()> undo_callback, std::function< void()> redo_callback, std::function< void()> copy_callback, std::function< void()> paste_callback, std::function< void()> cut_callback, std::function< void()> find_callback)
bool IsShortcutActiveInContext(const Shortcut &shortcut) const
const Theme & GetCurrentTheme() const
static ThemeManager & Get()
constexpr struct yaze::gui::anonymous_namespace{keyboard_shortcuts.cc}::@1 kLocalKeyNames[]
const char * GetCtrlDisplayName()
Get the display name for the primary modifier key.
ImVec4 ConvertColorToImVec4(const Color &color)
Definition color.h:23
const char * ShortcutContextToString(ShortcutContext context)
Convert ShortcutContext to display string.
ShortcutContext EditorNameToContext(const std::string &editor_name)
Convert editor type name to ShortcutContext.
const char * GetKeyName(ImGuiKey key)
Get the ImGui key name as a string.
ShortcutContext
Defines the context in which a shortcut is active.
const char * GetAltDisplayName()
Get the display name for the secondary modifier key.
Represents a keyboard shortcut with its associated action.
ShortcutContext context
std::string GetDisplayString() const
std::function< void()> action
bool Matches(ImGuiKey pressed_key, bool ctrl, bool shift, bool alt) const