feat(editor): implement enhanced hierarchical WhichKey navigation

- Introduced a new hierarchical WhichKey feature that allows users to navigate commands using a Spacemacs-style interface.
- Added functionality for breadcrumb navigation and auto-close after a specified duration.
- Implemented keyboard input handling for prefix keys to enter submenus or execute commands.
- Updated the CommandManager header to include new methods and state variables for managing WhichKey navigation.

Benefits:
- Improved user experience by providing a more intuitive command navigation system.
- Enhanced accessibility of commands through visual grouping and structured navigation.
This commit is contained in:
scawful
2025-10-12 01:41:12 -04:00
parent ecf797d633
commit 55a28661af
4 changed files with 255 additions and 0 deletions

View File

@@ -76,5 +76,145 @@ void CommandManager::LoadKeybindings(const std::string &filepath) {
}
}
// Enhanced hierarchical WhichKey with Spacemacs-style navigation
void CommandManager::ShowWhichKeyHierarchical() {
// Activate on Space key
if (ImGui::IsKeyPressed(ImGuiKey_Space) && current_prefix_.empty()) {
whichkey_active_ = true;
whichkey_timer_ = 0.0f;
ImGui::OpenPopup("WhichKeyHierarchical");
}
// ESC to close or go back
if (ImGui::IsKeyPressed(ImGuiKey_Escape)) {
if (!current_prefix_.empty()) {
current_prefix_.clear(); // Go back to root
} else {
whichkey_active_ = false;
ImGui::CloseCurrentPopup();
}
}
// Position at bottom of screen
ImGui::SetNextWindowPos(ImVec2(0, ImGui::GetIO().DisplaySize.y - 150),
ImGuiCond_Always);
ImGui::SetNextWindowSize(ImVec2(ImGui::GetIO().DisplaySize.x, 150),
ImGuiCond_Always);
if (ImGui::BeginPopup("WhichKeyHierarchical")) {
whichkey_active_ = true;
// Update timer for auto-close
whichkey_timer_ += ImGui::GetIO().DeltaTime;
if (whichkey_timer_ > 5.0f) { // Auto-close after 5 seconds
whichkey_active_ = false;
current_prefix_.clear();
ImGui::CloseCurrentPopup();
ImGui::EndPopup();
return;
}
// Show breadcrumb navigation
if (!current_prefix_.empty()) {
ImGui::TextColored(ImVec4(0.5f, 0.8f, 1.0f, 1.0f),
"Space > %s", current_prefix_.c_str());
ImGui::Separator();
} else {
ImGui::TextColored(ImVec4(0.5f, 0.8f, 1.0f, 1.0f), "Space > ...");
ImGui::Separator();
}
// Color palette for visual grouping
const ImVec4 colors[] = {
ImVec4(0.8f, 0.2f, 0.2f, 1.0f), // Red - Window
ImVec4(0.2f, 0.8f, 0.2f, 1.0f), // Green - Buffer
ImVec4(0.2f, 0.2f, 0.8f, 1.0f), // Blue - File
ImVec4(0.8f, 0.8f, 0.2f, 1.0f), // Yellow - Session
ImVec4(0.8f, 0.2f, 0.8f, 1.0f), // Magenta - Layout
ImVec4(0.2f, 0.8f, 0.8f, 1.0f) // Cyan - Theme
};
// Show commands based on current navigation level
if (current_prefix_.empty()) {
// Root level - show main groups
if (ImGui::BeginTable("RootCommands", 6, ImGuiTableFlags_SizingStretchProp)) {
int colorIndex = 0;
for (const auto &[shortcut, group] : commands_) {
ImGui::TableNextColumn();
ImGui::TextColored(colors[colorIndex % 6],
"%c: %s",
group.main_command.mnemonic,
group.main_command.name.c_str());
colorIndex++;
}
ImGui::EndTable();
}
} else {
// Submenu level - show subcommands
auto it = commands_.find(current_prefix_);
if (it != commands_.end()) {
const auto& group = it->second;
if (!group.subcommands.empty()) {
if (ImGui::BeginTable("Subcommands",
std::min(6, (int)group.subcommands.size()),
ImGuiTableFlags_SizingStretchProp)) {
int colorIndex = 0;
for (const auto& [key, cmd] : group.subcommands) {
ImGui::TableNextColumn();
ImGui::TextColored(colors[colorIndex % 6],
"%c: %s",
cmd.mnemonic,
cmd.name.c_str());
colorIndex++;
}
ImGui::EndTable();
}
} else {
ImGui::TextDisabled("No subcommands available");
}
}
}
ImGui::EndPopup();
} else {
whichkey_active_ = false;
current_prefix_.clear();
}
}
// Handle keyboard input for WhichKey navigation
void CommandManager::HandleWhichKeyInput() {
if (!whichkey_active_) return;
// Check for prefix keys (w, l, f, b, s, t, etc.)
for (const auto& [shortcut, group] : commands_) {
ImGuiKey key = gui::MapKeyToImGuiKey(group.main_command.mnemonic);
if (key != ImGuiKey_COUNT && ImGui::IsKeyPressed(key)) {
if (current_prefix_.empty()) {
// Enter submenu
current_prefix_ = shortcut;
whichkey_timer_ = 0.0f;
return;
} else {
// Execute subcommand
auto it = commands_.find(current_prefix_);
if (it != commands_.end()) {
for (const auto& [subkey, cmd] : it->second.subcommands) {
if (cmd.mnemonic == group.main_command.mnemonic) {
if (cmd.command) {
cmd.command();
}
whichkey_active_ = false;
current_prefix_.clear();
ImGui::CloseCurrentPopup();
return;
}
}
}
}
}
}
}
} // namespace editor
} // namespace yaze

