| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163 |
- #include <string>
- #include <vector>
- #include <map>
- #include <memory>
- #include <chrono>
- #include <optional>
- // Forward declaration for CommandSystem
- namespace lumacs {
- class CommandSystem;
- }
- namespace lumacs {
- /// @brief Represents a single key in a key sequence.
- struct Key {
- std::string name; ///< Key name (e.g., "a", "Return", "F1").
- bool ctrl = false; ///< Ctrl modifier active.
- bool meta = false; ///< Meta/Alt modifier active.
- bool shift = false; ///< Shift modifier active.
-
- Key() = default;
- explicit Key(const std::string& key_name);
-
- /// @brief Parse a key string like "C-x" or "M-S-a" into a Key structure.
- static Key parse(const std::string& key_str);
-
- /// @brief Convert Key back to canonical string representation.
- std::string to_string() const;
-
- bool operator==(const Key& other) const;
- bool operator<(const Key& other) const;
- };
- /// @brief Represents a sequence of keys (e.g., "C-x C-f").
- class KeySequence {
- public:
- KeySequence() = default;
- explicit KeySequence(const std::vector<Key>& keys);
- explicit KeySequence(const std::string& key_sequence_str);
-
- /// @brief Add a key to the sequence.
- void add_key(const Key& key);
- void add_key(const std::string& key_str);
-
- /// @brief Get the keys in this sequence.
- const std::vector<Key>& keys() const { return keys_; }
-
- /// @brief Check if this sequence is empty.
- bool empty() const { return keys_.empty(); }
-
- /// @brief Get the length of the sequence.
- size_t length() const { return keys_.size(); }
-
- /// @brief Check if this sequence is a prefix of another sequence.
- bool is_prefix_of(const KeySequence& other) const;
-
- /// @brief Check if this sequence has the given sequence as a prefix.
- bool has_prefix(const KeySequence& prefix) const;
-
- /// @brief Get string representation like "C-x 2".
- std::string to_string() const;
-
- /// @brief Clear the sequence.
- void clear();
-
- /// @brief Get a subsequence starting from index.
- KeySequence subsequence(size_t start_index) const;
-
- bool operator==(const KeySequence& other) const;
- bool operator<(const KeySequence& other) const;
- private:
- std::vector<Key> keys_;
- };
- /// @brief Represents a key binding with its associated function.
- struct KeyBinding {
- KeySequence sequence; ///< The key sequence that triggers this binding.
- std::string command_name; ///< The name of the command to execute.
- std::string description; ///< Human-readable description.
-
- KeyBinding() = default;
- KeyBinding(const KeySequence& seq, std::string cmd_name, std::string desc = "");
- KeyBinding(const std::string& key_str, std::string cmd_name, std::string desc = "");
- };
- /// @brief Result of processing a key in the binding manager.
- enum class KeyResult {
- Unbound, ///< No binding found, key not handled.
- Executed, ///< Binding found and executed successfully.
- Failed, ///< Binding found but execution failed.
- Partial, ///< Key is part of a multi-key sequence, waiting for more keys.
- Timeout ///< Partial sequence timed out.
- };
- /// @brief Central manager for all key bindings.
- ///
- /// Handles:
- /// - Registration of global key bindings.
- /// - Processing of key input (single and multi-key sequences).
- /// - Prefix key logic (e.g., C-x waiting for next key).
- class KeyBindingManager {
- public:
- KeyBindingManager(CommandSystem* command_system);
- ~KeyBindingManager() = default;
-
- /// @brief Bind a key sequence to a function.
- void bind(const KeySequence& sequence, std::string cmd_name, const std::string& description = "");
- void bind(const std::string& key_str, std::string cmd_name, const std::string& description = "");
-
- /// @brief Unbind a key sequence.
- void unbind(const KeySequence& sequence);
- void unbind(const std::string& key_str);
-
- /// @brief Process a single key press.
- /// @return Result indicating if key was bound, partial, etc.
- KeyResult process_key(const Key& key);
- KeyResult process_key(const std::string& key_str);
-
- /// @brief Check if a sequence has any bindings (exact or partial).
- bool has_binding(const KeySequence& sequence) const;
-
- /// @brief Check if a sequence has an exact binding.
- bool has_exact_binding(const KeySequence& sequence) const;
-
- /// @brief Check if a sequence is a prefix for other bindings.
- bool has_prefix_bindings(const KeySequence& sequence) const;
-
- /// @brief Clear current partial sequence (e.g., on timeout or escape).
- void clear_partial_sequence();
-
- /// @brief Get current partial sequence for display purposes.
- const KeySequence& current_partial_sequence() const { return current_sequence_; }
-
- /// @brief Check if we're currently building a multi-key sequence.
- bool is_building_sequence() const;
-
- /// @brief Get string representation of current sequence for display.
- std::string current_sequence_display() const;
-
- /// @brief Get all registered bindings (for debugging/help).
- std::vector<KeyBinding> get_all_bindings() const;
-
- /// @brief Set timeout for multi-key sequences.
- void set_sequence_timeout(std::chrono::milliseconds timeout);
-
- /// @brief Check if current partial sequence has timed out.
- bool has_sequence_timed_out() const;
- private:
- std::map<KeySequence, KeyBinding> bindings_;
- KeySequence current_sequence_;
- std::chrono::steady_clock::time_point sequence_start_time_;
- std::chrono::milliseconds sequence_timeout_;
-
- std::optional<KeyBinding> find_exact_binding(const KeySequence& sequence) const;
- bool has_prefix_bindings_impl(const KeySequence& sequence) const;
- };
- } // namespace lumacs
|