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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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_;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user