#pragma once #include #include #include #include #include #include #include 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& 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& 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 keys_; }; /// @brief Function type for key binding callbacks. using KeyBindingFunction = std::function; /// @brief Represents a key binding with its associated function. struct KeyBinding { KeySequence sequence; ///< The key sequence that triggers this binding. KeyBindingFunction function;///< The function to execute. std::string description; ///< Human-readable description. KeyBinding() = default; KeyBinding(const KeySequence& seq, KeyBindingFunction func, std::string desc = ""); KeyBinding(const std::string& key_str, KeyBindingFunction func, 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(); ~KeyBindingManager() = default; /// @brief Bind a key sequence to a function. void bind(const KeySequence& sequence, KeyBindingFunction function, const std::string& description = ""); void bind(const std::string& key_str, KeyBindingFunction function, 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 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 bindings_; KeySequence current_sequence_; std::chrono::steady_clock::time_point sequence_start_time_; std::chrono::milliseconds sequence_timeout_; std::optional find_exact_binding(const KeySequence& sequence) const; bool has_prefix_bindings_impl(const KeySequence& sequence) const; }; } // namespace lumacs