keybinding.cpp 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. #include "lumacs/keybinding.hpp"
  2. #include <sstream>
  3. #include <algorithm>
  4. #include <cctype>
  5. namespace lumacs {
  6. // ============================================================================
  7. // Key Implementation
  8. // ============================================================================
  9. Key::Key(const std::string& key_name) {
  10. *this = parse(key_name);
  11. }
  12. Key Key::parse(const std::string& key_str) {
  13. Key key;
  14. if (key_str.empty()) {
  15. return key;
  16. }
  17. std::string remaining = key_str;
  18. // Parse modifiers
  19. while (true) {
  20. if (remaining.length() >= 2 && remaining.substr(0, 2) == "C-") {
  21. key.ctrl = true;
  22. remaining = remaining.substr(2);
  23. } else if (remaining.length() >= 2 && (remaining.substr(0, 2) == "M-" || remaining.substr(0, 2) == "A-")) {
  24. key.meta = true;
  25. remaining = remaining.substr(2);
  26. } else if (remaining.length() >= 2 && remaining.substr(0, 2) == "S-") {
  27. key.shift = true;
  28. remaining = remaining.substr(2);
  29. } else {
  30. break;
  31. }
  32. }
  33. // The rest is the key name
  34. key.name = remaining;
  35. return key;
  36. }
  37. std::string Key::to_string() const {
  38. std::string result;
  39. if (ctrl) result += "C-";
  40. if (meta) result += "M-";
  41. if (shift) result += "S-";
  42. result += name;
  43. return result;
  44. }
  45. bool Key::operator==(const Key& other) const {
  46. return name == other.name &&
  47. ctrl == other.ctrl &&
  48. meta == other.meta &&
  49. shift == other.shift;
  50. }
  51. bool Key::operator<(const Key& other) const {
  52. if (name != other.name) return name < other.name;
  53. if (ctrl != other.ctrl) return ctrl < other.ctrl;
  54. if (meta != other.meta) return meta < other.meta;
  55. return shift < other.shift;
  56. }
  57. // ============================================================================
  58. // KeySequence Implementation
  59. // ============================================================================
  60. KeySequence::KeySequence(const std::vector<Key>& keys) : keys_(keys) {
  61. }
  62. KeySequence::KeySequence(const std::string& key_sequence_str) {
  63. if (key_sequence_str.empty()) {
  64. return;
  65. }
  66. // Split by spaces, but handle quoted strings
  67. std::istringstream iss(key_sequence_str);
  68. std::string token;
  69. while (iss >> token) {
  70. keys_.emplace_back(Key::parse(token));
  71. }
  72. }
  73. void KeySequence::add_key(const Key& key) {
  74. keys_.push_back(key);
  75. }
  76. void KeySequence::add_key(const std::string& key_str) {
  77. keys_.emplace_back(Key::parse(key_str));
  78. }
  79. bool KeySequence::is_prefix_of(const KeySequence& other) const {
  80. if (keys_.size() > other.keys_.size()) {
  81. return false;
  82. }
  83. return std::equal(keys_.begin(), keys_.end(), other.keys_.begin());
  84. }
  85. bool KeySequence::has_prefix(const KeySequence& prefix) const {
  86. return prefix.is_prefix_of(*this);
  87. }
  88. std::string KeySequence::to_string() const {
  89. if (keys_.empty()) {
  90. return "";
  91. }
  92. std::string result = keys_[0].to_string();
  93. for (size_t i = 1; i < keys_.size(); ++i) {
  94. result += " " + keys_[i].to_string();
  95. }
  96. return result;
  97. }
  98. void KeySequence::clear() {
  99. keys_.clear();
  100. }
  101. KeySequence KeySequence::subsequence(size_t start_index) const {
  102. if (start_index >= keys_.size()) {
  103. return KeySequence();
  104. }
  105. std::vector<Key> sub_keys(keys_.begin() + start_index, keys_.end());
  106. return KeySequence(sub_keys);
  107. }
  108. bool KeySequence::operator==(const KeySequence& other) const {
  109. return keys_ == other.keys_;
  110. }
  111. bool KeySequence::operator<(const KeySequence& other) const {
  112. return keys_ < other.keys_;
  113. }
  114. // ============================================================================
  115. // KeyBinding Implementation
  116. // ============================================================================
  117. KeyBinding::KeyBinding(const KeySequence& seq, KeyBindingFunction func, std::string desc)
  118. : sequence(seq), function(std::move(func)), description(std::move(desc)) {
  119. }
  120. KeyBinding::KeyBinding(const std::string& key_str, KeyBindingFunction func, std::string desc)
  121. : sequence(key_str), function(std::move(func)), description(std::move(desc)) {
  122. }
  123. // ============================================================================
  124. // KeyBindingManager Implementation
  125. // ============================================================================
  126. KeyBindingManager::KeyBindingManager()
  127. : sequence_timeout_(std::chrono::milliseconds(1000)) {
  128. }
  129. void KeyBindingManager::bind(const KeySequence& sequence, KeyBindingFunction function, const std::string& description) {
  130. if (sequence.empty() || !function) {
  131. return;
  132. }
  133. bindings_[sequence] = KeyBinding(sequence, std::move(function), description);
  134. }
  135. void KeyBindingManager::bind(const std::string& key_str, KeyBindingFunction function, const std::string& description) {
  136. bind(KeySequence(key_str), std::move(function), description);
  137. }
  138. void KeyBindingManager::unbind(const KeySequence& sequence) {
  139. bindings_.erase(sequence);
  140. }
  141. void KeyBindingManager::unbind(const std::string& key_str) {
  142. unbind(KeySequence(key_str));
  143. }
  144. KeyResult KeyBindingManager::process_key(const Key& key) {
  145. // Check for timeout first
  146. if (is_building_sequence() && has_sequence_timed_out()) {
  147. clear_partial_sequence();
  148. // Fall through to process this key as a new sequence
  149. }
  150. // Add this key to the current sequence
  151. current_sequence_.add_key(key);
  152. // Update timestamp
  153. sequence_start_time_ = std::chrono::steady_clock::now();
  154. // Check for exact binding
  155. auto exact_binding = find_exact_binding(current_sequence_);
  156. if (exact_binding.has_value()) {
  157. // Found exact match
  158. clear_partial_sequence();
  159. try {
  160. bool success = exact_binding->function();
  161. return success ? KeyResult::Executed : KeyResult::Failed;
  162. } catch (...) {
  163. return KeyResult::Failed;
  164. }
  165. }
  166. // Check if this could be a prefix for other bindings
  167. if (has_prefix_bindings_impl(current_sequence_)) {
  168. return KeyResult::Partial;
  169. }
  170. // No binding found, clear sequence and return unbound
  171. clear_partial_sequence();
  172. return KeyResult::Unbound;
  173. }
  174. KeyResult KeyBindingManager::process_key(const std::string& key_str) {
  175. return process_key(Key::parse(key_str));
  176. }
  177. bool KeyBindingManager::has_binding(const KeySequence& sequence) const {
  178. return has_exact_binding(sequence) || has_prefix_bindings(sequence);
  179. }
  180. bool KeyBindingManager::has_exact_binding(const KeySequence& sequence) const {
  181. return bindings_.find(sequence) != bindings_.end();
  182. }
  183. bool KeyBindingManager::has_prefix_bindings(const KeySequence& sequence) const {
  184. return has_prefix_bindings_impl(sequence);
  185. }
  186. void KeyBindingManager::clear_partial_sequence() {
  187. current_sequence_.clear();
  188. sequence_start_time_ = std::chrono::steady_clock::time_point{};
  189. }
  190. bool KeyBindingManager::is_building_sequence() const {
  191. return !current_sequence_.empty();
  192. }
  193. std::string KeyBindingManager::current_sequence_display() const {
  194. if (current_sequence_.empty()) {
  195. return "";
  196. }
  197. return current_sequence_.to_string() + "-";
  198. }
  199. std::vector<KeyBinding> KeyBindingManager::get_all_bindings() const {
  200. std::vector<KeyBinding> result;
  201. result.reserve(bindings_.size());
  202. for (const auto& [sequence, binding] : bindings_) {
  203. result.push_back(binding);
  204. }
  205. return result;
  206. }
  207. void KeyBindingManager::set_sequence_timeout(std::chrono::milliseconds timeout) {
  208. sequence_timeout_ = timeout;
  209. }
  210. bool KeyBindingManager::has_sequence_timed_out() const {
  211. if (!is_building_sequence()) {
  212. return false;
  213. }
  214. auto now = std::chrono::steady_clock::now();
  215. return (now - sequence_start_time_) > sequence_timeout_;
  216. }
  217. std::optional<KeyBinding> KeyBindingManager::find_exact_binding(const KeySequence& sequence) const {
  218. auto it = bindings_.find(sequence);
  219. if (it != bindings_.end()) {
  220. return it->second;
  221. }
  222. return std::nullopt;
  223. }
  224. bool KeyBindingManager::has_prefix_bindings_impl(const KeySequence& sequence) const {
  225. // Check if any registered binding has this sequence as a prefix
  226. for (const auto& [binding_sequence, binding] : bindings_) {
  227. if (sequence.is_prefix_of(binding_sequence) && sequence != binding_sequence) {
  228. return true;
  229. }
  230. }
  231. return false;
  232. }
  233. } // namespace lumacs