|
|
@@ -0,0 +1,162 @@
|
|
|
+#pragma once
|
|
|
+
|
|
|
+#include <string>
|
|
|
+#include <vector>
|
|
|
+#include <map>
|
|
|
+#include <memory>
|
|
|
+#include <functional>
|
|
|
+#include <chrono>
|
|
|
+#include <optional>
|
|
|
+
|
|
|
+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<Key>& 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<Key>& 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<Key> keys_;
|
|
|
+};
|
|
|
+
|
|
|
+/// Function type for key binding callbacks
|
|
|
+using KeyBindingFunction = std::function<bool()>;
|
|
|
+
|
|
|
+/// 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<KeyBinding> 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<KeySequence, KeyBinding> 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<KeyBinding> 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
|