keybinding.hpp 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. #include <string>
  2. #include <vector>
  3. #include <map>
  4. #include <memory>
  5. #include <chrono>
  6. #include <optional>
  7. // Forward declaration for CommandSystem
  8. namespace lumacs {
  9. class CommandSystem;
  10. }
  11. namespace lumacs {
  12. /// @brief Represents a single key in a key sequence.
  13. struct Key {
  14. std::string name; ///< Key name (e.g., "a", "Return", "F1").
  15. bool ctrl = false; ///< Ctrl modifier active.
  16. bool meta = false; ///< Meta/Alt modifier active.
  17. bool shift = false; ///< Shift modifier active.
  18. Key() = default;
  19. explicit Key(const std::string& key_name);
  20. /// @brief Parse a key string like "C-x" or "M-S-a" into a Key structure.
  21. static Key parse(const std::string& key_str);
  22. /// @brief Convert Key back to canonical string representation.
  23. std::string to_string() const;
  24. bool operator==(const Key& other) const;
  25. bool operator<(const Key& other) const;
  26. };
  27. /// @brief Represents a sequence of keys (e.g., "C-x C-f").
  28. class KeySequence {
  29. public:
  30. KeySequence() = default;
  31. explicit KeySequence(const std::vector<Key>& keys);
  32. explicit KeySequence(const std::string& key_sequence_str);
  33. /// @brief Add a key to the sequence.
  34. void add_key(const Key& key);
  35. void add_key(const std::string& key_str);
  36. /// @brief Get the keys in this sequence.
  37. const std::vector<Key>& keys() const { return keys_; }
  38. /// @brief Check if this sequence is empty.
  39. bool empty() const { return keys_.empty(); }
  40. /// @brief Get the length of the sequence.
  41. size_t length() const { return keys_.size(); }
  42. /// @brief Check if this sequence is a prefix of another sequence.
  43. bool is_prefix_of(const KeySequence& other) const;
  44. /// @brief Check if this sequence has the given sequence as a prefix.
  45. bool has_prefix(const KeySequence& prefix) const;
  46. /// @brief Get string representation like "C-x 2".
  47. std::string to_string() const;
  48. /// @brief Clear the sequence.
  49. void clear();
  50. /// @brief Get a subsequence starting from index.
  51. KeySequence subsequence(size_t start_index) const;
  52. bool operator==(const KeySequence& other) const;
  53. bool operator<(const KeySequence& other) const;
  54. private:
  55. std::vector<Key> keys_;
  56. };
  57. /// @brief Represents a key binding with its associated function.
  58. struct KeyBinding {
  59. KeySequence sequence; ///< The key sequence that triggers this binding.
  60. std::string command_name; ///< The name of the command to execute.
  61. std::string description; ///< Human-readable description.
  62. KeyBinding() = default;
  63. KeyBinding(const KeySequence& seq, std::string cmd_name, std::string desc = "");
  64. KeyBinding(const std::string& key_str, std::string cmd_name, std::string desc = "");
  65. };
  66. /// @brief Result of processing a key in the binding manager.
  67. enum class KeyResult {
  68. Unbound, ///< No binding found, key not handled.
  69. Executed, ///< Binding found and executed successfully.
  70. Failed, ///< Binding found but execution failed.
  71. Partial, ///< Key is part of a multi-key sequence, waiting for more keys.
  72. Timeout ///< Partial sequence timed out.
  73. };
  74. /// @brief Central manager for all key bindings.
  75. ///
  76. /// Handles:
  77. /// - Registration of global key bindings.
  78. /// - Processing of key input (single and multi-key sequences).
  79. /// - Prefix key logic (e.g., C-x waiting for next key).
  80. class KeyBindingManager {
  81. public:
  82. KeyBindingManager(CommandSystem* command_system);
  83. ~KeyBindingManager() = default;
  84. /// @brief Bind a key sequence to a function.
  85. void bind(const KeySequence& sequence, std::string cmd_name, const std::string& description = "");
  86. void bind(const std::string& key_str, std::string cmd_name, const std::string& description = "");
  87. /// @brief Unbind a key sequence.
  88. void unbind(const KeySequence& sequence);
  89. void unbind(const std::string& key_str);
  90. /// @brief Process a single key press.
  91. /// @return Result indicating if key was bound, partial, etc.
  92. KeyResult process_key(const Key& key);
  93. KeyResult process_key(const std::string& key_str);
  94. /// @brief Check if a sequence has any bindings (exact or partial).
  95. bool has_binding(const KeySequence& sequence) const;
  96. /// @brief Check if a sequence has an exact binding.
  97. bool has_exact_binding(const KeySequence& sequence) const;
  98. /// @brief Check if a sequence is a prefix for other bindings.
  99. bool has_prefix_bindings(const KeySequence& sequence) const;
  100. /// @brief Clear current partial sequence (e.g., on timeout or escape).
  101. void clear_partial_sequence();
  102. /// @brief Get current partial sequence for display purposes.
  103. const KeySequence& current_partial_sequence() const { return current_sequence_; }
  104. /// @brief Check if we're currently building a multi-key sequence.
  105. bool is_building_sequence() const;
  106. /// @brief Get string representation of current sequence for display.
  107. std::string current_sequence_display() const;
  108. /// @brief Get all registered bindings (for debugging/help).
  109. std::vector<KeyBinding> get_all_bindings() const;
  110. /// @brief Set timeout for multi-key sequences.
  111. void set_sequence_timeout(std::chrono::milliseconds timeout);
  112. /// @brief Check if current partial sequence has timed out.
  113. bool has_sequence_timed_out() const;
  114. private:
  115. std::map<KeySequence, KeyBinding> bindings_;
  116. KeySequence current_sequence_;
  117. std::chrono::steady_clock::time_point sequence_start_time_;
  118. std::chrono::milliseconds sequence_timeout_;
  119. std::optional<KeyBinding> find_exact_binding(const KeySequence& sequence) const;
  120. bool has_prefix_bindings_impl(const KeySequence& sequence) const;
  121. };
  122. } // namespace lumacs