| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301 |
- #include "lumacs/keybinding.hpp"
- #include <sstream>
- #include <algorithm>
- #include <cctype>
- namespace lumacs {
- // ============================================================================
- // Key Implementation
- // ============================================================================
- Key::Key(const std::string& key_name) {
- *this = parse(key_name);
- }
- Key Key::parse(const std::string& key_str) {
- Key key;
-
- if (key_str.empty()) {
- return key;
- }
-
- std::string remaining = key_str;
-
- // Parse modifiers
- while (true) {
- if (remaining.length() >= 2 && remaining.substr(0, 2) == "C-") {
- key.ctrl = true;
- remaining = remaining.substr(2);
- } else if (remaining.length() >= 2 && (remaining.substr(0, 2) == "M-" || remaining.substr(0, 2) == "A-")) {
- key.meta = true;
- remaining = remaining.substr(2);
- } else if (remaining.length() >= 2 && remaining.substr(0, 2) == "S-") {
- key.shift = true;
- remaining = remaining.substr(2);
- } else {
- break;
- }
- }
-
- // The rest is the key name
- key.name = remaining;
-
- return key;
- }
- std::string Key::to_string() const {
- std::string result;
-
- if (ctrl) result += "C-";
- if (meta) result += "M-";
- if (shift) result += "S-";
-
- result += name;
-
- return result;
- }
- bool Key::operator==(const Key& other) const {
- return name == other.name &&
- ctrl == other.ctrl &&
- meta == other.meta &&
- shift == other.shift;
- }
- bool Key::operator<(const Key& other) const {
- if (name != other.name) return name < other.name;
- if (ctrl != other.ctrl) return ctrl < other.ctrl;
- if (meta != other.meta) return meta < other.meta;
- return shift < other.shift;
- }
- // ============================================================================
- // KeySequence Implementation
- // ============================================================================
- KeySequence::KeySequence(const std::vector<Key>& keys) : keys_(keys) {
- }
- KeySequence::KeySequence(const std::string& key_sequence_str) {
- if (key_sequence_str.empty()) {
- return;
- }
-
- // Split by spaces, but handle quoted strings
- std::istringstream iss(key_sequence_str);
- std::string token;
-
- while (iss >> token) {
- keys_.emplace_back(Key::parse(token));
- }
- }
- void KeySequence::add_key(const Key& key) {
- keys_.push_back(key);
- }
- void KeySequence::add_key(const std::string& key_str) {
- keys_.emplace_back(Key::parse(key_str));
- }
- bool KeySequence::is_prefix_of(const KeySequence& other) const {
- if (keys_.size() > other.keys_.size()) {
- return false;
- }
-
- return std::equal(keys_.begin(), keys_.end(), other.keys_.begin());
- }
- bool KeySequence::has_prefix(const KeySequence& prefix) const {
- return prefix.is_prefix_of(*this);
- }
- std::string KeySequence::to_string() const {
- if (keys_.empty()) {
- return "";
- }
-
- std::string result = keys_[0].to_string();
-
- for (size_t i = 1; i < keys_.size(); ++i) {
- result += " " + keys_[i].to_string();
- }
-
- return result;
- }
- void KeySequence::clear() {
- keys_.clear();
- }
- KeySequence KeySequence::subsequence(size_t start_index) const {
- if (start_index >= keys_.size()) {
- return KeySequence();
- }
-
- std::vector<Key> sub_keys(keys_.begin() + start_index, keys_.end());
- return KeySequence(sub_keys);
- }
- bool KeySequence::operator==(const KeySequence& other) const {
- return keys_ == other.keys_;
- }
- bool KeySequence::operator<(const KeySequence& other) const {
- return keys_ < other.keys_;
- }
- // ============================================================================
- // KeyBinding Implementation
- // ============================================================================
- KeyBinding::KeyBinding(const KeySequence& seq, KeyBindingFunction func, std::string desc)
- : sequence(seq), function(std::move(func)), description(std::move(desc)) {
- }
- KeyBinding::KeyBinding(const std::string& key_str, KeyBindingFunction func, std::string desc)
- : sequence(key_str), function(std::move(func)), description(std::move(desc)) {
- }
- // ============================================================================
- // KeyBindingManager Implementation
- // ============================================================================
- KeyBindingManager::KeyBindingManager()
- : sequence_timeout_(std::chrono::milliseconds(1000)) {
- }
- void KeyBindingManager::bind(const KeySequence& sequence, KeyBindingFunction function, const std::string& description) {
- if (sequence.empty() || !function) {
- return;
- }
-
- bindings_[sequence] = KeyBinding(sequence, std::move(function), description);
- }
- void KeyBindingManager::bind(const std::string& key_str, KeyBindingFunction function, const std::string& description) {
- bind(KeySequence(key_str), std::move(function), description);
- }
- void KeyBindingManager::unbind(const KeySequence& sequence) {
- bindings_.erase(sequence);
- }
- void KeyBindingManager::unbind(const std::string& key_str) {
- unbind(KeySequence(key_str));
- }
- KeyResult KeyBindingManager::process_key(const Key& key) {
- // Check for timeout first
- if (is_building_sequence() && has_sequence_timed_out()) {
- clear_partial_sequence();
- // Fall through to process this key as a new sequence
- }
-
- // Add this key to the current sequence
- current_sequence_.add_key(key);
-
- // Update timestamp
- sequence_start_time_ = std::chrono::steady_clock::now();
-
- // Check for exact binding
- auto exact_binding = find_exact_binding(current_sequence_);
- if (exact_binding.has_value()) {
- // Found exact match
- clear_partial_sequence();
-
- try {
- bool success = exact_binding->function();
- return success ? KeyResult::Executed : KeyResult::Failed;
- } catch (...) {
- return KeyResult::Failed;
- }
- }
-
- // Check if this could be a prefix for other bindings
- if (has_prefix_bindings_impl(current_sequence_)) {
- return KeyResult::Partial;
- }
-
- // No binding found, clear sequence and return unbound
- clear_partial_sequence();
- return KeyResult::Unbound;
- }
- KeyResult KeyBindingManager::process_key(const std::string& key_str) {
- return process_key(Key::parse(key_str));
- }
- bool KeyBindingManager::has_binding(const KeySequence& sequence) const {
- return has_exact_binding(sequence) || has_prefix_bindings(sequence);
- }
- bool KeyBindingManager::has_exact_binding(const KeySequence& sequence) const {
- return bindings_.find(sequence) != bindings_.end();
- }
- bool KeyBindingManager::has_prefix_bindings(const KeySequence& sequence) const {
- return has_prefix_bindings_impl(sequence);
- }
- void KeyBindingManager::clear_partial_sequence() {
- current_sequence_.clear();
- sequence_start_time_ = std::chrono::steady_clock::time_point{};
- }
- bool KeyBindingManager::is_building_sequence() const {
- return !current_sequence_.empty();
- }
- std::string KeyBindingManager::current_sequence_display() const {
- if (current_sequence_.empty()) {
- return "";
- }
-
- return current_sequence_.to_string() + "-";
- }
- std::vector<KeyBinding> KeyBindingManager::get_all_bindings() const {
- std::vector<KeyBinding> result;
- result.reserve(bindings_.size());
-
- for (const auto& [sequence, binding] : bindings_) {
- result.push_back(binding);
- }
-
- return result;
- }
- void KeyBindingManager::set_sequence_timeout(std::chrono::milliseconds timeout) {
- sequence_timeout_ = timeout;
- }
- bool KeyBindingManager::has_sequence_timed_out() const {
- if (!is_building_sequence()) {
- return false;
- }
-
- auto now = std::chrono::steady_clock::now();
- return (now - sequence_start_time_) > sequence_timeout_;
- }
- std::optional<KeyBinding> KeyBindingManager::find_exact_binding(const KeySequence& sequence) const {
- auto it = bindings_.find(sequence);
- if (it != bindings_.end()) {
- return it->second;
- }
- return std::nullopt;
- }
- bool KeyBindingManager::has_prefix_bindings_impl(const KeySequence& sequence) const {
- // Check if any registered binding has this sequence as a prefix
- for (const auto& [binding_sequence, binding] : bindings_) {
- if (sequence.is_prefix_of(binding_sequence) && sequence != binding_sequence) {
- return true;
- }
- }
- return false;
- }
- } // namespace lumacs
|