#pragma once #include #include #include #include #include #include #include namespace lumacs { /// Represents a single key in a key sequence struct Key { std::string name; // Key name like "C-x", "M-f", "a", "Return" bool ctrl = false; // Ctrl modifier bool meta = false; // Meta/Alt modifier bool shift = false; // Shift modifier Key() = default; explicit Key(const std::string& key_name); /// Parse a key string like "C-x" into Key structure static Key parse(const std::string& key_str); /// Convert Key back to string representation std::string to_string() const; bool operator==(const Key& other) const; bool operator<(const Key& other) const; }; /// Represents a sequence of keys like ["C-x", "2"] for "C-x 2" class KeySequence { public: KeySequence() = default; explicit KeySequence(const std::vector& keys); explicit KeySequence(const std::string& key_sequence_str); /// Add a key to the sequence void add_key(const Key& key); void add_key(const std::string& key_str); /// Get the keys in this sequence const std::vector& keys() const { return keys_; } /// Check if this sequence is empty bool empty() const { return keys_.empty(); } /// Get the length of the sequence size_t length() const { return keys_.size(); } /// Check if this sequence is a prefix of another sequence bool is_prefix_of(const KeySequence& other) const; /// Check if this sequence has the given sequence as a prefix bool has_prefix(const KeySequence& prefix) const; /// Get string representation like "C-x 2" std::string to_string() const; /// Clear the sequence void clear(); /// 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_; }; /// Function type for key binding callbacks using KeyBindingFunction = std::function; /// Represents a key binding with its associated function struct KeyBinding { KeySequence sequence; KeyBindingFunction function; 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 = ""); }; /// 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 }; /// Central manager for all key bindings class KeyBindingManager { public: KeyBindingManager(); ~KeyBindingManager() = default; /// 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 = ""); /// Unbind a key sequence void unbind(const KeySequence& sequence); void unbind(const std::string& key_str); /// Process a single key press KeyResult process_key(const Key& key); KeyResult process_key(const std::string& key_str); /// Check if a sequence has any bindings (exact or partial) bool has_binding(const KeySequence& sequence) const; bool has_exact_binding(const KeySequence& sequence) const; bool has_prefix_bindings(const KeySequence& sequence) const; /// Clear current partial sequence (e.g., on timeout or escape) void clear_partial_sequence(); /// Get current partial sequence for display purposes const KeySequence& current_partial_sequence() const { return current_sequence_; } /// Check if we're currently building a multi-key sequence bool is_building_sequence() const; /// Get string representation of current sequence for display std::string current_sequence_display() const; /// Get all registered bindings (for debugging/help) std::vector get_all_bindings() const; /// Set timeout for multi-key sequences (in milliseconds) void set_sequence_timeout(std::chrono::milliseconds timeout); /// Check if current partial sequence has timed out bool has_sequence_timed_out() const; private: /// Map from key sequences to their bindings std::map bindings_; /// Current partial key sequence being built KeySequence current_sequence_; /// Timestamp of when current sequence started std::chrono::steady_clock::time_point sequence_start_time_; /// Timeout for partial sequences std::chrono::milliseconds sequence_timeout_; /// Find exact binding for a sequence std::optional find_exact_binding(const KeySequence& sequence) const; /// Check if sequence has any prefix bindings bool has_prefix_bindings_impl(const KeySequence& sequence) const; }; } // namespace lumacs