Class: Chamomile::Keymap
- Inherits:
-
Object
- Object
- Chamomile::Keymap
- Defined in:
- lib/chamomile/keymap.rb
Overview
Declarative keymap with composable guard conditions.
Usage:
@keymap = Keymap.new
.bind("q") { quit }
.bind(:tab) { focus_next }
.only(-> { !modal_open? }) do |km|
km.bind("j") { scroll_down }
km.bind("k") { scroll_up }
end
Then in update:
def update(msg)
return @keymap.handle(msg, self) if msg.is_a?(KeyEvent)
...
end
Defined Under Namespace
Classes: Entry
Instance Method Summary collapse
-
#bind(key, guard: nil, &action) ⇒ Object
Bind a key to a block.
-
#handle(msg, model) ⇒ Object
Process a KeyEvent against the keymap.
-
#initialize ⇒ Keymap
constructor
A new instance of Keymap.
-
#only(guard, &block) ⇒ Object
Add a group of bindings that only fire when guard returns true.
Constructor Details
#initialize ⇒ Keymap
Returns a new instance of Keymap.
23 24 25 |
# File 'lib/chamomile/keymap.rb', line 23 def initialize @entries = [] end |
Instance Method Details
#bind(key, guard: nil, &action) ⇒ Object
Bind a key to a block. Returns self for chaining.
28 29 30 31 |
# File 'lib/chamomile/keymap.rb', line 28 def bind(key, guard: nil, &action) @entries << Entry.new(key: key, guard: guard, action: action) self end |
#handle(msg, model) ⇒ Object
Process a KeyEvent against the keymap. Returns the result of the matching action, or nil if no match.
51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/chamomile/keymap.rb', line 51 def handle(msg, model) return nil unless msg.is_a?(KeyEvent) @entries.each do |entry| next unless keys_match?(entry.key, msg) next if entry.guard && !entry.guard.call(model) return model.instance_exec(&entry.action) end nil end |
#only(guard, &block) ⇒ Object
Add a group of bindings that only fire when guard returns true. Guard is a Proc that receives the model.
35 36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/chamomile/keymap.rb', line 35 def only(guard, &block) sub = self.class.new block.call(sub) sub.entries.each do |entry| combined_guard = if entry.guard ->(m) { guard.call(m) && entry.guard.call(m) } else guard end @entries << Entry.new(key: entry.key, guard: combined_guard, action: entry.action) end self end |