View File

@@ -72,11 +72,24 @@ class CommandManager {
void ShowWhichKey();
// Enhanced hierarchical WhichKey with submenu support
void ShowWhichKeyHierarchical();
void HandleWhichKeyInput();
void SaveKeybindings(const std::string &filepath);
void LoadKeybindings(const std::string &filepath);
// Navigation state
bool IsWhichKeyActive() const { return whichkey_active_; }
std::string GetCurrentPrefix() const { return current_prefix_; }
private:
std::unordered_map<std::string, CommandGroup> commands_;
// WhichKey state
bool whichkey_active_ = false;
std::string current_prefix_; // Current navigation prefix (e.g., "w", "l", "f")
float whichkey_timer_ = 0.0f; // Auto-close timer
};
} // namespace editor

View File

@@ -164,5 +164,90 @@ void ExecuteShortcuts(const ShortcutManager& shortcut_manager) {
}
}
} // namespace editor
} // namespace yaze
// Implementation in header file (inline methods)
namespace yaze {
namespace editor {
void ShortcutManager::RegisterStandardShortcuts(
std::function<void()> save_callback,
std::function<void()> open_callback,
std::function<void()> close_callback,
std::function<void()> find_callback,
std::function<void()> settings_callback) {
// Ctrl+S - Save
if (save_callback) {
RegisterShortcut("save", {ImGuiMod_Ctrl, ImGuiKey_S}, save_callback);
}
// Ctrl+O - Open
if (open_callback) {
RegisterShortcut("open", {ImGuiMod_Ctrl, ImGuiKey_O}, open_callback);
}
// Ctrl+W - Close
if (close_callback) {
RegisterShortcut("close", {ImGuiMod_Ctrl, ImGuiKey_W}, close_callback);
}
// Ctrl+F - Find
if (find_callback) {
RegisterShortcut("find", {ImGuiMod_Ctrl, ImGuiKey_F}, find_callback);
}
// Ctrl+, - Settings
if (settings_callback) {
RegisterShortcut("settings", {ImGuiMod_Ctrl, ImGuiKey_Comma}, settings_callback);
}
// Ctrl+Tab - Next tab (placeholder for now)
// Ctrl+Shift+Tab - Previous tab (placeholder for now)
}
void ShortcutManager::RegisterWindowNavigationShortcuts(
std::function<void()> focus_left,
std::function<void()> focus_right,
std::function<void()> focus_up,
std::function<void()> focus_down,
std::function<void()> close_window,
std::function<void()> split_horizontal,
std::function<void()> split_vertical) {
// Ctrl+Arrow keys for window navigation
if (focus_left) {
RegisterShortcut("focus_left", {ImGuiMod_Ctrl, ImGuiKey_LeftArrow}, focus_left);
}
if (focus_right) {
RegisterShortcut("focus_right", {ImGuiMod_Ctrl, ImGuiKey_RightArrow}, focus_right);
}
if (focus_up) {
RegisterShortcut("focus_up", {ImGuiMod_Ctrl, ImGuiKey_UpArrow}, focus_up);
}
if (focus_down) {
RegisterShortcut("focus_down", {ImGuiMod_Ctrl, ImGuiKey_DownArrow}, focus_down);
}
// Ctrl+W, C - Close current window
if (close_window) {
RegisterShortcut("close_window", {ImGuiMod_Ctrl, ImGuiKey_W, ImGuiKey_C}, close_window);
}
// Ctrl+W, S - Split horizontal
if (split_horizontal) {
RegisterShortcut("split_horizontal", {ImGuiMod_Ctrl, ImGuiKey_W, ImGuiKey_S}, split_horizontal);
}
// Ctrl+W, V - Split vertical
if (split_vertical) {
RegisterShortcut("split_vertical", {ImGuiMod_Ctrl, ImGuiKey_W, ImGuiKey_V}, split_vertical);
}
}
} // namespace editor
} // namespace yaze

View File

@@ -62,6 +62,23 @@ class ShortcutManager {
auto GetShortcuts() const { return shortcuts_; }
// Convenience methods for registering common shortcuts
void RegisterStandardShortcuts(
std::function<void()> save_callback,
std::function<void()> open_callback,
std::function<void()> close_callback,
std::function<void()> find_callback,
std::function<void()> settings_callback);
void RegisterWindowNavigationShortcuts(
std::function<void()> focus_left,
std::function<void()> focus_right,
std::function<void()> focus_up,
std::function<void()> focus_down,
std::function<void()> close_window,
std::function<void()> split_horizontal,
std::function<void()> split_vertical);
private:
std::unordered_map<std::string, Shortcut> shortcuts_;
};